22
$\begingroup$

I have Mathematica output that I would like to parse in python so I can generate equivalent expressions. So suppose I have the following:

testing2 = 
 ExpandAll[
  D[(x - A)^2 + (y - B)^2 +  ( 
      v - C)^2 + (x + y - (S + v) - D)^2  - λ1*x - λ2*
     y - λ3*v - λ4*(x + y - (S + v))  , {{x, y, v}}]]

Then I solve the following system:

Solve[Thread[
  testing2 == 0 &&  -λ1*x == 0 && - λ2*y == 
    0 && -λ3*v == 0 && - λ4*(x + y - (S + v)) == 
    0], {x, y, v, x, y, 
  v, λ1, λ2, λ3, λ4}]

Which returns a list of possible solutions. One possible solution is the following:

  {x -> 0, y -> 0, 
 v -> 1/2 (C - D - S), λ1 -> -2 A - C - D - 
   S, λ2 -> -2 B - C - D - S, λ3 -> 0, λ4 -> 
  0}

Since there are a lot of possible solutions, it is quite difficult to code everything by hand. I was trying to have the output put the multiplication symbol in the appropriate place. If I can do this, then it will be easier to parse the output in python. So suppose Mathematica is outputing 2A, I would like it to output 2*A. Is this possible?

$\endgroup$
1
  • 1
    $\begingroup$ FortranForm might get you close to what you want. FortranForm[a x^2 + b x + c] gives c + b*x + a*x**2 $\endgroup$
    – rm -rf
    Commented Jun 13, 2015 at 4:15

2 Answers 2

27
$\begingroup$

Since python has pretty close syntax as Fortran, converting the expression to FortranForm is what I usually do in this case.

testing2 = 
 ExpandAll[
  D[(x - A)^2 + (y - B)^2 + (v - C)^2 + (x + y - (S + v) - 
       D)^2 - λ1*x - λ2*y - λ3*
     v - λ4*(x + y - (S + v)), {{x, y, v}}]]
sols = {x, y, v, x, y, 
   v, λ1, λ2, λ3, λ4} /. 
  Solve[Thread[
    testing2 == 0 && -λ1*x == 0 && -λ2*y == 
      0 && -λ3*v == 0 && -λ4*(x + y - (S + v)) == 
      0], {x, y, v, x, y, 
    v, λ1, λ2, λ3, λ4}]
sols // FortranForm

This is not ideal, but a good starting point for python to work with.

If you have access to Maple. Another solution is using Maple. CodeGeneration is pretty handy in Maple, it can also generate function using numpy and scipy

with(MmaTranslator);
print(??); # input placeholder
e := FromMma("{{0, 0, 0, 0, 0, 0, -2 (A + D + S), -2 (B + D + S), -2 (C - D - S), 
  0}, {0, 0, 1/2 (C - D - S), 0, 0, 
  1/2 (C - D - S), -2 A - C - D - S, -2 B - C - D - S, 0, 0}, {0, 
  0, -S, 0, 0, -S, -2 (A + C + S), -2 (B + C + S), 0, 
  2 (C - D + S)}, {0, S, 0, 0, S, 0, -2 (A - B + S), 
  0, -2 (B + C - S), -2 (B + D - S)}, {0, 1/2 (B + C + S), 
  1/2 (B + C - S), 0, 1/2 (B + C + S), 
  1/2 (B + C - S), -2 A + B - C - S, 0, 0, -B + C - 2 D + S}, {0, 
  1/2 (B + D + S), 0, 0, 1/2 (B + D + S), 0, -2 A + B - D - S, 
  0, -B - 2 C + D + S, 0}, {0, 1/3 (2 B + C + D + S), 
  1/3 (B + 2 C - D - S), 0, 1/3 (2 B + C + D + S), 
  1/3 (B + 2 C - D - S), -(2/3) (3 A - B + C + D + S), 0, 0, 0}, {S, 
  0, 0, S, 0, 0, 0, 
  2 (A - B - S), -2 (A + C - S), -2 (A + D - S)}, {1/2 (A - B + S), 
  1/2 (-A + B + S), 0, 1/2 (A - B + S), 1/2 (-A + B + S), 0, 0, 
  0, -A - B - 2 C + S, -A - B - 2 D + S}, {1/2 (A + C + S), 0, 
  1/2 (A + C - S), 1/2 (A + C + S), 0, 1/2 (A + C - S), 0, 
  A - 2 B - C - S, 0, -A + C - 2 D + S}, {1/3 (2 A - B + C + S), 
  1/3 (-A + 2 B + C + S), 1/3 (A + B + 2 C - S), 
  1/3 (2 A - B + C + S), 1/3 (-A + 2 B + C + S), 
  1/3 (A + B + 2 C - S), 0, 0, 
  0, -(2/3) (A + B - C + 3 D - S)}, {1/2 (A + D + S), 0, 0, 
  1/2 (A + D + S), 0, 0, 0, A - 2 B - D - S, -A - 2 C + D + S, 
  0}, {1/3 (2 A - B + D + S), 1/3 (-A + 2 B + D + S), 0, 
  1/3 (2 A - B + D + S), 1/3 (-A + 2 B + D + S), 0, 0, 
  0, -(2/3) (A + B + 3 C - D - S), 0}, {1/3 (2 A + C + D + S), 0, 
  1/3 (A + 2 C - D - S), 1/3 (2 A + C + D + S), 0, 
  1/3 (A + 2 C - D - S), 0, 2/3 (A - 3 B - C - D - S), 0, 
  0}, {1/4 (3 A - B + C + D + S), 1/4 (-A + 3 B + C + D + S), 
  1/4 (A + B + 3 C - D - S), 1/4 (3 A - B + C + D + S), 
  1/4 (-A + 3 B + C + D + S), 1/4 (A + B + 3 C - D - S), 0, 0, 0, 0}}");
