# Using local variables in ExternalEvaluate Python

I am sure that I am missing something here, I have some larger matrices and Mathematica takes too long to compute the eigenvalues and vectors (using Eigensystem[m]) so I would like to pass them to a python environment using ExternalEvaluate so as to take advantage of Python's much faster computation for this.

I am sure there is an easy way to pass my matrix to Python, but I cannot see how.

MWE:

In[1]:= linearMap = {{1, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1/4, 0, 0, 0}, {0, 0, 0, 0, 1/2, 0, 0},
{0, 0, 0, 0, 0, 1/2, 0}, {0, 0, 0, 0, 0, 0, 1/4}};

In[2]:= ExternalEvaluate["Python", "import numpy; numpy.linalg.eigvalsh(linearMap)"]
Out[2]=


I am very aware that it is not necessary for such small matrices (or indeed this matrix at all really!), but as they get larger, I would greatly appreciate the ability to utilise the speed increases.

Clearly this isn't expected to work instantly as the linearMap from Mathematica would need to be reformatted to a Python array. I am just hoping that there is an easy way to do this that I have overlooked...

• MMA evalutes the eigensystem in 0.005s , that's to long??? – Ulrich Neumann Nov 5 '18 at 11:35
• As I said, the issue is not with this matrix, but for much larger matrices that I need to use. This is simply a Minimum Working Example. – wilsnunn Nov 5 '18 at 11:36
• I'm wondering if your speed issue arises because you feed exact numbers to Eigensystem, as in your linearMap example. If so, use Eigensystem[N@linearMap] instead. – Alan Nov 5 '18 at 15:15

You may use the Association syntax for ExternalEvaluate.

If numpy is installed in your Python instance then you should have a "Python-NumPy" external evaluator. Check by evaluating FindExternalEvaluators[].

Initialise the connection with

ExternalEvaluate["Python-NumPy", "1+1"]

2


Then

ExternalEvaluate["Python-NumPy",
<|
"Command" -> "numpy.linalg.eigvalsh",
"Arguments" -> {linearMap}
|>
]

{0.25, 0.25, 0.5, 0.5, 1., 1., 1.}


If you need to use this often then create a function

numpyEigvalsh[m_?MatrixQ] :=
ExternalEvaluate["Python-NumPy",
<|
"Command" -> "numpy.linalg.eigvalsh",
"Arguments" -> {m}
|>
]


Then

numpyEigvalsh@linearMap

{0.25, 0.25, 0.5, 0.5, 1., 1., 1.}


# Why it may be slower

Note that when using Rationals that Mathematica will take longer as it works to preserve the infinite precision of rationals.

Eigenvalues@linearMap


{1, 1, 1, 1/2, 1/2, 1/4, 1/4}

You can speed things up by using Reals. All you need to do is multiply by 1.

Eigenvalues[1. linearMap]

{1., 1., 1., 0.5, 0.5, 0.25, 0.25}


Note that the output is now with reals instead of rationals

Hope this helps.

If you are willing to setup and use WolframClientForPython you could do:

With Mathematica

linearMap = 1. {{1, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 1/4, 0, 0, 0},
{0, 0, 0, 0, 1/2, 0, 0}, {0, 0, 0, 0, 0, 1/2, 0},
{0, 0, 0, 0, 0, 0, 1/4}};
Export[FileNameJoin[{"C:", "temp", "linearMap.wxf"}], "WXF"]


then in Python

import numpy as np
import os
from wolframclient.evaluation import WolframLanguageSession
from wolframclient.serializers import export

math_kernel = r'C:\Program Files\Wolfram Research\Mathematica\11.3\MathKernel.exe'
output_path = r'C:\temp'

session = WolframLanguageSession(math_kernel)
session.start()

linear_map = session.evaluate('Import[FileNameJoin[{"C:", "temp", "linearMap.wxf"}]]')

linear_map = np.array(linear_map)

out = np.linalg.eigvalsh(linear_map)

export(out, os.path.join(output_path, 'out.wxf'), target_format='wxf')

session.terminate()


finally back in Mathematica

Import[FileNameJoin[{"C:", "temp", "out.wxf"}]] // Normal
(* {0.25, 0.25, 0.5, 0.5, 1., 1., 1.} *)