# Imports for the entire sessionimport numpy as np # NumPy for numerical computationsimport sympy as sym # SymPy for symbolic mathematicsimport matplotlib as mpl # Matplotlib for plottingimport matplotlib.pyplot as plt # Matplotlib pyplot interfacefrom scipy.integrate import solve_ivp # SciPy ODE solverfrom IPython.display import Math, displaympl.rcParams['figure.dpi'] =150mpl.rcParams['axes.spines.top'] =Falsempl.rcParams['axes.spines.right'] =False
This document reviews the equation types and solution techniques covered in Appendix A of (Logan 2015), which corresponds to the material in Chapters 1 and 2 of the text. For each equation type we give the key idea by hand and then show how to use Python — primarily SymPy for symbolic work and SciPy for numerical work — to solve or visualize the same problem.
The goal is pattern recognition: when you meet a differential equation, the first step is always to identify its type, and the type determines the technique.
A.1 — Antiderivatives
The simplest differential equation is \[
x' = g(t).
\] The solution is the antiderivative of \(g\): \[
x(t) = \int g(t)\,dt + C,
\] or equivalently, with a variable upper limit, \[
x(t) = \int_a^t g(s)\,ds + C.
\]
Pattern. If the equation is \(x' = g(t)\) — right-hand side depends only on \(t\), not on \(x\) — integrate both sides directly.
A.2 — Separable Equations
A separable equation has the form \[
\frac{dx}{dt} = g(t)\,f(x).
\] The variables can be separated and each side integrated: \[
\int \frac{1}{f(x)}\,dx = \int g(t)\,dt + C.
\]
By Hand
Example. Solve the IVP \(x' = x^2 \cos t\), \(x(0) = 2\), and find the interval of existence (Logan, Review Exercise 2).
The particular solution is \[
\boxed{x(t) = \frac{1}{0.5 - \sin t}.}
\]
Interval of existence. The solution blows up when \(\sin t = 0.5\), i.e. \(t = \pi/6 + 2k\pi\) or \(t = 5\pi/6 + 2k\pi\). Since \(x(0) = 2 > 0\) and the nearest singularity to the right of \(t = 0\) is \(t = \pi/6\), the interval of existence is \(-7\pi/6 < t < \pi/6\).
Figure 1: Solution of \(x' = x^2\cos t\), \(x(0) = 2\). The solution blows up as \(t \to \pi/6 \approx 0.524\).
Tip
Pattern. If you can write the ODE as \(\frac{dx}{dt} = g(t)\,f(x)\) — one factor depending only on \(t\), the other only on \(x\) — separate and integrate.
A.3 — Linear First-Order Equations
The standard form of a first-order linear ODE is \[
x' + p(t)\,x = q(t).
\] Multiply by the integrating factor\(\mu(t) = e^{\int p(t)\,dt}\) to obtain \[
\bigl(\mu(t)\,x\bigr)' = \mu(t)\,q(t),
\] then integrate both sides.
By Hand
Example. Solve \(x' - \frac{4}{t}x = t\), \(x(1) = 2\), and find the interval of existence (Logan, Review Exercise 3).
(Logan states the equivalent form \(x(t) = t^2/6 + 11/(6t^4)\); the discrepancy arises from a different but equivalent way of writing the particular + homogeneous structure — both are correct for the data as given. Note: double-check the right-hand side of the original problem as printed in the text.)
Interval of existence. The coefficient \(p(t) = -4/t\) is discontinuous at \(t = 0\). Since \(x(1) = 2\), the solution exists on \((0, +\infty)\).
Pattern. Linear first-order? Write in standard form \(x' + p(t)x = q(t)\), compute \(\mu = e^{\int p\,dt}\), multiply through, integrate.
A.4 — Bernoulli Equations
A Bernoulli equation has the form \[
x' + p(t)\,x = q(t)\,x^n, \quad n \neq 0, 1.
\] The substitution \(y = x^{1-n}\) (so \(y' = (1-n)x^{-n}x'\)) transforms it into a linear equation for \(y(t)\).
By Hand
Example. Solve \(x' + x = t x^3\) (Logan, Review Exercise 1(i), rearranged).
Here \(p = 1\), \(q = t\), \(n = 3\). Let \(y = x^{-2}\), so \(y' = -2x^{-3}x'\).
Dividing the ODE by \(x^3\): \[
x^{-3}x' + x^{-2} = t.
\]
Since \(y' = -2 x^{-3}x'\), we have \(x^{-3}x' = -y'/2\), giving \[
-\frac{y'}{2} + y = t
\quad\Longrightarrow\quad
y' - 2y = -2t.
\]
This is linear in \(y\). Integrating factor \(\mu = e^{-2t}\): \[
(e^{-2t} y)' = -2t e^{-2t}.
\]
Pattern. Spot the \(x^n\) on the right. Set \(y = x^{1-n}\), divide through by \(x^n\), and use \(y' = (1-n)x^{-n}x'\) to arrive at a linear ODE for \(y\).
A.5 — Homogeneous Equations
An equation of the form \[
x' = f\!\left(\frac{x}{t}\right)
\] is called homogeneous (in the sense of scaling). The substitution \(y(t) = x(t)/t\) (so \(x = ty\), \(x' = y + ty'\)) reduces it to a separable equation for \(y\).
Divide by \(t\): \[
x' = \frac{x}{t} - \frac{1}{2}\cos^2\!\!\left(\frac{2x}{t}\right).
\]
This has the form \(x' = f(x/t)\) with \(f(u) = u - \tfrac{1}{2}\cos^2(2u)\). Let \(y = x/t\): \[
y + ty' = y - \frac{1}{2}\cos^2(2y)
\quad\Longrightarrow\quad
ty' = -\frac{1}{2}\cos^2(2y).
\]
SymPy’s dsolve cannot handle this equation directly in its original form because the nonlinear term \(\cos^2(2x/t)\) prevents symbolic simplification of the implicit solution. Instead we implement the substitution \(y = x/t\) ourselves — exactly as we do by hand — and ask SymPy to solve the resulting separable ODE for \(y(t)\).
t = sym.Symbol('t', positive=True)y = sym.Function('y') # y = x/t# After substituting x = t*y the equation becomes t*y' = -1/2 * cos^2(2y)ode_y = sym.Eq(t * y(t).diff(t),-sym.Rational(1, 2) * sym.cos(2*y(t))**2)sol_y = sym.dsolve(ode_y, y(t))display(Math(r'\text{Solution for } y = x/t\text{: }'+ sym.latex(sol_y)))# Back-substitute y -> x/t to state the solution in terms of xt_sym, x_sym = sym.symbols('t x')C1 = sym.Symbol('C1')# The implicit solution tan(2y) = -ln(t) + C1 => tan(2x/t) = C1 - ln(t)implicit_sol = sym.Eq(sym.tan(2*x_sym/t_sym), C1 - sym.log(t_sym))display(Math(r'\text{In terms of } x\text{: }'+ sym.latex(implicit_sol)))
Pattern. If every term is degree-1 in \((x, t)\) together (or you can write the RHS as a function of \(x/t\) alone), substitute \(y = x/t\).
A.6 — Exact Equations
The equation \[
f(t, x) + g(t, x)\,x' = 0
\] is exact if \(\partial f/\partial x = \partial g/\partial t\). When exact, there exists a potential function \(H(t, x)\) such that \(H_t = f\) and \(H_x = g\), and the solution is \(H(t, x) = C\).
\(\displaystyle H(t,x) = 3 t^{2} x - t x^{3} - 2 x^{2} = C\)
Tip
Pattern. Write in the form \(f\,dt + g\,dx = 0\). Check \(f_x = g_t\). If exact, integrate \(f\) w.r.t. \(t\) (or \(g\) w.r.t. \(x\)) and determine the integration function by differentiating and matching.
A.7 — Autonomous Equations
An autonomous equation has the form \[
x' = f(x),
\] where the right side depends only on \(x\). Key qualitative features:
Equilibria (constant solutions): values \(x^*\) where \(f(x^*) = 0\).
Stability: \(x^*\) is stable if \(f'(x^*) < 0\), unstable if \(f'(x^*) > 0\).
Phase line: draw \(f(x)\) vs \(x\); arrows point right where \(f > 0\) and left where \(f < 0\).
By Hand
Example. Analyze the population model (Logan, Review Exercise 6): \[
p' = rp\,\frac{K - p}{K + ap}, \quad r, K, a > 0.
\]
Equilibria. Set \(p' = 0\):
\(p = 0\) (trivial)
\(K - p = 0 \Rightarrow p = K\)
Stability. Let \(f(p) = rp(K-p)/(K+ap)\).
\(f'(0) = r \cdot K/K = r > 0\)\(\Rightarrow\)\(p = 0\) is unstable.
\(f'(K) < 0\) (can be verified by differentiation) \(\Rightarrow\)\(p = K\) is stable.
For \(0 < p < K\): \(f(p) > 0\) so \(p\) increases toward \(K\). For \(p > K\): \(f(p) < 0\) so \(p\) decreases toward \(K\). The population always approaches the carrying capacity \(K\).
Phase Line and Direction Field with SymPy/Matplotlib
Figure 2: Phase portrait and direction field for the population model with \(r=1\), \(K=10\), \(a=2\). Solutions are attracted to the stable equilibrium \(p = K = 10\).
Tip
Pattern. For \(x' = f(x)\): find zeros of \(f\), check the sign of \(f'\) at each zero, draw the phase line, then sketch or compute solution curves with scipy.integrate.solve_ivp.
A.8 — Second-Order Linear Equations
There are two second-order linear homogeneous equations that can be solved in closed form.
A.8.1 — Constant-Coefficient Equations
\[
ax'' + bx' + cx = 0.
\]
Try \(x = e^{\lambda t}\): the characteristic equation is \[
a\lambda^2 + b\lambda + c = 0.
\]
Three cases based on the discriminant \(\Delta = b^2 - 4ac\):
Case
Roots
General solution
\(\Delta > 0\)
\(\lambda_1 \neq \lambda_2\) real
\(x = c_1 e^{\lambda_1 t} + c_2 e^{\lambda_2 t}\)
\(\Delta = 0\)
repeated root \(\lambda\)
\(x = (c_1 + c_2 t)e^{\lambda t}\)
\(\Delta < 0\)
\(\lambda = \alpha \pm \beta i\)
\(x = e^{\alpha t}(c_1\cos\beta t + c_2\sin\beta t)\)
Pattern. Constant-coefficient? Guess \(e^{\lambda t}\); read off the characteristic equation. Cauchy–Euler? Guess \(t^m\); read off the indicial equation. Both reduce to a quadratic — analyze the discriminant.
A.9 — Nonhomogeneous Equations
The general nonhomogeneous equation \[
x'' + p(t)\,x' + q(t)\,x = f(t)
\] has general solution \[
x(t) = x_h(t) + x_p(t),
\] where \(x_h\) is the general solution of the homogeneous equation (set \(f = 0\)) and \(x_p\) is any particular solution.
Method 1 — Undetermined Coefficients
When the coefficients \(p, q\) are constant and \(f(t)\) is a polynomial, exponential, sine, cosine, or sums/products thereof, guess \(x_p\) in the same family as \(f\) and solve for the unknown coefficients.
Always applicable. If \(x_1, x_2\) are two linearly independent solutions of the homogeneous equation with Wronskian \(W = x_1 x_2' - x_1' x_2\), then \[
x_p(t) = -x_1(t)\int\frac{x_2(t)f(t)}{W(t)}\,dt
+ x_2(t)\int\frac{x_1(t)f(t)}{W(t)}\,dt.
\]
Example. Find a particular solution of \(x'' - x' - 2x = \cosh t\) (Logan, Review Exercise 7).
Replace \(\cosh t\) by \((e^t + e^{-t})/2\) and compute the integrals. The result is \[
x_p = -\frac{1}{6}e^t\sinh t + \text{(constants absorbed into }x_h\text{)}.
\]
SymPy’s dsolve cannot handle cosh(t) as a forcing function for this ODE (it converts exponentials back to cosh internally before classifying the equation, and the hyperbolic form is not supported by its nonhomogeneous solvers). This is a good opportunity to implement the variation of parameters formula directly in SymPy — mirroring the by-hand steps exactly and giving students a reusable template for cases where dsolve falls short.
t = sym.Symbol('t')# Step 1 — homogeneous solutions (from characteristic roots lambda=2, lambda=-1)x1 = sym.exp(2*t)x2 = sym.exp(-t)# Step 2 — Wronskian W = x1*x2' - x1'*x2W = sym.simplify(x1*sym.diff(x2, t) - sym.diff(x1, t)*x2)display(Math(r'W(t) = '+ sym.latex(W)))# Step 3 — forcing function and the two VoP integralsf = sym.cosh(t)int1 = sym.integrate(-x2 * f / W, t) # coefficient of x1int2 = sym.integrate( x1 * f / W, t) # coefficient of x2# Step 4 — particular solutionxp = sym.trigsimp(sym.expand(x1*int1 + x2*int2))display(Math(r'x_p(t) = '+ sym.latex(xp)))# Step 5 — state the general solution symbolicallyC1, C2 = sym.symbols('C1 C2')x_gen = C1*x1 + C2*x2 + xpdisplay(Math(r'x(t) = '+ sym.latex(sym.collect(sym.expand(x_gen), [sym.exp(2*t), sym.exp(-t)]))))
Pattern. Constant coefficients + “nice” \(f(t)\)? Try undetermined coefficients first — it is usually faster. Variable coefficients or awkward \(f(t)\)? Use variation of parameters.
A.10 — Conservation Laws
Newton’s second law \(mx'' = F(x, x')\) is rewritten as the first-order system \[
x' = y, \qquad y' = m^{-1}F(x, y).
\] When \(F = F(x)\) depends only on position, the equation is conservative with potential \[
V(x) = -\int F(x)\,dx.
\] Dividing the two equations and integrating gives the energy integral\[
\frac{1}{2}m y^2 + V(x) = E,
\] where \(E\) is determined by the initial conditions. Replacing \(y = dx/dt\) and separating gives \[
t = \pm\sqrt{\frac{m}{2}}\int_{x_0}^x \frac{d\xi}{\sqrt{E - V(\xi)}} + t_0.
\]
This is conservative with \(F(x) = -3x^2\) and \[
V(x) = -\int(-3x^2)\,dx = x^3.
\] Energy conservation: \[
\frac{1}{2}(x')^2 + x^3 = E.
\] Separating and integrating: \[
t + C = \pm\int \frac{dx}{\sqrt{2(E - x^3)}}.
\]
This integral is an elliptic integral — there is no elementary closed form in general, but we can solve the IVP numerically.
Figure 3: Phase portrait (left) and time series (right) for the conservative system \(x'' = -3x^2\) with two initial conditions. Trajectories follow curves of constant energy \(E\).
Tip
Pattern. Conservative second-order equation? Form the energy \(E = \tfrac{1}{2}my^2 + V(x)\) and use it as a first integral to reduce the order. When the energy integral cannot be evaluated in elementary form, use scipy.integrate.solve_ivp.
A.11 — Selected Review Exercises with Python
This section works through a few of the longer review exercises from Logan’s Appendix A using Python to check or visualize the answers.
Bifurcation Diagram for \(x' = (x - a)(x^2 - a)\)
Problem. For all values of the parameter \(a\), find the equilibrium solutions of \(x' = (x-a)(x^2-a)\) and determine their stability. Summarize on a bifurcation diagram (Logan, Review Exercise 4).
For \(a < 0\): only real equilibrium is \(x^* = a\).
For \(a = 0\): triple root at \(x^* = 0\) (degenerate).
For \(a > 0\): three equilibria — \(x^* = -\sqrt{a}\), \(a\), \(\sqrt{a}\) — though \(a\) may coincide with \(\pm\sqrt{a}\) at special values (\(a = 1\): \(x^* = \pm 1\) and \(x^* = 1\), so actually \(x^* = \pm 1\)).
Problem. A spherical droplet loses volume by evaporation at a rate proportional to its surface area. Find \(r = r(t)\) in terms of the proportionality constant \(k > 0\) and initial radius \(r_0\) (Logan, Review Exercise 5).
The second solution involves \(\ln\!\left|\tfrac{1+t}{1-t}\right|\), confirming Logan’s result \[
x_2(t) = -1 + \frac{t}{2}\ln\frac{1+t}{1-t}.
\]
References
Logan, J David. 2015. A First Course in Differential Equations, Third Edition.
TipExpand for Session Info
import sys, importlibprint("Python version:", sys.version)for name in ['numpy', 'sympy', 'scipy', 'matplotlib']: mod = importlib.import_module(name)print(f"{name}=={mod.__version__}")