Symolic Computation playground!

This is a playgrond area for learning and trying out stuffs of symbolic computation via sympy for Vitor.

The try out here is based on Sympy toturial: an introduction to SymPy for someone who has not used the library before.

Introductory (What is Symbolic Computation?)

manipulate irrational numbers

perfect square: precise result

import math
math.sqrt(9)
3.0

non-perfect square: approximation result

math.sqrt(8)
2.8284271247461903

The way it does in sympy: square roots of numbers that are not perfect squares are left unevaluated by default.

import sympy
sympy.sqrt(3)
\[\displaystyle \sqrt{3}\]

And symbolic results can be symbolically simplified!

sympy.sqrt(8)
\[\displaystyle 2 \sqrt{2}\]

A More Interesting Example: computing symbolic expressions with variables

Symbolic computation systems (often called computer algebra systems, or just CASs) such as SymPy are capable of computing symbolic expressions with variables.

x, y = symbols('x y')
try:
    expr1 = x + 2y       # not like this!
except error:
    print("SyntaxError: invalid decimal literal")
  Cell In[5], line 3
    expr1 = x + 2y       # not like this!
                ^
SyntaxError: invalid decimal literal

For example, the way in sympy to define $x+2y$

from sympy import symbols
x, y = symbols('x y')
expr = x + 2*y
expr
\[\displaystyle x + 2 y\]

Play around now!

simplification automatically

expr + 1
\[\displaystyle x + 2 y + 1\]
expr - x
\[\displaystyle 2 y\]

Notice: factor form

x*expr
\[\displaystyle x \left(x + 2 y\right)\]

What if we want expanded form? use expand

from sympy import expand, factor
expanded_expr = expand(x*expr)
expanded_expr
\[\displaystyle x^{2} + 2 x y\]

The other way around? To factor the expaned form:

factor(expanded_expr)
\[\displaystyle x \left(x + 2 y\right)\]

The Real Power of Symbolic Computation !?

The real power of a symbolic computation system such as SymPy is the ability to do all sorts of computations symbolically.

“Symbolically”… What does it imply actually?

We will see some more examples!

from sympy import *
x, t, z, nu = symbols('x t z nu')
x
t
z
\[\displaystyle z\]
nu
\[\displaystyle \nu\]
# init_printing(pretty_print=False,use_unicode=True)
diff(sin(x)*exp(x), x)
\[\displaystyle e^{x} \sin{\left(x \right)} + e^{x} \cos{\left(x \right)}\]
integrate(exp(x)*sin(x) + exp(x)*cos(x), x)
\[\displaystyle e^{x} \sin{\left(x \right)}\]
integrate(sin(x**2), (x, -oo, oo))
\[\displaystyle \frac{\sqrt{2} \sqrt{\pi}}{2}\]
limit(sin(x)/x, x, 0)
\[\displaystyle 1\]
solve(x**2 - 2, x)
[-sqrt(2), sqrt(2)]
y = Function('y')
dsolve(Eq(y(t).diff(t, t) - y(t), exp(t)), y(t))
\[\displaystyle y{\left(t \right)} = C_{2} e^{- t} + \left(C_{1} + \frac{t}{2}\right) e^{t}\]
Matrix([[1, 2], [2, 2]]).eigenvals()
{3/2 - sqrt(17)/2: 1, 3/2 + sqrt(17)/2: 1}
besselj(nu, z).rewrite(jn)
\[\displaystyle \frac{\sqrt{2} \sqrt{z} j_{\nu - \frac{1}{2}}\left(z\right)}{\sqrt{\pi}}\]
latex(Integral(cos(x)**2, (x, 0, pi)))
'\\int\\limits_{0}^{\\pi} \\cos^{2}{\\left(x \\right)}\\, dx'

Why two “ \ “ above???

print("\\pi")
\pi
print("\pi")
\pi

Conclusion: needed to change \\ to \ manually… For a direct copy-paste into a Markdown cell without manual intervention, unfortunately, there’s no automatic way to convert the double backslashes to single backslashes due to the escape character behavior in Python strings and the different contexts in which the LaTeX is rendered (Python string vs. Markdown).

Consider to create a python function to achieve it!

$\int\limits_{0}^{\pi} \cos^{2}{\left(x \right)}\, dx$

import re
def convert_latex_for_markdown(latex_str):
    return re.sub(r'\\\\', r'\\', latex_str)
print(convert_latex_for_markdown(latex(Integral(cos(x)**2, (x, 0, pi)))))
\int\limits_{0}^{\pi} \cos^{2}{\left(x \right)}\, dx

