Skip to main content

Rational, Logarithm and Exponential Expressions with SymPy - Math with Python

·1135 words·6 mins·
Python Sympy Math Equations
Author
Francisco Bustamante
A chemist working with Data Science and Python Programming.
Table of Contents
SymPy - This article is part of a series.
Part 3: This Article

In this article we will see how to work with rational, exponential and logarithmic expressions with the SymPy library in Python.

Importing SymPy and creating some symbols
#

Let’s start by importing SymPy and creating some symbols that we will use throughout the article. If you have any difficulty with the concept of symbols in the library, see the first article in the series about SymPy:

import sympy

# settings for better outputs in the article, can be ignored
sympy.init_printing(
    use_latex="mathjax",
    scale=1.0,
    order="grlex",
    forecolor="Black",
    backcolor="White",
)

x, y, a, b, c, d, n = sympy.symbols('x y a b c d n')

Rational expressions
#

A rational function is any function that can be expressed as a ratio (quotient) of polynomials:

$$ f(x) = \frac{P(x)}{Q(x)} $$

By default, SymPy does not combine or divide rational expressions. For example:

a/b + c/d

\(\displaystyle \frac{a}{b} + \frac{c}{d}\)

If we want to “join” the fractions, which we would manually do with the least common multiple (LCM) procedure, we use the together method:

sympy.together(a/b + c/d)

\(\displaystyle \frac{a d + b c}{b d}\)

Now let’s see the opposite, a situation where we have a rational expression and we would like to write it in the form of simpler fractions, called partial fractions. A situation where we usually do this operation is in solving problems of integrals of rational functions. In this case, we use the apart method:

(x**2 + x + 4)/(x + 2)

\(\displaystyle \frac{x^{2} + x + 4}{x + 2}\)

sympy.apart( (x**2 + x + 4)/(x + 2) )

\(\displaystyle x - 1 + \frac{6}{x + 2}\)

Exponential and logarithmic expressions
#

Logarithms and exponentials appear in the most different contexts of mathematical problems. Thus, it is important to know how to use them with SymPy, especially the implicit considerations that the package makes and that can give rise to results that are not always expected at first.

Let’s begin by notation. In most programming languages and their libraries, the representation log expresses the natural logarithm that, in written works, we usually write as ln. In Python and in SymPy it is no different, but SymPy tries to be more friendly and considers ln = log:

sympy.ln(x)

\(\displaystyle \log{\left(x \right)}\)

See that in the result log(x) appears even though we wrote ln(x) in the cell. And remember that this is a “friendliness” of SymPy and not of Python itself. For example, do not try to use it with the standard library math:

import math

math.ln(10)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

Cell In[7], line 3
      1 import math
----> 3 math.ln(10)


AttributeError: module 'math' has no attribute 'ln'
math.log(10)  # here it works, being log the representation of ln. Do not confuse with log10

\(\displaystyle 2.30258509299405\)

math.log10(10)

\(\displaystyle 1.0\)

When studying mathematics at more basic levels, the following identities are taught for logarithms:

$$ \begin{align*} \log (xy) &= \log(x) + \log(y) \\ \log (x^n) &= n \log(x) \end{align*} $$

However, such identities are not valid if x and y are arbitrary complex due to the branch point (discontinuity) existing in the complex plane for a logarithm. Let’s see how SymPy handles such expressions:

sympy.log(x * y)

\(\displaystyle \log{\left(x y \right)}\)

sympy.log(x**n)

\(\displaystyle \log{\left(x^{n} \right)}\)

Note that SymPy kept the multiplication and exponentiation. Let’s try to force the expansion with the expand_log method:

sympy.expand_log(sympy.log(x * y))

\(\displaystyle \log{\left(x y \right)}\)

sympy.expand_log(sympy.log(x**n))

\(\displaystyle \log{\left(x^{n} \right)}\)

Apparently, it did not work. Or rather, it worked, because we did not specify boundary conditions for x, y and n. Therefore, SymPy makes no implicit considerations and considers that, in the worst case, they are complex and the expansion cannot be performed.

Now, expand_log, like other SymPy methods that we will see in articles in this series, has a parameter called force that, when True, forces the expansion to be performed:

sympy.expand_log(sympy.log(x * y), force=True)

\(\displaystyle \log{\left(x \right)} + \log{\left(y \right)}\)

sympy.expand_log(sympy.log(x**n), force=True)

\(\displaystyle n \log{\left(x \right)}\)

However, in real life, good ideas do not need to be forced. Or, bringing it to a context closer to programming, let’s remember the Zen of Python:

import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Look at the second verse: explicit is better than implicit. Thus, better than forcing the expansion would be to make the boundary conditions explicit for each symbol and, if such conditions are adequate, the expansion occurs naturally.

Since the above identities are valid only if x and y are positive and n is a real number, let’s redefine such symbols by specifying such conditions:

x, y = sympy.symbols('x y', positive=True)
n = sympy.symbols('n', real=True)

Let’s check if the expansions are now possible:

sympy.expand_log(sympy.log(x * y))

\(\displaystyle \log{\left(x \right)} + \log{\left(y \right)}\)

sympy.expand_log(sympy.log(x**n))

\(\displaystyle n \log{\left(x \right)}\)

In my opinion, this way is much better. Whenever you know the conditions of each symbol (if it is always positive, negative, real…) it is a good practice to make it explicit when creating the symbol.

For the opposite operation, combination, there is logcombine:

sympy.logcombine(sympy.log(x) + sympy.log(y))

\(\displaystyle \log{\left(x y \right)}\)

sympy.logcombine(n * sympy.log(x))

\(\displaystyle \log{\left(x^{n} \right)}\)

Finally, let’s talk about the Euler number, the base in the natural logarithm. This number has several definitions:

$$ e \equiv \lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^n \equiv \lim_{x \to 0} \left(1 + x\right)^{\frac{1}{x}} \equiv \sum_{n=0}^{\infty} \frac{1}{n!} $$

On SymPy, it is represented by E. Therefore, exp(x) is equivalent to E**x:

sympy.E

\(\displaystyle e\)

sympy.E**x

\(\displaystyle e^{x}\)

sympy.exp(x)

\(\displaystyle e^{x}\)

We can obtain a numerical approximation for E using ways already seen in other articles:

sympy.E.n()

\(\displaystyle 2.71828182845905\)

Let’s check with E that log really represents the natural logarithm:

sympy.log(sympy.E**2)

\(\displaystyle 2\)

But, what if we want the logarithm in some other base? Just pass the base as the second argument of the log method:

sympy.log(10**2, 10)

\(\displaystyle 2\)

Conclusion and more articles about SymPy #

More one article about SymPy. Rational expressions, logarithms and exponentials appear several times in several concepts, so what was seen here in this article will be useful several times in the next articles.

See you next time.

SymPy - This article is part of a series.
Part 3: This Article

Related

Solving equations with SymPy - Math with Python
·1530 words·8 mins
Python Sympy Math Equations
Much of the study of mathematics at more fundamental levels is dedicated to solving equations and systems of equations. In this article we will see how to use SymPy for these tasks quickly and intuitively.
Introduction to SymPy - Symbolic Math with Python
·2644 words·13 mins
Python Sympy Math
In this article, we’ll see an introduction to SymPy, one of the most powerful math packages for the Python language
Isotherms of ideal gases with Python and SymPy
·2061 words·10 mins
Python Sympy Matplotlib Physics
In this post we will see how we can use SymPy and Matplotlib, both Python packages, to visualize ideal gas isotherms.