```
In [1]:
```# import everything from SymPy.
from sympy import *
# Set up Jupyter notebook to display math.
init_printing()

The following displays SymPy expressions and provides the option of showing results in LaTeX format.

```
In [2]:
```from sympy.printing import latex
def show(expr, show_latex=False):
"""Display a SymPy expression.
expr: SymPy expression
show_latex: boolean
"""
if show_latex:
print(latex(expr))
return expr

Create a symbol for time.

```
In [3]:
```t = symbols('t')
t

```
Out[3]:
```

If you combine symbols and numbers, you get symbolic expressions.

```
In [4]:
```expr = t + 1
expr

```
Out[4]:
```

The result is an `Add`

object, which just represents the sum without trying to compute it.

```
In [5]:
```type(expr)

```
Out[5]:
```

`subs`

can be used to replace a symbol with a number, which allows the addition to proceed.

```
In [6]:
```expr.subs(t, 2)

```
Out[6]:
```

`f`

is a special class of symbol that represents a function.

```
In [7]:
```f = Function('f')
f

```
Out[7]:
```

The type of `f`

is `UndefinedFunction`

```
In [8]:
```type(f)

```
Out[8]:
```

SymPy understands that `f(t)`

means `f`

evaluated at `t`

, but it doesn't try to evaluate it yet.

```
In [9]:
```f(t)

```
Out[9]:
```

`diff`

returns a `Derivative`

object that represents the time derivative of `f`

```
In [10]:
```dfdt = diff(f(t), t)
dfdt

```
Out[10]:
```

```
In [11]:
```type(dfdt)

```
Out[11]:
```

We need a symbol for `alpha`

```
In [12]:
```alpha = symbols('alpha')
alpha

```
Out[12]:
```

Now we can write the differential equation for proportional growth.

```
In [13]:
```eq1 = Eq(dfdt, alpha*f(t))
eq1

```
Out[13]:
```

And use `dsolve`

to solve it. The result is the general solution.

```
In [14]:
```solution_eq = dsolve(eq1)
solution_eq

```
Out[14]:
```

We can tell it's a general solution because it contains an unspecified constant, `C1`

.

In this example, finding the particular solution is easy: we just replace `C1`

with `p_0`

```
In [15]:
```C1, p_0 = symbols('C1 p_0')

```
In [16]:
```particular = solution_eq.subs(C1, p_0)
particular

```
Out[16]:
```

In the next example, we have to work a little harder to find the particular solution.

```
In [17]:
```r, K = symbols('r K')

Now we can write the differential equation.

```
In [18]:
```eq2 = Eq(diff(f(t), t), r * f(t) * (1 - f(t)/K))
eq2

```
Out[18]:
```

And solve it.

```
In [19]:
```solution_eq = dsolve(eq2)
solution_eq

```
Out[19]:
```

The result, `solution_eq`

, contains `rhs`

, which is the right-hand side of the solution.

```
In [20]:
```general = solution_eq.rhs
general

```
Out[20]:
```

We can evaluate the right-hand side at $t=0$

```
In [21]:
```at_0 = general.subs(t, 0)
at_0

```
Out[21]:
```

Now we want to find the value of `C1`

that makes `f(0) = p_0`

.

So we'll create the equation `at_0 = p_0`

and solve for `C1`

. Because this is just an algebraic identity, not a differential equation, we use `solve`

, not `dsolve`

.

The result from `solve`

is a list of solutions. In this case, we have reason to expect only one solution, but we still get a list, so we have to use the bracket operator, `[0]`

, to select the first one.

```
In [22]:
```solutions = solve(Eq(at_0, p_0), C1)
type(solutions), len(solutions)

```
Out[22]:
```

```
In [23]:
```value_of_C1 = solutions[0]
value_of_C1

```
Out[23]:
```

Now in the general solution, we want to replace `C1`

with the value of `C1`

we just figured out.

```
In [24]:
```particular = general.subs(C1, value_of_C1)
particular

```
Out[24]:
```

The result is complicated, but SymPy provides a method that tries to simplify it.

```
In [25]:
```particular = simplify(particular)
particular

```
Out[25]:
```

Often simplicity is in the eye of the beholder, but that's about as simple as this expression gets.

Just to double-check, we can evaluate it at `t=0`

and confirm that we get `p_0`

```
In [26]:
```particular.subs(t, 0)

```
Out[26]:
```

This solution is called the logistic function.

In some places you'll see it written in a different form:

$f(t) = \frac{K}{1 + A e^{-rt}}$

where $A = (K - p_0) / p_0$.

We can use SymPy to confirm that these two forms are equivalent. First we represent the alternative version of the logistic function:

```
In [27]:
```A = (K - p_0) / p_0
A

```
Out[27]:
```

```
In [28]:
```logistic = K / (1 + A * exp(-r*t))
logistic

```
Out[28]:
```

```
In [29]:
```simplify(particular - logistic)

```
Out[29]:
```

This test only works one way: if SymPy says the difference reduces to 0, the expressions are definitely equivalent (and not just numerically close).

But if SymPy can't find a way to simplify the result to 0, that doesn't necessarily mean there isn't one. Testing whether two expressions are equivalent is a surprisingly hard problem; in fact, there is no algorithm that can solve it in general.

```
In [30]:
```# Solution
alpha, beta = symbols('alpha beta')

```
In [31]:
```# Solution
eq3 = Eq(diff(f(t), t), alpha*f(t) + beta*f(t)**2)
eq3

```
Out[31]:
```

```
In [32]:
```# Solution
solution_eq = dsolve(eq3)
solution_eq

```
Out[32]:
```

```
In [33]:
```# Solution
general = solution_eq.rhs
general

```
Out[33]:
```

```
In [34]:
```# Solution
at_0 = general.subs(t, 0)

```
In [35]:
```# Solution
solutions = solve(Eq(at_0, p_0), C1)
value_of_C1 = solutions[0]
value_of_C1

```
Out[35]:
```

```
In [36]:
```# Solution
particular = general.subs(C1, value_of_C1)
particular.simplify()

```
Out[36]:
```

**Exercise:** Use WolframAlpha to solve the quadratic growth model, using either or both forms of parameterization:

```
df(t) / dt = alpha f(t) + beta f(t)^2
```

or

```
df(t) / dt = r f(t) (1 - f(t)/K)
```

Find the general solution and also the particular solution where `f(0) = p_0`

.

```
In [ ]:
```