import sympy as sp
string = '1/(x-1) + 1/(x+1) + x + 1'
from sympy import init_printing, latex
init_printing()
expr = sp.sympify(string)
sym, = expr.free_symbols
sym
x = sp.Symbol(sym.name, real=True)
expr = expr.subs(sym,x)
expr
frac = sp.cancel(sp.together(expr))
frac
den = sp.denom(frac)
den
poles = sp.solve(den,x)
poles
# the full real line is Interval(-oo,oo)
from sympy import oo
sp.Interval(-oo,oo)
sp.FiniteSet.fromiter(poles)
The domain of definition of the function is the set of values of x for which the expression is well-defined, these are values for which the denominator is not zero. So the domain includes everything up to 1 or -1, thats what is meant by the ) instead of ].
domain = sp.Interval(-oo,oo) - sp.FiniteSet.fromiter(poles)
domain
deriv = sp.cancel(sp.diff(frac,x))
deriv
extrema = sp.solve(deriv, x)
extrema
extrema_values = [frac.subs(x,x0) for x0 in extrema]
extrema_values
m = sp.limit(frac/x,x,oo)
p = sp.limit(frac - m*x,x,oo)
m,p
m.is_finite
def find_asymptote(expr, x):
"""
Return m,p such that y=m*x+p is an asymptote to the curve y = expr.
If there is no asymptote, return None.
"""
m = sp.limit(expr/x, x, oo)
if not m.is_finite:
return None
else:
p = sp.limit(expr-m*x, x, oo)
return m,p
[find_asymptote(e, x) for e in (frac, x**2, x**2/(x**2+1))]
import matplotlib.pyplot as plt
import numpy as np
def numnuts(a,b,c,d,e):
print(b,c)
numnuts(1,*(2,3),*(4,5))
def plot_curve(expr, x, x_min, x_max, y_min, y_max):
""" Plot y = expr(x) over the specified domain"""
func = sp.lambdify(x, expr, 'numpy')
xs = np.linspace(x_min, x_max, 200)
plt.plot(xs, func(xs))
plt.ylim(y_min, y_max)
plt.xlim(x_min, x_max)
plot_curve(frac, x, *(-5, 5), *(-10, 10))
[0]+poles+extrema
def choose_domain(values):
values = list(map(float, values))
low = min(values) - 2
high = max(values) + 2
return low,high
values = map(float, [0]+poles+extrema)
min(list(values))
x_min, x_max = choose_domain([0]+poles+extrema)
y_min, y_max = choose_domain([0]+extrema_values+[m*x_min + p, m*x_max + p])
plot_curve(frac, x, *(x_min, x_max), *(y_min, y_max))
plt.vlines(poles, y_min, y_max, 'r', linewidth=2)
plt.plot([x_min, x_max], [m*x_min+p,m*x_max+p], 'r', linewidth=2)
plt.plot(extrema, extrema_values, 'ro')
plt.title("Sketch of $%s \mapsto %s$" % (latex(x), latex(frac)));
There are several building blocks for expressions the first of which are numbers. There are two kinds of numbers exact expressions and floating point numbers. The latter are represented by the class Float.
sp.Integer(3)/sp.Integer(4) # Returns a rational
sp.sympify(3)
sp.S(3)
sp.factorint(42)
z = sp.Symbol('z') # a complex variable
x = sp.Symbol('x',real=True) # a real variable
a = sp.Symbol('a', positive=True) # positive and therefore real
c = sp.Symbol('c', constant=True)
type(z)
sp.symbols('x:5')
Applying a function to an exact number expression always results in another expression. This results in an unevaluated expression.
f = sp.Function('f')
f(0)
g = sp.Lambda(x,1-x**2)
g(2)
Sympy objects are all considered atoms. These are integers, rationals, constants, symbols. All expressions are built on top of these simpler objects. The obey the following invariant expr == expr.class(*expr.args), which means that all their properties derive solely from their type and the contents of their .args attribute. Also since the .args are also sympy objects, the whole expression has a tree structure, which SymPy classes as nodes and atoms as the leaves.
expr = sp.exp(-x)*sp.log(1-x)*2
sp.srepr(expr)
Obtain an aggregate view of the contents of an expression with the .atoms() method. By default it returns the set of atomic objects contained in the expressions.
expr.atoms(sp.Integer)
Return a new expression when you do a substitution.
x,y = sp.symbols('x, y', real=True)
(sp.cos(x)*sp.exp(y)).subs({x:sp.pi,y:2})
To check whether two expressions are mathematically equal, you need to check whether their differences reduce to zero. To do this, apply a canonicalization function to the difference, and test whether its equal to zero. simplify() is generally a useful choice for this but expand() can also be used.
sp.simplify(x*(x+1) - (x**2+x)) == 0
expr = sp.exp(x)/(x+1)
deriv = sp.diff(expr,x)
sp.N(deriv, subs={x:1.2345})
To convert symbolic expressions obtained with SymPy into a more efficient form for numerical evaluation.
f = sp.lambdify([x,a],a+x**2,"numpy")
arr = np.random.randn(1000)
result = f(arr, 0.4)
result[:3]
f = sp.Function('f')
sp.diff(f(x**2),x)
sp.limit(sp.exp(-x),x,oo)
With a direction to approach the target limit.
sp.limit(1/x,x,0,"-")
Can find most integrals of logarithmic and exponential functions expressible with special functions. Can compute both definite integrals and antiderivatives.
For the anti-derivative of $sin(x)$
sp.integrate(sp.sin(x),x)
For the definite integral of $sin(x)$ from 0 to pi
sp.integrate(sp.sin(x),(x,0,sp.pi))
Unevaluated symbolic integrals and antiderivatives are represented by the Integral class. integrate may return these objects if it cannot evaluate the integral. It is also possible to create Integral objects directly, using the same syntax as integrate(). To evaluate them, call their .doit() method.
sp.Integral(sp.sin(x),x)
sp.Integral(sp.sin(x),(x,0,sp.pi))
i1 = sp.Integral(sp.sin(x),(x,0,sp.pi))
i1.doit()
A Taylor series approximation is an approximation of a function obtained by truncating its Taylor series.
sp.series(sp.cos(x),x)
The main function for solving equations is solve(). This solves by assuming the expression is equal to zero.
sp.solve(x**2-1,x)
Solving for a specific variable to break up the expression can be very helpful.
sp.solve(x**2 - sp.exp(a),x)
To solve a system of equations, pass a list of expressions to solve(): each one will be interpreted, as in the univariate case as an expression of the form expr=0. The result can be returned in one of two forms, depending on the mathematical structure of the input: either as a list of tuples, where each tuple contains the values for the variables in the order given to solve, or a single dictionary for use in subs(), mapping variables to their values.
sp.solve([-sp.exp(x**2)-y,y-3],x,y)
Use the dict=True option to always get a dictionary result, that can be used in subs.
sp.solve([-sp.exp(x**2)-y,y-2],x,y,dict=True)