Current section
Files
Jump to
Current section
Files
lib/ode/euler.ex
defmodule Fluxion.ODE.Euler do
@moduledoc """
The Euler method for solving the initial value problem for ordinary
differential equations of the form @y'(t) = f(t, y(t))@.
Euler's method defines a series of @y@ values according to the following:
@y\\_{k+1} = y\\_k + hf(t\\_k, y\\_k)@ with @k = 0, 1, ..., n@
In this module, the value for @y\\_0@ will be referred to as "initial" and
the value for @h@ will be referred to as the "stride".
The stride is a crucial parameter for Euler's method. Euler's method is
convergent because as @h \\to 0@ the series of values @y\\_k@ approaches
@y(t\\_k)@.
Convergence rate is @O(h)@ and numeric error accumulates as @O(1/h)@,
so properly selected stride is important to balance convergence and
numeric error.
"""
@doc """
Step Euler's solution @n@ times.
## Example
For the purpose of example, start from a known function for @y@ like
@y(t) = sin(t)@.
This gives @y'(t) = cos(t)@.
Given this, it's possible to approximate the real solution with
Euler's method.
iex> defmodule TSq do
...> @behaviour Fluxion.ODE
...> @impl true
...> def f(x, _y_k), do: :math.cos(x)
...> end
iex> steps = 10
iex> stride = 0.01
iex> # Compute the real value of y(t) after 10 steps
iex> real_y_1 = :math.sin(stride*steps)
iex> # Approximate the value of y(t) after 10 steps
iex> approx_y_1 = Fluxion.ODE.Euler.step(TSq, 0 , stride, steps)
iex> # Compare the results
iex> Fluxion.Math.is_equal(real_y_1, approx_y_1, 1.0e-3)
:true
"""
@spec step(
ode :: Fluxion.ODE.t(),
initial :: number,
stride :: number,
n :: number
) :: number
def step(ode, initial, stride, n \\ 1) do
iterate(ode, initial, stride, 0, n)
end
# Handle the last iteration
defp iterate(ode, y_k, stride, t, 1) do
y_k + stride * Fluxion.ODE.f!(ode, t, y_k)
end
# Handle all other iteration steps
defp iterate(ode, y_k, stride, t, n) do
dydt = Fluxion.ODE.f!(ode, t, y_k)
y_next = y_k + stride * dydt
iterate(ode, y_next, stride, t + stride, n - 1)
end
end