7
$\begingroup$

I am attempting to use ExternalEvaluate to use some python packages (ARC) to save me some effort in performing the calculations in Mathematica, and for the most part I think it works as intended. The issue is not with the specific python package, but I will keep it here for the example.

I am having an issue with calling one class function which throws an error about the positional arguments. In python the code would look just like this:

atom = arc.Rubidium87()
groundState = arc.DynamicPolarizability(atom, 5, 0, 0.5)
groundState.defineBasis(5, 25)

And it runs flawlessly.

In Mathematica I have the following:

session = StartExternalSession["Python"];
ExternalEvaluate[session, {"atom = arc.Rubidium87()","atom=arc.Rubidium87()"}]
setDynamicPolarizability[n_, l_, j_] := ExternalEvaluate[session, <|"Command" -> "gs=arc.DynamicPolarizability","Arguments" -> {"atom", n, l, j}|>]

setDynamicPolarizability[5, 0, 0.5];
setBasis[n_, nmax_] := ExternalEvaluate[session, <|"Command" -> "gs.defineBasis","Arguments" -> {n, nmax}|>]
setBasis[5, 25]

however the final line of code throws an error

![ErrorMessage

I clearly have both of the required arguments for the function, and the previous things I have tried have worked perfectly fine. I have a feeling that it is due to the fact that it is a class function (docs, source) and the missing self is the cause of the problem.

I hope the problem I'm having is clear, any tips would be greatly appreciated.

$\endgroup$
0

2 Answers 2

5
$\begingroup$

To me the problem is not directly about the self but rather the way commands are executed. Consider this example before solving the problem.

let's say you want to assign a variable, the sum of two numbers:

non-interactive method

ExternalEvaluate["Python", "
import operator
temp = operator.add(1,2)
temp
"]

(* Out: 3 *)

Interactive method

session = StartExternalSession["Python"];

ExternalEvaluate[session, "import operator"];

ExternalEvaluate[session, <|"Command" -> "temp = operator.add", "Arguments" -> {1, 2}|>]

(* Out: Error *)

enter image description here

What happened? It seems "temp = operator.add" is not executed as we might think. So one workaround is to define an intermediary function:

ExternalEvaluate[session, <|"Command" -> "
def temp(a,b):
    global sample
    sample=operator.add(a,b)
", "Arguments" -> {1, 3}|>]

ExternalValue[session, "sample"]

(* Out: 4 *)

Back to your question, I think "gs" is not initialized (gs=arc.DynamicPolarizability is executed), when you call with 2 arguments, it doesn't match to (self,arg1,arg2) pattern. We could solve it using the above method:

ClearAll[setDynamicPolarizability,setBasis];

ExternalEvaluate[session, "
import arc
atom=arc.Rubidium87()
"]

setDynamicPolarizability[n_, l_, j_] := 
 ExternalEvaluate[session, <|"Command" -> "
gs=None
def temp2(a,b,c):
    global gs
    gs=arc.DynamicPolarizability(atom,a,b,c)
", "Arguments" -> {n, l, j}|>]

setDynamicPolarizability[5, 0, 0.5];

setBasis[n_, nmax_] := ExternalEvaluate[session, <|"Command" -> "
def temp4(a,b):
    global gs
    return gs.defineBasis(a,b)
", "Arguments" -> {n, nmax}|>]

setBasis[5, 25]

works as expected.

And lastly:

DeleteObject[session];

Notes:

  • In Python use global to manipulate global variables rather than creating an in-scope variable
  • If you should define multiple functions, pick unique names to prevent mixing up
  • Code was tested on Mathematica 13.0 with Python 3.10 on Windows 10

If you found a better solution, don't forget to share it with the community.

$\endgroup$
6
  • $\begingroup$ In my experience, the last bullet point isn't necessary. $\endgroup$ Feb 3, 2022 at 11:24
  • $\begingroup$ @AlexeyPopkov you're right, thanks for the suggestion. $\endgroup$
    – Ben Izd
    Feb 3, 2022 at 15:20
  • $\begingroup$ Thank you very much, this is exactly what I was not understanding with all of this, this helps me very much. $\endgroup$
    – D. Brown
    Feb 4, 2022 at 2:38
  • $\begingroup$ Apologies if this is a simple question, I have implemented the above example you posted, however each function call of setBasis and setDynamicPolarizability is just outputting Null[5,25] and Null[5,0,0.5]. Using ExternalValue[session,"gs"] also returns nothing, saying that gs is not defined. Am I missing something simple here? $\endgroup$
    – D. Brown
    Feb 4, 2022 at 4:06
  • 1
    $\begingroup$ @D.Brown in the above code, setDynamicPolarizability does not return anything in python, it just manipulates global objects, which also could be applied to the setBasis (I think the return keyword is redundant in your case and could be removed). I execute the above code in a fresh kernel and after each call, ExternalValue[session, "gs"] was a PythonObject. I suggest you do the same, start a fresh kernel and run the above code. $\endgroup$
    – Ben Izd
    Feb 4, 2022 at 5:27
7
$\begingroup$

It is an extended comment to the answer by Ben Izd.

The simplest interactive solution for his example is as follows:

session = StartExternalSession[<|"System" -> "Python",
                                 "SessionProlog" -> "import operator"|>]

addTwo = ExternalFunction[session, "operator.add"]

enter image description here

You can use it as follows:

addTwo[2, 3]
5

The simplest non-interactive solution:

addTwo = ExternalFunction["Python", "import operator;  operator.add"]

enter image description here

Usage is the same (but works slower, because Python session is new for every call):

addTwo[2, 3]
5
$\endgroup$
1
  • $\begingroup$ Thank you very much, this is incredibly helpful $\endgroup$
    – D. Brown
    Feb 4, 2022 at 2:37

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.