with(CodeGeneration);
Python(e);

Here is an example from help of Maple

# Translate a procedure involving linear algebra.
detHilbert := proc(M, n :: posint) uses LinearAlgebra;
   return Determinant( HilbertMatrix( n ) );
end proc:
Python(detHilbert);
import numpy.linalg
import scipy.linalg

def detHilbert (M, n):
    return(numpy.linalg.det(scipy.linalg.hilbert(n)))

Update: sympy method

sympy has now support translate MMA code to sympy. Since this is a pretty new method, I will demonstrate a bit here.

In [1]: from sympy.parsing import mathematica

In [2]: mathematica.parse('Sin[a]^2 27 + 54 x + 36 x^2 + 8 x^3')
Out[2]: 'sin(a)**2 27+54 x+36 x**2+8 x**3'

Noted: sympy does not handle the multiplication correctly now, but I believe this will be solved in the future. The list conversion is not correct as well.

Currently, you can do something like this:

in MMA:

In[178]:= {Sin[a]^2 27 + 54 x + 36 x^2 + 8 x^3, ArcTan[x]} // InputForm

Out[178]//InputForm=
{54*x + 36*x^2 + 8*x^3 + 27*Sin[a]^2, ArcTan[x]}

Copy the output to Python:

In [3]: mathematica.parse('54*x + 36*x^2 + 8*x^3 + 27*Sin[a]^2')
Out[3]: '54*x+36*x**2+8*x**3+27*sin(a)**2'

This result can be further converted to sympy object

In [4]: mathematica.sympify(_)
Out[4]: 8*x**3 + 36*x**2 + 54*x + 27*sin(a)**2

You may also use mathematica function in the module to merge above two functions. But I do not suggest to use the function. Because for parse function, you get the parse result in any case, but mathematica function returns a result only the result is a valid sympy expression. Here is a example of using mathematica function:

In [1]: from sympy.parsing import mathematica as M
In [2]: M.mathematica('4a+8b^2+Cos[9a]')
Out[2]: 4*a + 8*b**2 + cos(9*a)

It is very welcomed if you can improve Mathematica parser in sympy.

$\endgroup$
1
  • $\begingroup$ Another approach is Robert Jacobson's FoxySheep parser which I have put on PyPI and added a crude translator to Python/Numpy/Sympy. The code in sympy seems to work using regexps largely. This approach uses a pretty good parser, than transforms the parse tree into a Python AST and finally dumps that out as text. Although it is very crude, it can handle the 0 vs 1 origin indexing differences, and deeper parsing and translation. There is still a lot of low-hanging fruit here, but I must stop working on this for now. It could however be integrated int sympy. $\endgroup$
    – rocky
    Commented Aug 3, 2020 at 18:57
15
$\begingroup$

