Runge Kutta visualized

We're going to try to recreate this image from the Runge-Kutta methods Wikipedia page:

What's happening here

There's some function y(t) we're trying to approximate. It takes as input time, and gives us the value of y at that time. We know ( \frac{dy}{dt} = f(t, y) ). This means for any given t and y, we know what the instantaneous rate of change is for y, e.g. if y is 5, and time is 3, we can plug that into the function, and we'll see what y's velocity is, let's say its 10. That means advancing time by 1, we'd expect y to increase by 10.

However, since y's velocity is a function of time and of its current position, it won't increase by 10 exactly, since each time step will change its velocity slightly. So this Runge-Kutta provides a way of approximating that continuous change by taking a few small steps, and averaging them together.

There are 4 velocities we calculate, then we take a weighted average of them and multiply by the time step. Or put another way, we make small vectors of each of them and add them.

These velocities are:

[ k_1 = f(t_0, y_0) ]

[ k_2 = f(t_0 + \frac{h}{2}, y_0 + h\frac{k_1}{2}) ]

[ k_3 = f(t_0 + \frac{h}{2}, y_0 + h\frac{k_2}{2}) ]

[ k_4 = f(t_0 + h, y_0 + hk_3) ]

Labeled below, we have each of the k1, k2, k3 and k4 values in red. They're represented as vectors from their inputs, and scaled by h/4 sort of arbitrarily to make it look nice.

I also put in the "starting" vectors as light gray. This shows the "input" to f at each of these k's, with the red value being a depiction of the output.

Finally, the green arrows represent the final answer, i.e. the weighted average of the k's added to y0.

Observations

An interesting thing is that each k value depends on the k value before it, so they each factor in some information about what was happening before.

Also interesting is that the middle 2 vectors use the same "t" value, just with different y values. Also somewhat not intuitive is that the k4 value starts at the end of the time step. This means it represents the ending velocity, but we use it to influence where the thing will be before that time step.

  • We weigh the k2 & k3 twice as heavily as the other two.
  • If there's no factor of y in ( \frac{dy}{dt} ), then k2 and k3 are identical. This makes the gray vector from the starting point of k4 just an extension of the one to the starting point of k3 which isn't as interesting.

Sidenote: selecting a y(t) function to approximate

For the purposes of this illustration, I actually do know y(t), since I want to be able to tell how close the estimation method was. To get there, I started with just a basic parabola, ( y = t^2 ), then tweaked some values so it would fit more nicely into the graph, so I ended up with ( y = 0.5(t+0.5)^2 + 1 ).

The ( \frac{dy}{dt} ) in that case was just ( t + 0.5 ). From there, I wanted the ( \frac{dy}{dt} ) to also depend on y, so I made it ( \frac{dy}{dt} = t + 0.5 + 0.3y ).

It's been awhile since I've done multivariable integration, so I leaned on Wolfram Alpha to give me the solution for this when ( y_0 = 1.125 ):

[ y(t) = -12.7778 + 13.9028e^{0.3x} - 3.33333x ]

So that's the function I'm approximating above (i.e. the blue line in the background).

Plotting ( \frac{dy}{dt} )

An interesting thing to do with this is plot a field of what ( \frac{dy}{dt} ) looks like at all points on this graph.

Imports

import { createStandardGrid, drawVector, addArrowHead } from "@pcarleton/range-kutta-supporting-functions@287";

Function Definition

vectorAdd = (v1, v2) => {
    return [v1[0] + v2[0], v1[1] + v2[1]];
}