$\int\limits_{0}^{\pi} \cos^{2}{\left(x \right)}, dx$

Gotchas (Working with symbolic expressions in Python)

Symbols

from sympy import *
x + 1
\[\displaystyle x + 1\]

If we run in python and not in jupter notebook, it would gives error:

Traceback (most recent call last):

File “”, line 1, in

NameError: name ‘x’ is not defined

Naming of symbols!

x = symbols('x')
x + 1
\[\displaystyle x + 1\]
x, y, z = symbols('x y z')
a, b = symbols('b a')
a
\[\displaystyle b\]
b
\[\displaystyle a\]
crazy = symbols('unrelated')
crazy + 1
\[\displaystyle unrelated + 1\]

“Symbol”: Sympy symbol

“Variable”: Python variable

x = symbols('x')
expr = x + 1
x = 2
print(expr)
x + 1

python variable changed. But not sympy symbol.

x = 'abc'
expr = x + 'def'
expr
'abcdef'
x = 'ABC'
expr
'abcdef'

To change the value of a Symbol in an expression, use subs:

x = symbols('x')
expr = x + 1
expr.subs(x, 2)
\[\displaystyle 3\]

Equal signs

x + 1 == 4
False
Eq(x + 1, 4)
\[\displaystyle x + 1 = 4\]
(x + 1)**2 == x**2 + 2*x + 1
False

“==” only equals in “exact structure! It is for structural equality.

Not for symbolic equality and mathematical equality.

recall that if $a = b$, then $a - b = 0$

If two symbolic expressions are identically equal.

simplify

a = (x + 1)**2
b = x**2 + 2*x + 1
simplify(a - b)
\[\displaystyle 0\]
c = x**2 - 2*x + 1
simplify(a - c)
\[\displaystyle 4 x\]

equals: evaluating them numerically at random points.

a = cos(x)**2 - sin(x)**2
b = cos(2*x)
a.equals(b)
True

^ and /

^ represent logical exclusive OR, XOR in Python

True ^ False
True
True ^ True
False
False ^ False
False
Xor(x, y)
\[\displaystyle x \veebar y\]

Sympy objects and Python objects

type(Integer(1) + 1)
sympy.core.numbers.Integer
type(1 + 1)
int

Division in sympy gives rational

Integer(1)/Integer(3)
\[\displaystyle \frac{1}{3}\]
type(Integer(1)/Integer(3))
sympy.core.numbers.Rational
from __future__ import division
1/2
0.5
Rational(1, 2)
\[\displaystyle \frac{1}{2}\]
x + 1/2
\[\displaystyle x + 0.5\]
x + Rational(1, 2)
\[\displaystyle x + \frac{1}{2}\]

Lambdify (Lambdification to numerical functions)

forwards

import numpy 
a = numpy.arange(10) 
expr = sin(x)
f = lambdify(x, expr, "numpy") 
f(a) 
array([ 0.        ,  0.84147098,  0.90929743,  0.14112001, -0.7568025 ,
       -0.95892427, -0.2794155 ,  0.6569866 ,  0.98935825,  0.41211849])
f = lambdify(x, expr, "math")
f(0.1)
0.09983341664682815
def mysin(x):
    """
    My sine. Note that this is only accurate for small x.
    """
    return x
f = lambdify(x, cos(x)*sin(x), {"sin":mysin,"cos":mysin})
f(0.1)
0.010000000000000002
0.1+0.2
0.30000000000000004
import inspect

print(inspect.getsource(f))
def _lambdifygenerated(x):
    return sin(x)*cos(x)
sympy.print_tree(expr)
sin: sin(x)
commutative: True
+-Symbol: x
  commutative: True
f(0.5)
0.25
sin(0.5)
\[\displaystyle 0.479425538604203\]
type(sympy.sin)
sympy.core.function.FunctionClass
type(sin)
sympy.core.function.FunctionClass

This module provides convenient functions to transform SymPy expressions to lambda functions which can be used to calculate numerical values very fast.

from sympy.abc import x
from sympy.utilities.lambdify import implemented_function
from sympy import lambdify
f = implemented_function('f', lambda x: x+1)
lam_f = lambdify(x, f(x))
lam_f(4)
5
from sympy.abc import x, y, z
from sympy.utilities.lambdify import lambdastr
lambdastr(x, x**2)
lambdastr((x,y,z), [z,y,x])
'lambda x,y,z: ([z, y, x])'
lambdastr((x, (y, z)), x + y)
'lambda _0,_1: (lambda x,y,z: (x + y))(_0,_1[0],_1[1])'

Codegen (Code generation: the principle behind lambdification)

See here.