Here is a Module I prepared to convert most expressions (including lists) to Numpy. It may not be the most elegant solution, but it works pretty well. Below I also include some examples.

 ToPython[x_, numpyprefix_: "numpy"] := 
 Module[{expression = x, greekrule, PythonForm, 
   numpypre = numpyprefix, lp, rp, a, b},
  (*FUNCTION TO CONVERT MATHEMATICA EXPRESSION TO NUMPY;
  ----------------------------------------------------;
  INPUT ARGUMENTS;
  x: your mathematica expression, it can be numbers, literals, 
  complexes or lists;
  numpy\[LetterSpace]prefix: 
  string defining your Numpy import prefix, e.g.:
  if your used "import numpy as np", 
  your prefix should be the string "np"
  if your used "from numpy import *", 
  your prefix should be the empty string ""
  ;
  OUTPUT;
  the Numpy python-ready expression (to be copied as a string);
  !The formatted expression will be copied ot your clipboard, 
  ready to paste on Python!;
  ------------------------------------------------------;
  Not tested for every possible combination; use at your risk, 
  by Gustavo Wiederhecker*)

  If[numpyprefix == "", sep = "", 
   sep = "."];(*if no prefix is included, 
  the "." separator is not used*)
  lp = "( ";
  rp = " )";
  PythonForm[Rational[a_, b_]] := 
   PythonForm[a] <> "/" <> PythonForm[b];
  PythonForm[Complex[a_, b_]] := 
   "complex" <> lp <> PythonForm[a] <> "," <> PythonForm[b] <> rp;
  PythonForm[Times[a_, b_]] := PythonForm[a] <> " * " <> PythonForm[b];
  PythonForm[Plus[a_, b_]] := 
   lp <> PythonForm[a] <> " + " <> PythonForm[b] <> rp;
  PythonForm[h_[args__]] := 
   numpypre <> sep <> ToLowerCase[PythonForm[h]] <> lp <> 
    PythonForm[args] <> rp;
  PythonForm[Power[a_, b_]] := 
   lp <> PythonForm[a] <> rp <> "**" <> lp <> PythonForm[b] <> rp;
  PythonForm[a_ListQ] := 
   numpypre <> sep <> "array" <> 
    StringReplace[ToString[a], {"{" -> "[", "}" -> "]"}];
  PythonForm[Arg] = numpypre <> sep <> "angle";
  (*Some functions that are note defined in numpy*)

  PythonForm[Csc] := "1/" <> numpypre <> sep <> "sin";
  PythonForm[Sec] := "1/" <> numpypre <> sep <> "cos";
  PythonForm[Cot] := "1/" <> numpypre <> sep <> "tan";
  PythonForm[Csch] := "1/" <> numpypre <> sep <> "sinh";
  PythonForm[Sech] := "1/" <> numpypre <> sep <> "cosh";
  PythonForm[Coth] := "1/" <> numpypre <> sep <> "tanh";
  (*Handling arrays*)

  PythonForm[List[args__]] := 
   numpypre <> sep <> "array" <> lp <> "[" <> 
    Table[PythonForm[{args}[[ii]]] <> ",", {ii, 1, Length@{args}}] <> 
    "]" <> rp;
  (*Pi and E*)
  PythonForm[\[Pi]] = numpypre <> sep <> "pi";
  PythonForm[E] = numpypre <> sep <> "e";
  (*real numbers, engineering notation*)

  PythonForm[r_Real] := 
   Block[{a = MantissaExponent[r]}, 
    If[r >= 0, ToString[N[a[[1]], 6]] <> "e" <> ToString[a[[2]]], 
     "(" <> ToString[N[a[[1]], 6]] <> "e" <> ToString[a[[2]]] <> ")"]];
  (*Greek characters*)

  greekrule = {"\[Alpha]" -> "alpha", "\[Beta]" -> "beta", 
    "\[Gamma]" -> "gamma", "\[Delta]" -> "delta", 
    "\[CurlyEpsilon]" -> "curlyepsilon", "\[Zeta]" -> "zeta", 
    "\[Eta]" -> "eta", "\[Theta]" -> "theta", "\[Iota]" -> "iota", 
    "\[Kappa]" -> "kappa", "\[Lambda]" -> "lambda", "\[Mu]" -> "mu", 
    "\[Nu]" -> "nu", "\[Xi]" -> "xi", "\[Omicron]" -> "omicron", 
    "\[Pi]" -> "pi", "\[Rho]" -> "rho", 
    "\[FinalSigma]" -> "finalsigma", "\[Sigma]" -> "sigma", 
    "\[Tau]" -> "tau", "\[Upsilon]" -> "upsilon", 
    "\[CurlyPhi]" -> "curlyphi", "\[Chi]" -> "chi", "\[Psi]" -> "psi",
     "\[Omega]" -> "omega", "\[CapitalAlpha]" -> "Alpha", 
    "\[CapitalBeta]" -> "Beta", "\[CapitalGamma]" -> "Gamma", 
    "\[CapitalDelta]" -> "Delta", 
    "\[CapitalEpsilon]" -> "CurlyEpsilon", "\[CapitalZeta]" -> "Zeta",
     "\[CapitalEta]" -> "Eta", "\[CapitalTheta]" -> "Theta", 
    "\[CapitalIota]" -> "Iota", "\[CapitalKappa]" -> "Kappa", 
    "\[CapitalLambda]" -> "Lambda", "\[CapitalMu]" -> "Mu", 
    "\[CapitalNu]" -> "Nu", "\[CapitalXi]" -> "Xi", 
    "\[CapitalOmicron]" -> "Omicron", "\[CapitalPi]" -> "Pi", 
    "\[CapitalRho]" -> "Rho", "\[CapitalSigma]" -> "Sigma", 
    "\[CapitalTau]" -> "Tau", "\[CapitalUpsilon]" -> "Upsilon", 
    "\[CapitalPhi]" -> "CurlyPhi", "\[CapitalChi]" -> "Chi", 
    "\[CapitalPsi]" -> "Psi", "\[CapitalOmega]" -> "Omega"};
  (*Everything else*)

  PythonForm[allOther_] := 
   StringReplace[ToString[allOther, FortranForm], greekrule];
  (*Copy results to clipboard*)

  CopyToClipboard[PythonForm[expression]];
  PythonForm[expression]]

