f.. pymathics-hello documentation master file, created by

sphinx-quickstart on Sun Nov 29 14:06:02 2020. You can adapt this file completely to your liking, but it should at least contain the root toctree directive.

Using Mathics3 from your code

There a various ways you make use of Mathics3 ability to solve equations, or run code which we describe here.

From a shell

Perhaps the least-tightly coupled way is to just call the command-line interpreter in a shell and process its output.

Here is some POSIX shell code that does this:

$ mathicsscript -e '3! + E^(Pi I)'
5
$ seq $(mathicsscript -e 'Integrate[x^2, {x, 0, 3}])'
1
2
3
4
5
6
7
8
9

The above code runs the Mathics3 interpreter in a subshell and then uses the number returned in a call another command, seq which uses that number.

To call Mathics3 as a subshell this inside Python, the subprocess module might be used like this:

from subprocess import run, PIPE

expression = "Integrate[x^2, {x, 0, 3}]"
cmd = ["mathicsscript", "--execute", expression]
result = run(cmd, stdout=PIPE)
if result.returncode == 0:
    print(int(result.stdout) + 5)

This code runs the Mathics3 interpreter as a subprocess, capturing the output and if the execution was successful converts the result from its string output to an Python int and adds 5.

Using MathicsSession

While the above is fine for running an isolated expression or two, it is pretty inefficient: Python has to be loaded every time along with the huge Mathics3 program; all of the built-in functions need to be set up, and some of terminal interaction needs set up as well.

If you have a sequence of Mathics3 expressions, or need to get results from Mathics3 a number of times inside your Python code, it is faster to just import mathics and set up and environment for running code once.

Here is an example of that:

from mathics.session import MathicsSession
session = MathicsSession(add_builtin=True, catch_interrupt=True)

# Compute 20!
result = session.evaluate("20!").to_python()
print(result)

In the above code, session is the scratchpad area that contains results of the evaluations. Creating this stores all of the builtin deinitions. We explicitly set the parameter add_builtin=True to include things like Factorial which is used later.

Although we set add_builtin explicitly for pedagodical purpose, True is the default, adding this paraemter wasn’t necessary. We’ll leave it off in future examples.

Mathics3 Results as Python Objects

In the last section we passed a string to session.evaluate() we passed a string. That string was scanned and parsed, before it was evaluated. (See the section below for what goes on in scanning and parsing.)

A more flexible way to use Mathics3 is to skip the scanning and parsing and call the same functions that Mathics3 calls underneath to evaluation expressions. In this section we will do just that.

As before, we need a MathicsSession session object as a scratchpad area to save results, and to lookup previous definitions and results.

# This is the same as before
from mathics.session import MathicsSession
session = MathicsSession(catch_interrupt=True)

# These are Mathics3 classes we are going to use.
from mathics.core.expression import Expression, Integer
from mathics.core.systemsymbols import SymbolFactorial
# SymbolFactorial = Symbol("System`Factorial")

# Compute 20!
x = Expression(SymbolFactorial, Integer(10)
              ).evaluate(session.evaluation).to_python() # SymbolFactorial can be replaced by Symbol("Factorial")
print(x) # 2432902008176640000

The above code computes the same value as in the last section. However we are doing this by interacting with the Mathics3 classes now.

In this example shown above, we convert from Python’s literal 10 to Mathics3’s representation of for 10 using Integer(10). This value is needed as a parameter to the Factorial function . Strictly speaking the full name of the factorial function is System`Factorial, but we can leave off the context name, System, and Mathics3 will look that up.

Notice how to evaluate a general Mathics3 expression in Python using Expression(): the first parameter is an instance of Symbol constructed with the Full-Form name of the function to get called. Here that is Symbol("System`Factorial"). The parameters after the first one are the parameters to the function specified in the first parameter. Here it is that parameter Integer(10). Each of the parameters should have type Mathics3 Expression.

The returned value of Expression() is Python object and data structure that Mathics3 uses to evaluation expressions. However that object isn’t evaluated until you invoke its evaluate() method. Aside from evaluating the expression, other things you might do are format the expression so that it can be displayed nicely, or inspect the expression in the same way you might inspect a lambda function in Lisp.

When the evaluate() method is called, the function is evaluated but the return value is still a Mathics3 Expression, even if it is the computed value rather than its more symbolic form. So if Python is going to use the value, it needs to call to_python() to convert the value into a Python integer value.

Just as Python expressions can be composed from other Python expressions, the same is true in Mathics: an Expression() parameter can be another expression.

Here is an example of that:

# This is the same as before
from mathics.session import MathicsSession
session = MathicsSession(catch_interrupt=True)

# These are Mathics3 classes we are going to use.
from mathics.core.expression import Expression, Integer
from mathics.core.symbols import SymbolPlus, SymbolTimes
# SymbolPlus = Symbol("System`Plus"), SymbolTimes = Symbol("System`Times")

# Compute 5 * (6 + 3)
x = Expression(SymbolTimes, Integer(5),
      Expression(SymbolPlus, Integer(6), Integer(3))
      ).evaluate(session.evaluation).to_python()
print(x) # 45

Notice that precedence between operations, like Plus() and Times() is handled simply in the order in which these functions are called, so no parenthesis is used in the functional way.

To simplify the above, we have overloaded the standard binary and unary numeric operators +, -, /, *, abs(), //, and ** in the Expressions and Numbers classes. With this, the above can be written as:

# This is the same as before
from mathics.session import MathicsSession
session = MathicsSession(catch_interrupt=True)

from mathics.core.expression import Integer

# Compute 5 * (6 + 3)

x = (
     Integer(5) * (Integer(6) + Integer(3))
     ).evaluate(session.evaluation).to_python()
print(x) # 45

Note that when we switch to (overloaded) infix operators now need parenthesis again, since * binds tigher than +.

Conversion to and from Python