Usage
In this section we’ll take a quick tour of smoothmath. We’ll see how to write smoothmath expressions, how to evaluate them, and how to differentiate them.
Basics of smoothmath expressions
We use smoothmath expressions to represent numbers and mathematical functions.
Let’s start with something easy: adding two numbers. We can use the
Constant class to represent a number as an
expression, and we represent addition using the
Add class.
>>> from smoothmath.expression import Constant, Add
>>> Add(Constant(2), Constant(3))
Add(Constant(2), Constant(3))
Notice that python just echoed back the expression we entered! Unlike the usual
math operations built in to python, smoothmath expressions don’t automatically
reduce to numbers during program evaluation. It would get tiring to write
Add() as a function on the left all of the time, so smoothmath supports using
the standard infix operators like the plus symbol. Under the hood, however,
smoothmath always works with operators written on the left.
>>> from smoothmath.expression import Constant
>>> Constant(2) + Constant(3)
Add(Constant(2), Constant(3))
>>> Constant(7) - Constant(1)
Minus(Constant(7), Constant(1))
>>> Constant(4) * Constant(5)
Multiply(Constant(4), Constant(5))
>>> Constant(2) ** Constant(5)
Power(Constant(2), Constant(5))
Because smoothmath doesn’t try to reduce expressions automatically, we can write expressions that have variables representing unknown values. Notice how differently this works from using the stardard python math operators.
>>> x + 4 # python gets upset because it doesn't have a value for x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> from smoothmath.expression import Variable, Constant
>>> Variable("x") + Constant(4) # OK, these are smoothmath expressions!
Add(Variable("x"), Constant(4))
You can get a full list of building blocks for making expressions from the
smoothmath.expression module. But what can we do once the have an
expression? The simplest thing we can do is evaluate it using the
at() method!
>>> from smoothmath.expression import Variable, Constant
>>> z = Constant(2) * Variable("x") + Constant(4)
>>> z.at(3)
10.0
>>> z.at(-2)
0.0
Taking the derivative of an expression with one variable
We can take the derivative of a smoothmath expression. Here’s how:
>>> from smoothmath import Derivative
>>> from smoothmath.expression import Variable, NthPower
>>> z = NthPower(Variable("x"), n=2) + Variable("x")
>>> derivative = Derivative(z)
>>> derivative
Derivative(Add(NthPower(Variable("x"), n=2), Variable("x")))
>>> derivative.as_expression()
Add(Multiply(Constant(2), Variable("x")), Constant(1))
>>> derivative.at(1)
3.0
>>> derivative.at(5)
11.0
Just as before, smoothmath doesn’t reduce by default. Instead, to reduce the
derivative, we call the as_expression() method.
And we can call the at() method to evaulate the
derivative for different x values.
Curiously, while smoothmath can compute the derivative as an expression, it does not have to in order to evaluate the derivative at x values.
>>> from smoothmath import Derivative
>>> from smoothmath.expression import Variable, Constant
>>> z = NthPower(Variable("x"), n=2) + Variable("x")
>>> early_derivative = Derivative(z, compute_early=True)
>>> late_derivative = Derivative(z, compute_early=False)
The “early derivative” and “late derivative” behave identically: they give all the same answers when calling their methods. But they have different performance characteristics. If you only need to evaluate your derivative at a few x values, the late derivative will be fast. But if you need to evaluate your derivative at many x values, the early derivative can give a performance boost. By default, the derivative (and other ways of differentiating) do not compute early.
Taking the differential of an expression with several variables
Up until now, our expressions have only used a single variable,
Variable("x"). This makes things simple: we can evaluate and differentiate
without needing to specify which variable we have in mind. To work with
expressions with multiple variables, we’ll need to be a little more careful,
and we’ll need to work with points.
>>> from smoothmath import Point
>>> Point(x=7, y=-2)
Point(x=7, y=-2)
When specifying a point, we use keyword arguments that name our variables. The order of the arguments does not matter, but the variable names do!
>>> from smoothmath import Point
>>> Point(x=7, y=-2) == Point(x=7, y=-2)
True
>>> Point(x=7, y=-2) == Point(y=-2, x=7)
True
>>> Point(x=7, y=-2) == Point(v=7, w=-2)
False
Let’s use a point to evaluate an expression that has two variables.
>>> from smoothmath import Point
>>> from smoothmath.expression import Variable
>>> x = Variable("x")
>>> y = Variable("y")
>>> z = x ** 2 + x * y - y ** 2
>>> z.at(Point(x=3, y=2))
11.0
Great! While we can only take the derivative when an expression has a single
variable, we can take the Differential of an expression that
has multiple variables. The differential has several components, one for each
variable. Each component of the differential is referred to as a partial.
>>> from smoothmath import Differential, Point
>>> from smoothmath.expression import Variable
>>> x = Variable("x")
>>> y = Variable("y")
>>> z = x ** 2 + x * y - y ** 2
>>> differential = Differential(z)
>>> x_partial = differential.component(x)
>>> x_partial.as_expression()
Add(Multiply(Constant(2), Variable("x")), Variable("y"))
>>> x_partial.at(Point(x=1, y=2))
4.0
>>> y_partial = differential.component(y)
>>> y_partial.as_expression()
Minus(Variable("x"), Multiply(Constant(2), Variable("y")))
>>> y_partial.at(Point(x=1, y=2))
-3.0
If we only need the differential at a single point, we can use a
LocatedDifferential.
>>> from smoothmath import Differential, Point
>>> from smoothmath.expression import Variable
>>> x = Variable("x")
>>> y = Variable("y")
>>> z = x ** 2 + x * y - y ** 2
>>> differential = Differential(z)
>>> located_differential = differential.at(Point(x=1, y=2))
>>> located_differential.component(x)
4.0
>>> located_differential.component(y)
-3.0
Taking a located differential is a fast way to compute partials for every variable all in one go.