Here are some examples that I've tested:

(*Expression examples*)
ToPython[a + b]
ToPython[a*b]
ToPython[a/b]
ToPython[(a + b)/(d + e + g)]
ToPython[(a + b)^(d + e + g)]
ToPython[Exp[a + b]]
ToPython[Sin[(a + b)]/Cos[d + e]]
ToPython[Sin[(a + b)]/Tanh[d + e]]
ToPython[\[Pi] Cosh[a]]
ToPython[Log10[x]]
(*Expression with greek letters*)
ToPython[Sin[\[Alpha] + \[Beta]]]
(*Numeric examples*)
ToPython[2]
ToPython[1/3]
ToPython[1.0/3]
ToPython[2.31]
ToPython[2.31 + 5.3 I]
(*Array handling*)
ToPython[{1, 2, 3}]
ToPython[{{1, 2, 3}}]
ToPython[Cos[{1, 2, 3}]]
(*Example with numpy as np*)
ToPython[\[Pi] Cosh[a]/Sin[b], "np"]
ToPython[Exp[a + b], "np"]
ToPython[Cos[{1, 2, 3}], "np"]
(*Example with numpy as "from numpy import *"*)
ToPython[\[Pi] Cosh[
    a]/Sin[b], ""]
ToPython[Exp[a + b], ""]
ToPython[Cos[{1, 2, 3}], ""]

Here is the output to the code above:

( a + b )
a * b
a * ( b )**( -1 )
( a + b ) * ( ( d + ( e + g ) ) )**( -1 )
( ( a + b ) )**( ( d + ( e + g ) ) )
( numpy.e )**( ( a + b ) )
numpy.1/numpy.cos( ( d + e ) ) * numpy.sin( ( a + b ) )
numpy.1/numpy.tanh( ( d + e ) ) * numpy.sin( ( a + b ) )
numpy.pi * numpy.cosh( a )
( numpy.log( 10 ) )**( -1 ) * numpy.log( x )
numpy.sin( ( alpha + beta ) )
2
1/3
0.333333e0
0.231e1
complex( 0.231e1,0.53e1 )
numpy.array( [1,2,3,] )
numpy.array( [numpy.array( [1,2,3,] ),] )
numpy.array( [numpy.cos( 1 ),numpy.cos( 2 ),numpy.cos( 3 ),] )
np.pi * np.cosh( a ) * np.1/np.sin( b )
( np.e )**( ( a + b ) )
np.array( [np.cos( 1 ),np.cos( 2 ),np.cos( 3 ),] )
pi * cosh( a ) * 1/sin( b )
( e )**( ( a + b ) )
$\endgroup$
1
  • $\begingroup$ Need to add PythonForm[\[Degree]] = numpypre <> sep <> "pi/180"; $\endgroup$
    – Martin R
    Commented May 6 at 16:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.