From 4293e9d4e66e02b77ac1d18b0212705a5cb7418c Mon Sep 17 00:00:00 2001 From: James Schloss Date: Sun, 7 Nov 2021 18:00:42 +0100 Subject: [PATCH 1/3] removing Euler and adjacent chapters --- SUMMARY.md | 2 - .../differential_equations.md | 21 - .../forward_euler_method/code/asm-x64/euler.s | 97 ----- .../forward_euler_method/code/c/SConscript | 6 - contents/forward_euler_method/code/c/euler.c | 35 -- .../code/clisp/euler.lisp | 29 -- .../forward_euler_method/code/cpp/euler.cpp | 55 --- .../forward_euler_method/code/elm/elm.json | 28 -- .../code/elm/src/Euler.elm | 397 ------------------ .../code/fortran/euler.f90 | 64 --- .../forward_euler_method/code/go/euler.go | 39 -- .../code/haskell/euler.hs | 20 - .../code/java/ForwardEuler.java | 38 -- .../code/javascript/euler.js | 31 -- .../forward_euler_method/code/julia/euler.jl | 39 -- .../forward_euler_method/code/matlab/euler.m | 46 -- .../code/nim/forwardeuler.nim | 32 -- .../forward_euler_method/code/python/euler.py | 31 -- .../forward_euler_method/code/rust/euler.rs | 31 -- .../code/swift/euler.swift | 42 -- contents/forward_euler_method/code/v/euler.v | 37 -- .../forward_euler_method.md | 174 -------- contents/forward_euler_method/res/error.png | Bin 8736 -> 0 bytes .../forward_euler_method/res/instability.png | Bin 10680 -> 0 bytes 24 files changed, 1294 deletions(-) delete mode 100644 contents/differential_equations/differential_equations.md delete mode 100644 contents/forward_euler_method/code/asm-x64/euler.s delete mode 100644 contents/forward_euler_method/code/c/SConscript delete mode 100644 contents/forward_euler_method/code/c/euler.c delete mode 100644 contents/forward_euler_method/code/clisp/euler.lisp delete mode 100644 contents/forward_euler_method/code/cpp/euler.cpp delete mode 100644 contents/forward_euler_method/code/elm/elm.json delete mode 100644 contents/forward_euler_method/code/elm/src/Euler.elm delete mode 100644 contents/forward_euler_method/code/fortran/euler.f90 delete mode 100644 contents/forward_euler_method/code/go/euler.go delete mode 100644 contents/forward_euler_method/code/haskell/euler.hs delete mode 100644 contents/forward_euler_method/code/java/ForwardEuler.java delete mode 100644 contents/forward_euler_method/code/javascript/euler.js delete mode 100644 contents/forward_euler_method/code/julia/euler.jl delete mode 100644 contents/forward_euler_method/code/matlab/euler.m delete mode 100644 contents/forward_euler_method/code/nim/forwardeuler.nim delete mode 100644 contents/forward_euler_method/code/python/euler.py delete mode 100644 contents/forward_euler_method/code/rust/euler.rs delete mode 100644 contents/forward_euler_method/code/swift/euler.swift delete mode 100644 contents/forward_euler_method/code/v/euler.v delete mode 100644 contents/forward_euler_method/forward_euler_method.md delete mode 100644 contents/forward_euler_method/res/error.png delete mode 100644 contents/forward_euler_method/res/instability.png diff --git a/SUMMARY.md b/SUMMARY.md index d76af219d..e41ba39d4 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -32,8 +32,6 @@ * [FFT](contents/cooley_tukey/cooley_tukey.md) * [Decision Problems](contents/decision_problems/decision_problems.md) * [Stable Marriage Problem](contents/stable_marriage_problem/stable_marriage_problem.md) -* [Differential Equation Solvers](contents/differential_equations/differential_equations.md) - * [Forward Euler Method](contents/forward_euler_method/forward_euler_method.md) * [Physics Solvers](contents/physics_solvers/physics_solvers.md) * [Verlet Integration](contents/verlet_integration/verlet_integration.md) * [Quantum Systems](contents/quantum_systems/quantum_systems.md) diff --git a/contents/differential_equations/differential_equations.md b/contents/differential_equations/differential_equations.md deleted file mode 100644 index 3cfbba1fa..000000000 --- a/contents/differential_equations/differential_equations.md +++ /dev/null @@ -1,21 +0,0 @@ -# Differential Equations - -Differential equations lie at the heart of many different systems in physics, economics, biology, chemistry, and many other areas of research and engineering. -Here, we discuss many different methods to solve particular sets of differential equations. - -## License - -##### Code Examples - -The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/master/LICENSE.md)). - -##### Text - -The text of this chapter was written by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). - -[

](https://creativecommons.org/licenses/by-sa/4.0/) - -##### Pull Requests - -After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter: -- none diff --git a/contents/forward_euler_method/code/asm-x64/euler.s b/contents/forward_euler_method/code/asm-x64/euler.s deleted file mode 100644 index 98e303b00..000000000 --- a/contents/forward_euler_method/code/asm-x64/euler.s +++ /dev/null @@ -1,97 +0,0 @@ -.intel_syntax noprefix - -.section .rodata - three: .double -3.0 - fabs_const: - .long 4294967295 - .long 2147483647 - .long 0 - .long 0 - inital_val: .double 1.0 - threshold: .double 0.01 - timestep: .double 0.01 - error_fmt: .string "%f %f\n" - fmt: .string "%d\n" - -.section .text - .global main - .extern printf - .extern exp - -# rdi - array size -# rsi - array ptr -# xmm0 - timestep -solve_euler: - movsd xmm1, inital_val - lea rax, [rsi + 8 * rdi + 8] # Set to end of the array -solve_euler_loop: - movsd xmm3, three # Set to -3.0 - mulsd xmm2, xmm1 # xmm2 = -3.0 * array[i-1] * timestep - mulsd xmm2, xmm0 - subsd xmm1, xmm2 # xmm1 = xmm1 - xmm2 - movsd QWORD PTR [rsi], xmm1 - add rsi, 8 - cmp rsi, rax # Test if we have gone through the array - jne solve_euler_loop -solve_euler_return: - ret - -# rdi - array size -# rsi - array ptr -# xmm0 - timestep -# xmm1 - threshold -# RET rax - success code 0 if sucess else 1 -check_result: - push r12 - push r13 - xor rax, rax # Return code is 0 - xor r12, r12 # The index is set to 0 - mov r13, rdi # Moving array size to free rdi for printf - movsd xmm2, xmm0 # Moving timestep to free xmm0 for exp - jmp loop_check -results_loop: - cvtsi2sd xmm0, r12 # Making int to a double - movsd xmm3, three # Calculating exp(-3.0 * i * timestep) - mulsd xmm0, xmm3 - mulsd xmm0, xmm2 - call exp - movsd xmm3, QWORD PTR [rsi + r12 * 8] # Calculating abs(array[i] - xmm0) - subsd xmm2, xmm3 - movq xmm3, fabs_const - andpd xmm0, xmm3 - comisd xmm0, xmm1 # Check if abs(...) > threshold - jbe if_false - mov rdi, OFFSET error_fmt # If true print out array[i] and solution - mov rax, 1 - call printf - mov rax, 1 # and set sucess code to failed (rax = 1) -if_false: - add r12, 1 -loop_check: - cmp r12, r13 # Check if index is less the array size - jle results_loop - pop r13 - pop r12 - ret - -main: - push rbp - sub rsp, 800 # Making double array[100] - mov rdi, 100 - mov rsi, rsp - movsd xmm0, timestep - call solve_euler # Calling solve_euler - mov rdi, 100 - mov rsi, rsp - movsd xmm0, timestep - movsd xmm1, threshold - call check_result # Check if results are correct - mov rdi, OFFSET fmt - mov rsi, rax - xor rax, rax - call printf # Print out success code - add rsp, 800 # Deallocating array - pop rbp - xor rax, rax - ret - diff --git a/contents/forward_euler_method/code/c/SConscript b/contents/forward_euler_method/code/c/SConscript deleted file mode 100644 index 34a951e7f..000000000 --- a/contents/forward_euler_method/code/c/SConscript +++ /dev/null @@ -1,6 +0,0 @@ -Import('*') -from pathlib import Path - -dirname = Path.cwd().parents[1].stem - -env.C(f'#/build/c/{dirname}', Glob('*.c'), LIBS='m') diff --git a/contents/forward_euler_method/code/c/euler.c b/contents/forward_euler_method/code/c/euler.c deleted file mode 100644 index 9ce095930..000000000 --- a/contents/forward_euler_method/code/c/euler.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include - -void solve_euler(double timestep, double *result, size_t n) { - if (n != 0) { - result[0] = 1; - for (size_t i = 1; i < n; ++i) { - result[i] = result[i-1] - 3.0 * result[i-1] * timestep; - } - } -} - -int check_result(double *result, size_t n, double threshold, double timestep) { - int is_approx = 1; - for (size_t i = 0; i < n; ++i) { - double solution = exp(-3.0 * i * timestep); - if (fabs(result[i] - solution) > threshold) { - printf("%f %f\n", result[i], solution); - is_approx = 0; - } - } - - return is_approx; -} - -int main() { - double result[100]; - double threshold = 0.01; - double timestep = 0.01; - - solve_euler(timestep, result, 100); - printf("%d\n", check_result(result, 100, threshold, timestep)); - - return 0; -} diff --git a/contents/forward_euler_method/code/clisp/euler.lisp b/contents/forward_euler_method/code/clisp/euler.lisp deleted file mode 100644 index 04d7b6749..000000000 --- a/contents/forward_euler_method/code/clisp/euler.lisp +++ /dev/null @@ -1,29 +0,0 @@ -;;;; Forward euler implementation in Common Lisp - -(defun solve-euler (timestep n) - "Returns a function where y'(t) = -3t and y(0) = 0 using the forward euler method" - (loop - with result = (make-array n :initial-element 1) - for i from 1 upto (1- n) do - (setf (svref result i) (- (svref result (1- i)) (* 3 (svref result (1- i)) timestep))) - finally (return result))) - -(defun approximatep (result threshold timestep) - "Checks the result from the solve-euler function" - (loop - with approximatep = t - with solution = 0 - for i from 0 upto (1- (length result)) do - (setf solution (exp (* (- 3) i timestep))) - (when (> (- (svref result i) solution) threshold) - (setf approximatep nil) - (format t "~d ~d~%" (svref result i) solution)) - finally (return approximatep))) - -(defvar timestep 0.01) -(defvar n 100) ; number of steps -(defvar threshold 0.01) - -(defvar result (solve-euler timestep n)) -(defvar approximatep (approximatep result threshold timestep)) -(format t "~:[Value(s) not in threshold~;All values within threshold~]~%" approximatep) diff --git a/contents/forward_euler_method/code/cpp/euler.cpp b/contents/forward_euler_method/code/cpp/euler.cpp deleted file mode 100644 index a341655f4..000000000 --- a/contents/forward_euler_method/code/cpp/euler.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -using std::begin; -using std::end; - -using std::size_t; - -std::vector solve_euler(double timestep, size_t size) { - std::vector result; - double current = 1.0; - for (size_t i = 0; i < size; ++i) { - result.push_back(current); - current -= 3.0 * current * timestep; - } - return result; -} - -// check_result takes an iterator over doubles, -// and returns whether any value is outside the passed threshold. -template -bool check_result(Iter first, Iter last, double threshold, double timestep) { - auto it = first; - for (size_t idx = 0; it != last; ++idx, ++it) { - double solution = std::exp(-3.0 * idx * timestep); - if (std::abs(*it - solution) > threshold) { - std::cout << "We found a value outside the threshold; the " << idx - << "-th value was " << *it << ", but the expected solution was " - << solution << '\n'; - std::cout << "(the threshold was " << threshold - << " and the difference was " << std::abs(*it - solution) - << ")\n"; - return true; - } - } - return false; -} - -int main() { - double threshold = 0.01; - double timestep = 0.01; - - auto result = solve_euler(timestep, 100); - auto outside_threshold = - check_result(begin(result), end(result), threshold, timestep); - auto msg = outside_threshold ? "yes :(" : "no :D"; - - std::cout << "Were any of the values outside of the threshold (" << threshold - << ")? " << msg << '\n'; -} diff --git a/contents/forward_euler_method/code/elm/elm.json b/contents/forward_euler_method/code/elm/elm.json deleted file mode 100644 index 5eac761dd..000000000 --- a/contents/forward_euler_method/code/elm/elm.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "type": "application", - "source-directories": [ - "src" - ], - "elm-version": "0.19.1", - "dependencies": { - "direct": { - "bemyak/elm-slider": "1.0.0", - "elm/browser": "1.0.2", - "elm/core": "1.0.5", - "elm/html": "1.0.0", - "elm/json": "1.1.3", - "elm/svg": "1.0.1", - "elm/time": "1.0.0", - "rtfeldman/elm-hex": "1.0.0" - }, - "indirect": { - "debois/elm-dom": "1.3.0", - "elm/url": "1.0.0", - "elm/virtual-dom": "1.0.2" - } - }, - "test-dependencies": { - "direct": {}, - "indirect": {} - } -} diff --git a/contents/forward_euler_method/code/elm/src/Euler.elm b/contents/forward_euler_method/code/elm/src/Euler.elm deleted file mode 100644 index c9207d2de..000000000 --- a/contents/forward_euler_method/code/elm/src/Euler.elm +++ /dev/null @@ -1,397 +0,0 @@ -module Euler exposing (..) - -import Browser -import Browser.Dom exposing (Viewport) -import Browser.Events as Events -import Hex -import Html exposing (Html, button, div, h3, text) -import Html.Attributes exposing (style) -import Html.Events exposing (on, onClick) -import Json.Decode as Decode exposing (Decoder) -import Maybe -import SingleSlider as Slider -import Svg exposing (circle, line, polyline, svg) -import Svg.Attributes exposing (cx, cy, fill, height, points, r, stroke, width, x1, x2, y1, y2) -import Task -import Time exposing (Posix) - - -main : Platform.Program () Model Msg -main = - Browser.element - { init = \() -> init - , view = view - , update = update - , subscriptions = subscriptions - } - - - --- MODEL - - -type alias Model = - { part : Particle - , dt : Time - , dt0 : Time - , t : Time - , status : Status - , wWidth : Float - , wHeight : Float - , history : List ( Time, Time, Particle ) - , drag : Maybe Drag - , slider : Slider.Model - } - - -x0 : Position -x0 = - 2.5 - - -init : ( Model, Cmd Msg ) -init = - ( { part = Particle [ x0 ] [ 0 ] - , dt = 0.25 - , dt0 = 0.25 - , t = 0 - , status = Idle - , wWidth = 0 - , wHeight = 0 - , history = [] - , drag = Nothing - , slider = - { min = 0 - , max = 1 - , step = 0.01 - , value = 0.25 - , minFormatter = \_ -> "" - , maxFormatter = \_ -> "" - , currentValueFormatter = \_ _ -> "" - , disabled = False - } - } - , Task.perform GetViewPort Browser.Dom.getViewport - ) - - -type alias Time = - Float - - -type alias Position = - Float - - -type alias Velocity = - Float - - -type alias Particle = - { pos : List Position, vel : List Velocity } - - -type Status - = Idle - | Running - - -type alias Drag = - { start : Float - , current : Float - } - - -getX : Particle -> Position -getX p = - Maybe.withDefault 0 <| List.head <| .pos p - - -getV : Particle -> Velocity -getV p = - Maybe.withDefault 0 <| List.head <| .vel p - - -getX0 : Model -> Position -getX0 m = - let - scale x = - 3 - 6 * x / m.wHeight - in - case m.drag of - Nothing -> - getX m.part - - Just { start, current } -> - getX m.part + scale current - scale start - - -resetParticle : Particle -> Particle -resetParticle { pos, vel } = - case ( List.reverse pos, List.reverse vel ) of - ( x :: _, v :: _ ) -> - Particle [ x ] [ v ] - - _ -> - Particle [ x0 ] [ 0 ] - - - --- UPDATE - - -type Msg - = Start - | Stop - | Tick Posix - | GetViewPort Viewport - | SliderUpdate Float - | SliderMsg Slider.Msg - | DragStart Float - | DragAt Float - | DragEnd Float - - -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = - case msg of - Start -> - ( { model - | status = Running - , t = 0 - , dt = model.dt0 - , drag = Nothing - , part = resetParticle model.part - } - , Cmd.none - ) - - Stop -> - ( { model - | status = Idle - , part = resetParticle model.part - , t = 0 - } - , Cmd.none - ) - - Tick _ -> - case model.status of - Idle -> - ( model, Cmd.none ) - - Running -> - if model.t > 5 + model.dt then - ( { model - | status = Idle - , part = Particle [ x0 ] [ 0 ] - , history = ( model.dt, model.t, model.part ) :: model.history - , t = 0 - } - , Cmd.none - ) - - else - ( { model - | part = evolve model.part model.t model.dt - , t = model.t + model.dt - } - , Task.perform GetViewPort Browser.Dom.getViewport - ) - - GetViewPort { viewport } -> - ( { model | wWidth = viewport.width, wHeight = viewport.height * 8 / 10 }, Cmd.none ) - - SliderUpdate dt -> - ( { model | dt0 = dt }, Cmd.none ) - - SliderMsg sliderMsg -> - let - ( newSlider, cmd, updateResults ) = - Slider.update sliderMsg model.slider - - newModel = - { model | slider = newSlider, dt0 = newSlider.value } - - newCmd = - if updateResults then - Cmd.batch [ Cmd.map SliderMsg cmd, Cmd.none ] - - else - Cmd.none - in - ( newModel, newCmd ) - - DragStart y -> - case model.status of - Idle -> - ( { model | drag = Just (Drag y y) }, Cmd.none ) - - Running -> - ( model, Cmd.none ) - - DragAt y -> - ( { model | drag = Maybe.map (\{ start } -> Drag start y) model.drag } - , Cmd.none - ) - - DragEnd _ -> - ( { model - | drag = Nothing - , part = Particle [ getX0 model ] [ k * getX0 model ] - } - , Cmd.none - ) - - -k : Float -k = - -2 - - -diffEq : Position -> Velocity -> Time -> Time -> ( Position, Velocity ) -diffEq x _ _ dt = - ( x + (k * x) * dt, k * (x + (k * x) * dt) ) - - -evolve : Particle -> Time -> Time -> Particle -evolve p t dt = - let - ( x, v ) = - diffEq (getX p) (getV p) t dt - in - { p | pos = x :: p.pos, vel = v :: p.vel } - - - --- SUBSCRIPTIONS - - -subscriptions : Model -> Sub Msg -subscriptions model = - (Slider.subscriptions model.slider |> Sub.map SliderMsg) - :: (case model.drag of - Nothing -> - [ Time.every (model.dt * 1000) Tick ] - - Just _ -> - [ Events.onMouseMove (Decode.map DragAt decodeMouseHeight) - , Events.onMouseUp (Decode.map DragEnd decodeMouseHeight) - ] - ) - |> Sub.batch - - -decodeMouseHeight : Decoder Float -decodeMouseHeight = - Decode.field "pageY" Decode.float - - - --- VIEW - - -view : Model -> Html Msg -view model = - div [] - [ h3 [] [ text "Drag the ball up or down, pick a dt and click Start" ] - , h3 [ style "color" (gradient model.dt0) ] - [ viewSlider model.slider - , button [ onClick Start ] [ text "Start" ] - , button [ onClick Stop ] [ text "Stop" ] - , text ("dt = " ++ String.fromFloat model.dt0) - ] - , svg - [ width (String.fromFloat model.wWidth) - , height (String.fromFloat model.wHeight) - , stroke "black" - ] - ([ line - [ x1 "0" - , x2 (String.fromFloat model.wWidth) - , y1 (String.fromFloat (model.wHeight / 2)) - , y2 (String.fromFloat (model.wHeight / 2)) - ] - [] - , line - [ x1 (String.fromFloat (model.wWidth / 20)) - , x2 (String.fromFloat (model.wWidth / 20)) - , y1 "0" - , y2 (String.fromFloat model.wHeight) - ] - [] - , viewCircle model - ] - ++ plotHistory model - ) - ] - - -viewSlider : Slider.Model -> Html Msg -viewSlider slider = - Slider.view slider |> Html.map SliderMsg - - -scaleX : Float -> Position -> String -scaleX h x = - String.fromFloat (h / 2 * (1 - x / 3)) - - -scaleT : Float -> Time -> String -scaleT w t = - String.fromFloat (w * (0.05 + t / 5)) - - -viewCircle : Model -> Html Msg -viewCircle m = - circle - [ cy (scaleX m.wHeight (getX0 m)) - , cx (scaleT m.wWidth m.t) - , r "10" - , on "mousedown" (Decode.map DragStart decodeMouseHeight) - ] - [] - - -plotPath : Float -> Float -> ( Time, Time, Particle ) -> String -plotPath w h ( dt, tf, particle ) = - let - comb x ( t, s ) = - ( t - dt, s ++ scaleT w t ++ "," ++ scaleX h x ++ " " ) - in - Tuple.second <| List.foldl comb ( tf, "" ) particle.pos - - -plotHistory : Model -> List (Html Msg) -plotHistory m = - let - ( w, h ) = - ( m.wWidth, m.wHeight ) - in - List.map - (\( dt, t, p ) -> - polyline - [ stroke "black" - , fill "none" - , stroke (gradient dt) - , points (plotPath w h ( dt, t, p )) - ] - [] - ) - (( m.dt, m.t, m.part ) :: m.history) - - -gradient : Time -> String -gradient dt = - let - ( r, g, b ) = - ( round (255 * dt), 0, round (255 * (1 - dt)) ) - - col = - Hex.toString (256 * (256 * r + g) + b) - in - if String.length col < 6 then - "#" ++ String.repeat (6 - String.length col) "0" ++ col - - else - "#" ++ col diff --git a/contents/forward_euler_method/code/fortran/euler.f90 b/contents/forward_euler_method/code/fortran/euler.f90 deleted file mode 100644 index 5a7c8332e..000000000 --- a/contents/forward_euler_method/code/fortran/euler.f90 +++ /dev/null @@ -1,64 +0,0 @@ -PROGRAM euler - - IMPLICIT NONE - LOGICAL :: is_approx - REAL(8), DIMENSION(:), ALLOCATABLE :: vec - REAL(8) :: time_step, threshold - INTEGER :: n - - time_step = 0.01d0 - n = 100 - threshold = 0.01d0 - - ALLOCATE(vec(n)) - CALL forward_euler(time_step, n, vec) - is_approx = check_result(vec, threshold, time_step) - - WRITE(*,*) is_approx - - DEALLOCATE(vec) - -CONTAINS - - SUBROUTINE forward_euler(time_step, n, vec) - - IMPLICIT NONE - REAL(8), DIMENSION(:), INTENT(OUT) :: vec - REAL(8), INTENT(IN) :: time_step - INTEGER, INTENT(IN) :: n - INTEGER :: i - - vec(1) = 1d0 - - DO i=1, n-1 - - vec(i+1) = vec(i) - 3d0 * vec(i) * time_step - - END DO - END SUBROUTINE - - LOGICAL FUNCTION check_result(euler_result, threshold, time_step) - - IMPLICIT NONE - REAL(8), DIMENSION(:), INTENT(IN) :: euler_result - REAL(8), INTENT(IN) :: threshold, time_step - REAL(8) :: time, solution - INTEGER :: i - - check_result = .TRUE. - - DO i = 1, SIZE(euler_result) - - time = (i - 1) * time_step - solution = EXP(-3d0 * time) - - IF (ABS(euler_result(i) - solution) > threshold) THEN - - WRITE(*,*) euler_result(i), solution - check_result = .FALSE. - - END IF - END DO - END FUNCTION -END PROGRAM euler - diff --git a/contents/forward_euler_method/code/go/euler.go b/contents/forward_euler_method/code/go/euler.go deleted file mode 100644 index 0f8ba1618..000000000 --- a/contents/forward_euler_method/code/go/euler.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "fmt" - "math" -) - -func forwardEuler(timeStep float64, n int) []float64 { - result := make([]float64, n) - result[0] = 1 - for x := 1; x < n; x++ { - result[x] = result[x-1] - 3*result[x-1]*timeStep - } - return result -} - -func check(result []float64, threshold, timeStep float64) bool { - approx := true - for x := 0.; int(x) < len(result); x++ { - solution := math.Exp(-3. * x * timeStep) - if math.Abs(result[int(x)]-solution) > threshold { - fmt.Println(result[int(x)], solution) - approx = false - } - } - return approx -} - -func main() { - timeStep, threshold := .01, .01 - n := 100 - - result := forwardEuler(timeStep, n) - if check(result, threshold, timeStep) { - fmt.Println("All values within threshold") - } else { - fmt.Println("Value(s) not within threshold") - } -} diff --git a/contents/forward_euler_method/code/haskell/euler.hs b/contents/forward_euler_method/code/haskell/euler.hs deleted file mode 100644 index 6ca9bf479..000000000 --- a/contents/forward_euler_method/code/haskell/euler.hs +++ /dev/null @@ -1,20 +0,0 @@ -solveEuler :: Num a => (a -> a) -> a -> a -> [a] -solveEuler f ts = iterate (\x -> x + f x * ts) - -checkResult :: (Ord a, Num a, Num t, Enum t) => a -> (t -> a) -> [a] -> Bool -checkResult thresh check = - and . zipWith (\i k -> abs (check i - k) < thresh) [0..] - -kinematics :: Double -> Double -kinematics x = -3 * x - -main :: IO () -main = - let timestep = 0.01 - n = 100 - threshold = 0.01 - checkResult' = checkResult threshold $ exp . (\x -> -3 * x * timestep) - in putStrLn $ - if checkResult' (take n $ solveEuler kinematics timestep 1) - then "All values within threshold" - else "Value(s) not in threshold" diff --git a/contents/forward_euler_method/code/java/ForwardEuler.java b/contents/forward_euler_method/code/java/ForwardEuler.java deleted file mode 100644 index 16d398a5f..000000000 --- a/contents/forward_euler_method/code/java/ForwardEuler.java +++ /dev/null @@ -1,38 +0,0 @@ -public class ForwardEuler { - private static double[] solveEuler(double timestep, int n) { - double[] eulerResult = new double[n]; - - //Setting the initial condition - eulerResult[0] = 1; - for(int i = 1; i < eulerResult.length; i++) { - eulerResult[i] = eulerResult[i - 1] - (3 * eulerResult[i - 1] * timestep); - } - return eulerResult; - } - - private static boolean checkResult(double[] eulerResult, double threshold, double timestep) { - boolean isApprox = true; - - for(int i = 0; i < eulerResult.length; i++) { - double time = i * timestep; - double solution = Math.exp(-3 * time); - if(Math.abs(eulerResult[i] - solution) > threshold) { - System.out.println(eulerResult[i] + " " + solution); - isApprox = false; - } - } - - return isApprox; - } - - public static void main(String[] args) { - double timestep = 0.1; - int n = 100; - double threshold = 0.1; - - double[] eulerResult = solveEuler(timestep, n); - boolean isApprox = checkResult(eulerResult, threshold, timestep); - - System.out.println(isApprox); - } -} diff --git a/contents/forward_euler_method/code/javascript/euler.js b/contents/forward_euler_method/code/javascript/euler.js deleted file mode 100644 index c9cbcccb3..000000000 --- a/contents/forward_euler_method/code/javascript/euler.js +++ /dev/null @@ -1,31 +0,0 @@ -function forwardEuler(timeStep, n) { - const arr = [1]; - for (let i = 1; i <= n; i++) { - arr[i] = arr[i - 1] - 3 * arr[i - 1] * timeStep; - } - return arr; -} - -function checkEuler(arr, timeStep, threshold) { - let isApprox = true; - arr.forEach((_value, i) => { - const solution = Math.exp(-3 * timeStep * i); - - if (Math.abs(arr[i] - solution) > threshold) { - console.log(arr[i], solution); - isApprox = false; - } - }); - return isApprox; -} - -function main() { - const timeStep = 0.01; - const threshold = 0.01; - const n = 100; - const eulerResult = forwardEuler(timeStep, n); - const checkResult = checkEuler(eulerResult, timeStep, threshold); - console.log(checkResult); -} - -main(); diff --git a/contents/forward_euler_method/code/julia/euler.jl b/contents/forward_euler_method/code/julia/euler.jl deleted file mode 100644 index 49ddcacaf..000000000 --- a/contents/forward_euler_method/code/julia/euler.jl +++ /dev/null @@ -1,39 +0,0 @@ -function solve_euler(timestep::Float64, n::Int64) - euler_result = Vector{Float64}(undef, n) - - # Setting the initial condition - euler_result[1] = 1; - for i = 2:length(euler_result) - euler_result[i] = euler_result[i-1] - 3.0*euler_result[i-1]*timestep - end - return euler_result -end - -function check_result(euler_result::Vector{Float64}, threshold::Float64, - timestep::Float64) - is_approx = true - - for i = 1:length(euler_result) - time = (i - 1)*timestep - solution = exp(-3*time); - if (abs(euler_result[i] - solution) > threshold) - println(euler_result[i], solution) - is_approx = false - end - end - - return is_approx -end - -function main() - timestep = 0.01 - n = 100 - threshold = 0.01 - - euler_result = solve_euler(timestep,n) - is_approx = check_result(euler_result, threshold, timestep) - - println(is_approx) -end - -main() diff --git a/contents/forward_euler_method/code/matlab/euler.m b/contents/forward_euler_method/code/matlab/euler.m deleted file mode 100644 index fb6bf210f..000000000 --- a/contents/forward_euler_method/code/matlab/euler.m +++ /dev/null @@ -1,46 +0,0 @@ -clc;clear;close all - -%========================================================================== -% Define Function -f =@(x) -3*x; - -% Define Initial and Final time -tInit=0; -tLast=5; - -% Define number of points -N=1e2; - -% Set Initial Conditions -yInit=1; -%========================================================================== - -dt=(tLast-tInit)/(N-1); % Calculate dt -t=[tInit:dt:tLast]; % Preallocate time array -y=zeros(1,length(t)); % Preallocate solution array -y(1)=yInit; % Impose Initial Conditions - -% Loop over time -for i=1:length(t)-1 - - t(i+1) = t(i) + dt; % Calculate next time - y(i+1) = y(i) + f( y(i) )*dt; % Update solution - -end - -% Plot numerical solution -plot(t,y) - -% Create analytical solution -g=@(x) exp(-3*x); -z=g(t); - -% Plot analytical solution on the same graph -hold on -plot(t,z,'--') - -% Set axis, title and legend -xlabel('t');ylabel('y(t)'); -title('Analytical VS Numerical Solution') -grid -legend('Numerical','Analytical') diff --git a/contents/forward_euler_method/code/nim/forwardeuler.nim b/contents/forward_euler_method/code/nim/forwardeuler.nim deleted file mode 100644 index 6199a9b89..000000000 --- a/contents/forward_euler_method/code/nim/forwardeuler.nim +++ /dev/null @@ -1,32 +0,0 @@ -import math - -proc solveEuler(timestep: float, n: int): seq[float] = - var res = newSeq[float](n) - - res[0] = 1.0 - - for i in 1 .. n - 1: - res[i] = res[i - 1] - 3 * res[i - 1] * timestep - - return res - -proc check(res: seq[float], timestep, threshold: float): bool = - var approx: bool = true; - - for i in 0 .. len(res) - 1: - let solution: float = exp(-3.0 * float(i) * timestep) - if abs(res[i]) - solution > threshold: - echo res[i] - echo solution - approx = false - - return approx - -const - timestep: float = 0.1 - n: int = 100 - threshold: float = 0.1 - eulerResult: seq[float] = solveEuler(timestep, n) - approx: bool = check(eulerResult, threshold, timestep) - -echo approx diff --git a/contents/forward_euler_method/code/python/euler.py b/contents/forward_euler_method/code/python/euler.py deleted file mode 100644 index 993be5b95..000000000 --- a/contents/forward_euler_method/code/python/euler.py +++ /dev/null @@ -1,31 +0,0 @@ -import math - - -def forward_euler(time_step, n): - result = [0] * n - result[0] = 1 - for i in range(1, n): - result[i] = result[i - 1] - 3 * result[i - 1] * time_step - return result - - -def check(result, threshold, time_step): - approx = True - for i in range(len(result)): - solution = math.exp(-3 * i * time_step) - if abs(result[i] - solution) > threshold: - print(result[i], solution) - approx = False - return approx - - -def main(): - time_step = 0.01 - n = 100 - threshold = 0.01 - - result = forward_euler(time_step, n) - approx = check(result, threshold, time_step) - print("All values within threshold") if approx else print("Value(s) not in threshold") - -main() diff --git a/contents/forward_euler_method/code/rust/euler.rs b/contents/forward_euler_method/code/rust/euler.rs deleted file mode 100644 index cbfc44138..000000000 --- a/contents/forward_euler_method/code/rust/euler.rs +++ /dev/null @@ -1,31 +0,0 @@ -fn main() { - let mut result = [0.0; 100]; - let threshold = 0.01; - let timestep = 0.01; - - solve_euler(timestep, &mut result); - println!("{}", check_result(&result, threshold, timestep)); -} - -fn solve_euler(timestep: f64, result: &mut [f64]) { - let n = result.len(); - if n != 0 { - result[0] = 1.0; - for i in 1..n { - result[i] = result[i - 1] - 3.0 * result[i - 1] * timestep; - } - } -} - -fn check_result(result: &[f64], threshold: f64, timestep: f64) -> bool { - let mut is_approx: bool = true; - for (i, val) in result.iter().enumerate() { - let solution = (-3.0 * i as f64 * timestep).exp(); - if (val - solution).abs() > threshold { - println!("{} {}", val, solution); - is_approx = false; - } - } - - return is_approx; -} diff --git a/contents/forward_euler_method/code/swift/euler.swift b/contents/forward_euler_method/code/swift/euler.swift deleted file mode 100644 index 033b6d3fb..000000000 --- a/contents/forward_euler_method/code/swift/euler.swift +++ /dev/null @@ -1,42 +0,0 @@ -import Foundation - -func solveEuler(timeStep: Double, n: Int) -> [Double] { - var result : [Double] = [1] - - for i in 1...n { - result.append(result[i - 1] - 3 * result[i - 1] * timeStep) - } - - return result -} - -func checkResult(result: [Double], threshold: Double, timeStep: Double) -> Bool { - var isApprox = true - - for i in 0.. threshold { - print(result[i], solution) - isApprox = false - } - } - - return isApprox -} - -func main() { - let timeStep = 0.01 - let n = 100 - let threshold = 0.01 - - let result = solveEuler(timeStep: timeStep, n: n) - let isApprox = checkResult(result: result, threshold: threshold, timeStep: timeStep) - - if isApprox { - print("All values within threshold") - } else { - print("Value(s) not in threshold") - } -} - -main() diff --git a/contents/forward_euler_method/code/v/euler.v b/contents/forward_euler_method/code/v/euler.v deleted file mode 100644 index e590bbdcf..000000000 --- a/contents/forward_euler_method/code/v/euler.v +++ /dev/null @@ -1,37 +0,0 @@ -import math - -fn forward_euler(timestep f64, n int) []f64 { - mut res := [f64(0.0)].repeat(n) - res[0] = f64(1) - for x := 1; x < n; x++ { - res[x] = res[x-1] - 3.0*res[x-1]*timestep - } - return res -} - -fn check(result []f64, threshold, timestep f64) bool { - mut approx := true - for x := 0; x < result.len; x++ { - solution := math.exp(-3.0 * f64(x) * timestep) - if math.abs(result[x]-solution) > threshold { - tmp := result[x] - println("There is a mismatch: abs($tmp-$solution) > $threshold!") - approx = false - } - } - return approx -} - -fn main() { - timestep := .01 - threshold := .01 - n := 100 - - result := forward_euler(timestep, n) - - if check(result, threshold, timestep) { - println("All values within threshold") - } else { - println("Value(s) not within threshold") - } -} \ No newline at end of file diff --git a/contents/forward_euler_method/forward_euler_method.md b/contents/forward_euler_method/forward_euler_method.md deleted file mode 100644 index f97ff27a2..000000000 --- a/contents/forward_euler_method/forward_euler_method.md +++ /dev/null @@ -1,174 +0,0 @@ -# The Forward Euler Method - -The Euler methods are some of the simplest methods to solve ordinary differential equations numerically. -They introduce a new set of methods called the Runge Kutta methods, which will be discussed in the near future! - -As a physicist, I tend to understand things through methods that I have learned before. -In this case, it makes sense for me to see Euler methods as extensions of the [Taylor Series Expansion](../taylor_series_expansion/taylor_series_expansion.md). -These expansions basically approximate functions based on their derivatives, like so: - -$$ -f(x) \approx f(a) + \frac{1}{1!}\frac{df(a)}{da}(x-a) - + \frac{1}{2!}\frac{d^2f(a)}{da^2}(x-a)^2 - + \frac{1}{3!}\frac{d^3f(a)}{da^3}(x-a)^3 + \cdots -$$ - -Like before, $$f(x)$$ is some function along real or complex space, $$a$$ is the point that we are expanding from, and $$f^{(n)}(x)$$ denotes the $$n^{\text{th}}$$ derivative of $$f(x)$$. - -So, what does this mean? Well, as mentioned, we can think of this similarly to the kinematic equation: -$$ -x = x_0 + vt + \frac{1}{2}at^2 -$$ -where $$x$$ is position, $$v$$ is velocity, and $$a$$ is acceleration. -This equation allows us to find the position of an object based on it's previous position ($$x_0$$), the derivative of it's position with respect to time ($$\frac{dx}{dt} = v_0$$) and one derivative on top of that ($$\frac{d^2x}{dx^2} = a$$). -As stated in the Tayor Series Expansion, the acceleration term must also have $$\frac{1}{2!}$$ in front of it. - -Now, how does this relate to the Euler methods? -Well, with these methods, we assume that we are looking for a position in some space, usually denoted as $$y(t)$$, but we can use any variable. -The methods assume that we have some function to evaluate the derivative of $$y(t)$$. In other words, we know that $$\frac{dy(t)}{dt} = f(t,y(t))$$. -For the kinematic equation, we know what this is! - -$$ -\frac{dy(t)}{dt} = v = f(t,y(t)) = v_0 + a(t) -$$ - -So, we can iteratively solve for position by first solving for velocity. By following the kinematic equation (or Taylor Series Expansion), we find that - -$$ -y_{n+1} = y_n + f(t_n, y_n) dt -$$ - -For any timestep $$dt$$. This means that if we are solving the kinematic equation, we simply have the following equations: - -$$ -\begin{align} - x_{n+1} &= x_n + v dt \\ - v_{n+1} &= a (t_n) -\end{align} -$$ - -Now, solving this set of equations in this way is known as the *forward* Euler Method. -In fact, there is another method known as the *backward* Euler Method, which we will get to soon enough. -For now, it is important to note that the error of these methods depend on the timestep chosen. - -

- -

- -For example, here we see dramatically different results for different timesteps for solving the ODE $$y' = \frac{x^3}{6}$$, whose solution is $$y = \frac{x^2}{2}$$. -The blue line is the analytical solution, the green is with a timestep of 0.5 and the red is with a timestep of 1. -To be clear: the larger the timestep, the worse the error becomes; however, there is at least one more problem with using the forward Euler method on real problems: instabilities. - -As we mentioned, the forward Euler method approximates the solution to an Ordinary Differential Equation (ODE) by using only the first derivative. -This is (rather expectedly) a poor approximation. -In fact, the approximation is so poor that the error associated with running this algorithm can add up and result in incredibly incorrect results. -As you might imagine, the only solution to this is decreasing the timestep and hoping for the best or using a similar method with different stability regions, like the backward Euler method. - -Let's assume we are solving a simple ODE: $$y' = -3y, y(0) = 1$$. -The solution here is $$y(t) = e^{-3t}$$ and we can find this solution somewhat easily with the forward Euler method shown below. -That said, by choosing a larger timestep, we see the Euler method's solution oscillate above and below 0, which should *never* happen. -If we were to take the Euler method's solution as valid, we would incorrectly assume that $$e^{-3t}$$ will become negative! - -

- -

- -Like above, the blue line is the analytical solution, the green is with a timestep of 0.5 and the red is with a timestep of 1. -Here, it's interesting that we see 2 different instability patterns. -The green is initially unstable, but converges onto the correct solution, but the red is wrong from the get-go and only gets more wrong as time goes on. - -In truth, the stability region of the forward Euler method for the case where $$y' = ky$$ can be found with the following inequality: -$$ -|kdt + 1 | \leq 1 -$$ -Which means that the forward Euler method is actually unstable for most values! -If we want to stick to using the forward Euler method exclusively, the only solution is to decrease the timestep until it is within this stability region, and that's not necessarily easy for all cases. -So now it might be obvious that another, more stable method should be used instead; however, many other stable methods are *implicit*, which means that in order to find the solution, we need to solve a system of equations via the [Thomas Algorithm](../thomas_algorithm/thomas_algorithm.md) or [Gaussian Elimination](../gaussian_elimination/gaussian_elimination.md). -Which is an entire layer of complexity that most people don't want to mess with! - -Now, here is where we might want to relate the method to another algorithm that is sometimes used for a similar use-case: [Verlet Integration](../verlet_integration/verlet_integration.md). -Verlet integration has a distinct advantage over the forward Euler method in both error and stability with more coarse-grained timesteps; however, Euler methods are powerful in that they may be used for cases other than simple kinematics. -That said, in practice, due to the instability of the forward Euler method and the error with larger timesteps, this method is rarely used in practice. -That said, variations of this method *are* certainly used (for example Crank-Nicolson and [Runge-Kutta](../runge_kutta_methods/runge_kutta_methods.md), so the time spent reading this chapter is not a total waste! - -## Video Explanation - -Here is a video describing the forward Euler method: - -
- -
- -## Example Code - -Like in the case of [Verlet Integration](../verlet_integration/verlet_integration.md), the easiest way to test to see if this method works is to test it against a simple test-case. -Here, the most obvious test-case would be dropping a ball from 5 meters, which is my favorite example, but proved itself to be slightly less enlightening than I would have thought. -So, this time, let's remove ourselves from any physics and instead solve the following ODE: $$y(t)' = -3t$$ with the initial condition that $$y(0) = 1$$. -Note that in this case, the velocity is directly given by the ODE and the acceleration is not part of the model. - -{% method %} -{% sample lang="jl" %} -[import, lang:"julia"](code/julia/euler.jl) -{% sample lang="c" %} -[import, lang:"c"](code/c/euler.c) -{% sample lang="cpp" %} -[import, lang:"cpp"](code/cpp/euler.cpp) -{% sample lang="rs" %} -[import, lang:"rust"](code/rust/euler.rs) -{% sample lang="elm" %} -[import:78-91, lang:"elm"](code/elm/src/Euler.elm) -[import:236-252, lang:"elm"](code/elm/src/Euler.elm) - -Full code for the visualization follows: -[import, lang:"elm"](code/elm/src/Euler.elm) - -{% sample lang="py" %} -[import, lang:"python"](code/python/euler.py) -{% sample lang="hs" %} -[import, lang:"haskell"](code/haskell/euler.hs) -{% sample lang="m" %} -[import, lang:"matlab"](code/matlab/euler.m) -{% sample lang="swift" %} -[import, lang:"swift"](code/swift/euler.swift) -{% sample lang="js" %} -[import, lang:"javascript"](code/javascript/euler.js) -{% sample lang="f90" %} -[import, lang:"fortran"](code/fortran/euler.f90) -{% sample lang="go" %} -[import, lang:"go"](code/go/euler.go) -{% sample lang="v" %} -[import, lang:"v"](code/v/euler.v) -{% sample lang="asm-x64" %} -[import, lang:"asm-x64"](code/asm-x64/euler.s) -{% sample lang="java" %} -[import, lang:"java"](code/java/ForwardEuler.java) -{% sample lang="nim" %} -[import, lang:"nim"](code/nim/forwardeuler.nim) -{% sample lang="lisp" %} -[import, lang="lisp"](code/clisp/euler.lisp) -{% endmethod %} - - - -## License - -##### Code Examples - -The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/master/LICENSE.md)). - -##### Text - -The text of this chapter was written by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). - -[

](https://creativecommons.org/licenses/by-sa/4.0/) - -##### Images/Graphics -- The image "[FEerror](res/error.png)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). -- The image "[FEinstability](res/instability.png)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). - -##### Pull Requests - -After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter: -- none diff --git a/contents/forward_euler_method/res/error.png b/contents/forward_euler_method/res/error.png deleted file mode 100644 index 4d5e4feb9879d1089e7a57894d479833fbf83cd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8736 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 Y{l&-Y3MEy*kq?lzhQXr>HT#JF13qopyZ`_I diff --git a/contents/forward_euler_method/res/instability.png b/contents/forward_euler_method/res/instability.png deleted file mode 100644 index e1c2b8c476b0e77621896ae99d60aba4d90050cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10680 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 From 8e76e1231c09badbc690deb1e8c650244c385428 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Sun, 7 Nov 2021 20:16:11 +0100 Subject: [PATCH 2/3] Revert "removing Euler and adjacent chapters" This reverts commit 4293e9d4e66e02b77ac1d18b0212705a5cb7418c. --- SUMMARY.md | 2 + .../differential_equations.md | 21 + .../forward_euler_method/code/asm-x64/euler.s | 97 +++++ .../forward_euler_method/code/c/SConscript | 6 + contents/forward_euler_method/code/c/euler.c | 35 ++ .../code/clisp/euler.lisp | 29 ++ .../forward_euler_method/code/cpp/euler.cpp | 55 +++ .../forward_euler_method/code/elm/elm.json | 28 ++ .../code/elm/src/Euler.elm | 397 ++++++++++++++++++ .../code/fortran/euler.f90 | 64 +++ .../forward_euler_method/code/go/euler.go | 39 ++ .../code/haskell/euler.hs | 20 + .../code/java/ForwardEuler.java | 38 ++ .../code/javascript/euler.js | 31 ++ .../forward_euler_method/code/julia/euler.jl | 39 ++ .../forward_euler_method/code/matlab/euler.m | 46 ++ .../code/nim/forwardeuler.nim | 32 ++ .../forward_euler_method/code/python/euler.py | 31 ++ .../forward_euler_method/code/rust/euler.rs | 31 ++ .../code/swift/euler.swift | 42 ++ contents/forward_euler_method/code/v/euler.v | 37 ++ .../forward_euler_method.md | 174 ++++++++ contents/forward_euler_method/res/error.png | Bin 0 -> 8736 bytes .../forward_euler_method/res/instability.png | Bin 0 -> 10680 bytes 24 files changed, 1294 insertions(+) create mode 100644 contents/differential_equations/differential_equations.md create mode 100644 contents/forward_euler_method/code/asm-x64/euler.s create mode 100644 contents/forward_euler_method/code/c/SConscript create mode 100644 contents/forward_euler_method/code/c/euler.c create mode 100644 contents/forward_euler_method/code/clisp/euler.lisp create mode 100644 contents/forward_euler_method/code/cpp/euler.cpp create mode 100644 contents/forward_euler_method/code/elm/elm.json create mode 100644 contents/forward_euler_method/code/elm/src/Euler.elm create mode 100644 contents/forward_euler_method/code/fortran/euler.f90 create mode 100644 contents/forward_euler_method/code/go/euler.go create mode 100644 contents/forward_euler_method/code/haskell/euler.hs create mode 100644 contents/forward_euler_method/code/java/ForwardEuler.java create mode 100644 contents/forward_euler_method/code/javascript/euler.js create mode 100644 contents/forward_euler_method/code/julia/euler.jl create mode 100644 contents/forward_euler_method/code/matlab/euler.m create mode 100644 contents/forward_euler_method/code/nim/forwardeuler.nim create mode 100644 contents/forward_euler_method/code/python/euler.py create mode 100644 contents/forward_euler_method/code/rust/euler.rs create mode 100644 contents/forward_euler_method/code/swift/euler.swift create mode 100644 contents/forward_euler_method/code/v/euler.v create mode 100644 contents/forward_euler_method/forward_euler_method.md create mode 100644 contents/forward_euler_method/res/error.png create mode 100644 contents/forward_euler_method/res/instability.png diff --git a/SUMMARY.md b/SUMMARY.md index e41ba39d4..d76af219d 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -32,6 +32,8 @@ * [FFT](contents/cooley_tukey/cooley_tukey.md) * [Decision Problems](contents/decision_problems/decision_problems.md) * [Stable Marriage Problem](contents/stable_marriage_problem/stable_marriage_problem.md) +* [Differential Equation Solvers](contents/differential_equations/differential_equations.md) + * [Forward Euler Method](contents/forward_euler_method/forward_euler_method.md) * [Physics Solvers](contents/physics_solvers/physics_solvers.md) * [Verlet Integration](contents/verlet_integration/verlet_integration.md) * [Quantum Systems](contents/quantum_systems/quantum_systems.md) diff --git a/contents/differential_equations/differential_equations.md b/contents/differential_equations/differential_equations.md new file mode 100644 index 000000000..3cfbba1fa --- /dev/null +++ b/contents/differential_equations/differential_equations.md @@ -0,0 +1,21 @@ +# Differential Equations + +Differential equations lie at the heart of many different systems in physics, economics, biology, chemistry, and many other areas of research and engineering. +Here, we discuss many different methods to solve particular sets of differential equations. + +## License + +##### Code Examples + +The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/master/LICENSE.md)). + +##### Text + +The text of this chapter was written by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) + +##### Pull Requests + +After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter: +- none diff --git a/contents/forward_euler_method/code/asm-x64/euler.s b/contents/forward_euler_method/code/asm-x64/euler.s new file mode 100644 index 000000000..98e303b00 --- /dev/null +++ b/contents/forward_euler_method/code/asm-x64/euler.s @@ -0,0 +1,97 @@ +.intel_syntax noprefix + +.section .rodata + three: .double -3.0 + fabs_const: + .long 4294967295 + .long 2147483647 + .long 0 + .long 0 + inital_val: .double 1.0 + threshold: .double 0.01 + timestep: .double 0.01 + error_fmt: .string "%f %f\n" + fmt: .string "%d\n" + +.section .text + .global main + .extern printf + .extern exp + +# rdi - array size +# rsi - array ptr +# xmm0 - timestep +solve_euler: + movsd xmm1, inital_val + lea rax, [rsi + 8 * rdi + 8] # Set to end of the array +solve_euler_loop: + movsd xmm3, three # Set to -3.0 + mulsd xmm2, xmm1 # xmm2 = -3.0 * array[i-1] * timestep + mulsd xmm2, xmm0 + subsd xmm1, xmm2 # xmm1 = xmm1 - xmm2 + movsd QWORD PTR [rsi], xmm1 + add rsi, 8 + cmp rsi, rax # Test if we have gone through the array + jne solve_euler_loop +solve_euler_return: + ret + +# rdi - array size +# rsi - array ptr +# xmm0 - timestep +# xmm1 - threshold +# RET rax - success code 0 if sucess else 1 +check_result: + push r12 + push r13 + xor rax, rax # Return code is 0 + xor r12, r12 # The index is set to 0 + mov r13, rdi # Moving array size to free rdi for printf + movsd xmm2, xmm0 # Moving timestep to free xmm0 for exp + jmp loop_check +results_loop: + cvtsi2sd xmm0, r12 # Making int to a double + movsd xmm3, three # Calculating exp(-3.0 * i * timestep) + mulsd xmm0, xmm3 + mulsd xmm0, xmm2 + call exp + movsd xmm3, QWORD PTR [rsi + r12 * 8] # Calculating abs(array[i] - xmm0) + subsd xmm2, xmm3 + movq xmm3, fabs_const + andpd xmm0, xmm3 + comisd xmm0, xmm1 # Check if abs(...) > threshold + jbe if_false + mov rdi, OFFSET error_fmt # If true print out array[i] and solution + mov rax, 1 + call printf + mov rax, 1 # and set sucess code to failed (rax = 1) +if_false: + add r12, 1 +loop_check: + cmp r12, r13 # Check if index is less the array size + jle results_loop + pop r13 + pop r12 + ret + +main: + push rbp + sub rsp, 800 # Making double array[100] + mov rdi, 100 + mov rsi, rsp + movsd xmm0, timestep + call solve_euler # Calling solve_euler + mov rdi, 100 + mov rsi, rsp + movsd xmm0, timestep + movsd xmm1, threshold + call check_result # Check if results are correct + mov rdi, OFFSET fmt + mov rsi, rax + xor rax, rax + call printf # Print out success code + add rsp, 800 # Deallocating array + pop rbp + xor rax, rax + ret + diff --git a/contents/forward_euler_method/code/c/SConscript b/contents/forward_euler_method/code/c/SConscript new file mode 100644 index 000000000..34a951e7f --- /dev/null +++ b/contents/forward_euler_method/code/c/SConscript @@ -0,0 +1,6 @@ +Import('*') +from pathlib import Path + +dirname = Path.cwd().parents[1].stem + +env.C(f'#/build/c/{dirname}', Glob('*.c'), LIBS='m') diff --git a/contents/forward_euler_method/code/c/euler.c b/contents/forward_euler_method/code/c/euler.c new file mode 100644 index 000000000..9ce095930 --- /dev/null +++ b/contents/forward_euler_method/code/c/euler.c @@ -0,0 +1,35 @@ +#include +#include + +void solve_euler(double timestep, double *result, size_t n) { + if (n != 0) { + result[0] = 1; + for (size_t i = 1; i < n; ++i) { + result[i] = result[i-1] - 3.0 * result[i-1] * timestep; + } + } +} + +int check_result(double *result, size_t n, double threshold, double timestep) { + int is_approx = 1; + for (size_t i = 0; i < n; ++i) { + double solution = exp(-3.0 * i * timestep); + if (fabs(result[i] - solution) > threshold) { + printf("%f %f\n", result[i], solution); + is_approx = 0; + } + } + + return is_approx; +} + +int main() { + double result[100]; + double threshold = 0.01; + double timestep = 0.01; + + solve_euler(timestep, result, 100); + printf("%d\n", check_result(result, 100, threshold, timestep)); + + return 0; +} diff --git a/contents/forward_euler_method/code/clisp/euler.lisp b/contents/forward_euler_method/code/clisp/euler.lisp new file mode 100644 index 000000000..04d7b6749 --- /dev/null +++ b/contents/forward_euler_method/code/clisp/euler.lisp @@ -0,0 +1,29 @@ +;;;; Forward euler implementation in Common Lisp + +(defun solve-euler (timestep n) + "Returns a function where y'(t) = -3t and y(0) = 0 using the forward euler method" + (loop + with result = (make-array n :initial-element 1) + for i from 1 upto (1- n) do + (setf (svref result i) (- (svref result (1- i)) (* 3 (svref result (1- i)) timestep))) + finally (return result))) + +(defun approximatep (result threshold timestep) + "Checks the result from the solve-euler function" + (loop + with approximatep = t + with solution = 0 + for i from 0 upto (1- (length result)) do + (setf solution (exp (* (- 3) i timestep))) + (when (> (- (svref result i) solution) threshold) + (setf approximatep nil) + (format t "~d ~d~%" (svref result i) solution)) + finally (return approximatep))) + +(defvar timestep 0.01) +(defvar n 100) ; number of steps +(defvar threshold 0.01) + +(defvar result (solve-euler timestep n)) +(defvar approximatep (approximatep result threshold timestep)) +(format t "~:[Value(s) not in threshold~;All values within threshold~]~%" approximatep) diff --git a/contents/forward_euler_method/code/cpp/euler.cpp b/contents/forward_euler_method/code/cpp/euler.cpp new file mode 100644 index 000000000..a341655f4 --- /dev/null +++ b/contents/forward_euler_method/code/cpp/euler.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include + +using std::begin; +using std::end; + +using std::size_t; + +std::vector solve_euler(double timestep, size_t size) { + std::vector result; + double current = 1.0; + for (size_t i = 0; i < size; ++i) { + result.push_back(current); + current -= 3.0 * current * timestep; + } + return result; +} + +// check_result takes an iterator over doubles, +// and returns whether any value is outside the passed threshold. +template +bool check_result(Iter first, Iter last, double threshold, double timestep) { + auto it = first; + for (size_t idx = 0; it != last; ++idx, ++it) { + double solution = std::exp(-3.0 * idx * timestep); + if (std::abs(*it - solution) > threshold) { + std::cout << "We found a value outside the threshold; the " << idx + << "-th value was " << *it << ", but the expected solution was " + << solution << '\n'; + std::cout << "(the threshold was " << threshold + << " and the difference was " << std::abs(*it - solution) + << ")\n"; + return true; + } + } + return false; +} + +int main() { + double threshold = 0.01; + double timestep = 0.01; + + auto result = solve_euler(timestep, 100); + auto outside_threshold = + check_result(begin(result), end(result), threshold, timestep); + auto msg = outside_threshold ? "yes :(" : "no :D"; + + std::cout << "Were any of the values outside of the threshold (" << threshold + << ")? " << msg << '\n'; +} diff --git a/contents/forward_euler_method/code/elm/elm.json b/contents/forward_euler_method/code/elm/elm.json new file mode 100644 index 000000000..5eac761dd --- /dev/null +++ b/contents/forward_euler_method/code/elm/elm.json @@ -0,0 +1,28 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "bemyak/elm-slider": "1.0.0", + "elm/browser": "1.0.2", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm/json": "1.1.3", + "elm/svg": "1.0.1", + "elm/time": "1.0.0", + "rtfeldman/elm-hex": "1.0.0" + }, + "indirect": { + "debois/elm-dom": "1.3.0", + "elm/url": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/contents/forward_euler_method/code/elm/src/Euler.elm b/contents/forward_euler_method/code/elm/src/Euler.elm new file mode 100644 index 000000000..c9207d2de --- /dev/null +++ b/contents/forward_euler_method/code/elm/src/Euler.elm @@ -0,0 +1,397 @@ +module Euler exposing (..) + +import Browser +import Browser.Dom exposing (Viewport) +import Browser.Events as Events +import Hex +import Html exposing (Html, button, div, h3, text) +import Html.Attributes exposing (style) +import Html.Events exposing (on, onClick) +import Json.Decode as Decode exposing (Decoder) +import Maybe +import SingleSlider as Slider +import Svg exposing (circle, line, polyline, svg) +import Svg.Attributes exposing (cx, cy, fill, height, points, r, stroke, width, x1, x2, y1, y2) +import Task +import Time exposing (Posix) + + +main : Platform.Program () Model Msg +main = + Browser.element + { init = \() -> init + , view = view + , update = update + , subscriptions = subscriptions + } + + + +-- MODEL + + +type alias Model = + { part : Particle + , dt : Time + , dt0 : Time + , t : Time + , status : Status + , wWidth : Float + , wHeight : Float + , history : List ( Time, Time, Particle ) + , drag : Maybe Drag + , slider : Slider.Model + } + + +x0 : Position +x0 = + 2.5 + + +init : ( Model, Cmd Msg ) +init = + ( { part = Particle [ x0 ] [ 0 ] + , dt = 0.25 + , dt0 = 0.25 + , t = 0 + , status = Idle + , wWidth = 0 + , wHeight = 0 + , history = [] + , drag = Nothing + , slider = + { min = 0 + , max = 1 + , step = 0.01 + , value = 0.25 + , minFormatter = \_ -> "" + , maxFormatter = \_ -> "" + , currentValueFormatter = \_ _ -> "" + , disabled = False + } + } + , Task.perform GetViewPort Browser.Dom.getViewport + ) + + +type alias Time = + Float + + +type alias Position = + Float + + +type alias Velocity = + Float + + +type alias Particle = + { pos : List Position, vel : List Velocity } + + +type Status + = Idle + | Running + + +type alias Drag = + { start : Float + , current : Float + } + + +getX : Particle -> Position +getX p = + Maybe.withDefault 0 <| List.head <| .pos p + + +getV : Particle -> Velocity +getV p = + Maybe.withDefault 0 <| List.head <| .vel p + + +getX0 : Model -> Position +getX0 m = + let + scale x = + 3 - 6 * x / m.wHeight + in + case m.drag of + Nothing -> + getX m.part + + Just { start, current } -> + getX m.part + scale current - scale start + + +resetParticle : Particle -> Particle +resetParticle { pos, vel } = + case ( List.reverse pos, List.reverse vel ) of + ( x :: _, v :: _ ) -> + Particle [ x ] [ v ] + + _ -> + Particle [ x0 ] [ 0 ] + + + +-- UPDATE + + +type Msg + = Start + | Stop + | Tick Posix + | GetViewPort Viewport + | SliderUpdate Float + | SliderMsg Slider.Msg + | DragStart Float + | DragAt Float + | DragEnd Float + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Start -> + ( { model + | status = Running + , t = 0 + , dt = model.dt0 + , drag = Nothing + , part = resetParticle model.part + } + , Cmd.none + ) + + Stop -> + ( { model + | status = Idle + , part = resetParticle model.part + , t = 0 + } + , Cmd.none + ) + + Tick _ -> + case model.status of + Idle -> + ( model, Cmd.none ) + + Running -> + if model.t > 5 + model.dt then + ( { model + | status = Idle + , part = Particle [ x0 ] [ 0 ] + , history = ( model.dt, model.t, model.part ) :: model.history + , t = 0 + } + , Cmd.none + ) + + else + ( { model + | part = evolve model.part model.t model.dt + , t = model.t + model.dt + } + , Task.perform GetViewPort Browser.Dom.getViewport + ) + + GetViewPort { viewport } -> + ( { model | wWidth = viewport.width, wHeight = viewport.height * 8 / 10 }, Cmd.none ) + + SliderUpdate dt -> + ( { model | dt0 = dt }, Cmd.none ) + + SliderMsg sliderMsg -> + let + ( newSlider, cmd, updateResults ) = + Slider.update sliderMsg model.slider + + newModel = + { model | slider = newSlider, dt0 = newSlider.value } + + newCmd = + if updateResults then + Cmd.batch [ Cmd.map SliderMsg cmd, Cmd.none ] + + else + Cmd.none + in + ( newModel, newCmd ) + + DragStart y -> + case model.status of + Idle -> + ( { model | drag = Just (Drag y y) }, Cmd.none ) + + Running -> + ( model, Cmd.none ) + + DragAt y -> + ( { model | drag = Maybe.map (\{ start } -> Drag start y) model.drag } + , Cmd.none + ) + + DragEnd _ -> + ( { model + | drag = Nothing + , part = Particle [ getX0 model ] [ k * getX0 model ] + } + , Cmd.none + ) + + +k : Float +k = + -2 + + +diffEq : Position -> Velocity -> Time -> Time -> ( Position, Velocity ) +diffEq x _ _ dt = + ( x + (k * x) * dt, k * (x + (k * x) * dt) ) + + +evolve : Particle -> Time -> Time -> Particle +evolve p t dt = + let + ( x, v ) = + diffEq (getX p) (getV p) t dt + in + { p | pos = x :: p.pos, vel = v :: p.vel } + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + (Slider.subscriptions model.slider |> Sub.map SliderMsg) + :: (case model.drag of + Nothing -> + [ Time.every (model.dt * 1000) Tick ] + + Just _ -> + [ Events.onMouseMove (Decode.map DragAt decodeMouseHeight) + , Events.onMouseUp (Decode.map DragEnd decodeMouseHeight) + ] + ) + |> Sub.batch + + +decodeMouseHeight : Decoder Float +decodeMouseHeight = + Decode.field "pageY" Decode.float + + + +-- VIEW + + +view : Model -> Html Msg +view model = + div [] + [ h3 [] [ text "Drag the ball up or down, pick a dt and click Start" ] + , h3 [ style "color" (gradient model.dt0) ] + [ viewSlider model.slider + , button [ onClick Start ] [ text "Start" ] + , button [ onClick Stop ] [ text "Stop" ] + , text ("dt = " ++ String.fromFloat model.dt0) + ] + , svg + [ width (String.fromFloat model.wWidth) + , height (String.fromFloat model.wHeight) + , stroke "black" + ] + ([ line + [ x1 "0" + , x2 (String.fromFloat model.wWidth) + , y1 (String.fromFloat (model.wHeight / 2)) + , y2 (String.fromFloat (model.wHeight / 2)) + ] + [] + , line + [ x1 (String.fromFloat (model.wWidth / 20)) + , x2 (String.fromFloat (model.wWidth / 20)) + , y1 "0" + , y2 (String.fromFloat model.wHeight) + ] + [] + , viewCircle model + ] + ++ plotHistory model + ) + ] + + +viewSlider : Slider.Model -> Html Msg +viewSlider slider = + Slider.view slider |> Html.map SliderMsg + + +scaleX : Float -> Position -> String +scaleX h x = + String.fromFloat (h / 2 * (1 - x / 3)) + + +scaleT : Float -> Time -> String +scaleT w t = + String.fromFloat (w * (0.05 + t / 5)) + + +viewCircle : Model -> Html Msg +viewCircle m = + circle + [ cy (scaleX m.wHeight (getX0 m)) + , cx (scaleT m.wWidth m.t) + , r "10" + , on "mousedown" (Decode.map DragStart decodeMouseHeight) + ] + [] + + +plotPath : Float -> Float -> ( Time, Time, Particle ) -> String +plotPath w h ( dt, tf, particle ) = + let + comb x ( t, s ) = + ( t - dt, s ++ scaleT w t ++ "," ++ scaleX h x ++ " " ) + in + Tuple.second <| List.foldl comb ( tf, "" ) particle.pos + + +plotHistory : Model -> List (Html Msg) +plotHistory m = + let + ( w, h ) = + ( m.wWidth, m.wHeight ) + in + List.map + (\( dt, t, p ) -> + polyline + [ stroke "black" + , fill "none" + , stroke (gradient dt) + , points (plotPath w h ( dt, t, p )) + ] + [] + ) + (( m.dt, m.t, m.part ) :: m.history) + + +gradient : Time -> String +gradient dt = + let + ( r, g, b ) = + ( round (255 * dt), 0, round (255 * (1 - dt)) ) + + col = + Hex.toString (256 * (256 * r + g) + b) + in + if String.length col < 6 then + "#" ++ String.repeat (6 - String.length col) "0" ++ col + + else + "#" ++ col diff --git a/contents/forward_euler_method/code/fortran/euler.f90 b/contents/forward_euler_method/code/fortran/euler.f90 new file mode 100644 index 000000000..5a7c8332e --- /dev/null +++ b/contents/forward_euler_method/code/fortran/euler.f90 @@ -0,0 +1,64 @@ +PROGRAM euler + + IMPLICIT NONE + LOGICAL :: is_approx + REAL(8), DIMENSION(:), ALLOCATABLE :: vec + REAL(8) :: time_step, threshold + INTEGER :: n + + time_step = 0.01d0 + n = 100 + threshold = 0.01d0 + + ALLOCATE(vec(n)) + CALL forward_euler(time_step, n, vec) + is_approx = check_result(vec, threshold, time_step) + + WRITE(*,*) is_approx + + DEALLOCATE(vec) + +CONTAINS + + SUBROUTINE forward_euler(time_step, n, vec) + + IMPLICIT NONE + REAL(8), DIMENSION(:), INTENT(OUT) :: vec + REAL(8), INTENT(IN) :: time_step + INTEGER, INTENT(IN) :: n + INTEGER :: i + + vec(1) = 1d0 + + DO i=1, n-1 + + vec(i+1) = vec(i) - 3d0 * vec(i) * time_step + + END DO + END SUBROUTINE + + LOGICAL FUNCTION check_result(euler_result, threshold, time_step) + + IMPLICIT NONE + REAL(8), DIMENSION(:), INTENT(IN) :: euler_result + REAL(8), INTENT(IN) :: threshold, time_step + REAL(8) :: time, solution + INTEGER :: i + + check_result = .TRUE. + + DO i = 1, SIZE(euler_result) + + time = (i - 1) * time_step + solution = EXP(-3d0 * time) + + IF (ABS(euler_result(i) - solution) > threshold) THEN + + WRITE(*,*) euler_result(i), solution + check_result = .FALSE. + + END IF + END DO + END FUNCTION +END PROGRAM euler + diff --git a/contents/forward_euler_method/code/go/euler.go b/contents/forward_euler_method/code/go/euler.go new file mode 100644 index 000000000..0f8ba1618 --- /dev/null +++ b/contents/forward_euler_method/code/go/euler.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "math" +) + +func forwardEuler(timeStep float64, n int) []float64 { + result := make([]float64, n) + result[0] = 1 + for x := 1; x < n; x++ { + result[x] = result[x-1] - 3*result[x-1]*timeStep + } + return result +} + +func check(result []float64, threshold, timeStep float64) bool { + approx := true + for x := 0.; int(x) < len(result); x++ { + solution := math.Exp(-3. * x * timeStep) + if math.Abs(result[int(x)]-solution) > threshold { + fmt.Println(result[int(x)], solution) + approx = false + } + } + return approx +} + +func main() { + timeStep, threshold := .01, .01 + n := 100 + + result := forwardEuler(timeStep, n) + if check(result, threshold, timeStep) { + fmt.Println("All values within threshold") + } else { + fmt.Println("Value(s) not within threshold") + } +} diff --git a/contents/forward_euler_method/code/haskell/euler.hs b/contents/forward_euler_method/code/haskell/euler.hs new file mode 100644 index 000000000..6ca9bf479 --- /dev/null +++ b/contents/forward_euler_method/code/haskell/euler.hs @@ -0,0 +1,20 @@ +solveEuler :: Num a => (a -> a) -> a -> a -> [a] +solveEuler f ts = iterate (\x -> x + f x * ts) + +checkResult :: (Ord a, Num a, Num t, Enum t) => a -> (t -> a) -> [a] -> Bool +checkResult thresh check = + and . zipWith (\i k -> abs (check i - k) < thresh) [0..] + +kinematics :: Double -> Double +kinematics x = -3 * x + +main :: IO () +main = + let timestep = 0.01 + n = 100 + threshold = 0.01 + checkResult' = checkResult threshold $ exp . (\x -> -3 * x * timestep) + in putStrLn $ + if checkResult' (take n $ solveEuler kinematics timestep 1) + then "All values within threshold" + else "Value(s) not in threshold" diff --git a/contents/forward_euler_method/code/java/ForwardEuler.java b/contents/forward_euler_method/code/java/ForwardEuler.java new file mode 100644 index 000000000..16d398a5f --- /dev/null +++ b/contents/forward_euler_method/code/java/ForwardEuler.java @@ -0,0 +1,38 @@ +public class ForwardEuler { + private static double[] solveEuler(double timestep, int n) { + double[] eulerResult = new double[n]; + + //Setting the initial condition + eulerResult[0] = 1; + for(int i = 1; i < eulerResult.length; i++) { + eulerResult[i] = eulerResult[i - 1] - (3 * eulerResult[i - 1] * timestep); + } + return eulerResult; + } + + private static boolean checkResult(double[] eulerResult, double threshold, double timestep) { + boolean isApprox = true; + + for(int i = 0; i < eulerResult.length; i++) { + double time = i * timestep; + double solution = Math.exp(-3 * time); + if(Math.abs(eulerResult[i] - solution) > threshold) { + System.out.println(eulerResult[i] + " " + solution); + isApprox = false; + } + } + + return isApprox; + } + + public static void main(String[] args) { + double timestep = 0.1; + int n = 100; + double threshold = 0.1; + + double[] eulerResult = solveEuler(timestep, n); + boolean isApprox = checkResult(eulerResult, threshold, timestep); + + System.out.println(isApprox); + } +} diff --git a/contents/forward_euler_method/code/javascript/euler.js b/contents/forward_euler_method/code/javascript/euler.js new file mode 100644 index 000000000..c9cbcccb3 --- /dev/null +++ b/contents/forward_euler_method/code/javascript/euler.js @@ -0,0 +1,31 @@ +function forwardEuler(timeStep, n) { + const arr = [1]; + for (let i = 1; i <= n; i++) { + arr[i] = arr[i - 1] - 3 * arr[i - 1] * timeStep; + } + return arr; +} + +function checkEuler(arr, timeStep, threshold) { + let isApprox = true; + arr.forEach((_value, i) => { + const solution = Math.exp(-3 * timeStep * i); + + if (Math.abs(arr[i] - solution) > threshold) { + console.log(arr[i], solution); + isApprox = false; + } + }); + return isApprox; +} + +function main() { + const timeStep = 0.01; + const threshold = 0.01; + const n = 100; + const eulerResult = forwardEuler(timeStep, n); + const checkResult = checkEuler(eulerResult, timeStep, threshold); + console.log(checkResult); +} + +main(); diff --git a/contents/forward_euler_method/code/julia/euler.jl b/contents/forward_euler_method/code/julia/euler.jl new file mode 100644 index 000000000..49ddcacaf --- /dev/null +++ b/contents/forward_euler_method/code/julia/euler.jl @@ -0,0 +1,39 @@ +function solve_euler(timestep::Float64, n::Int64) + euler_result = Vector{Float64}(undef, n) + + # Setting the initial condition + euler_result[1] = 1; + for i = 2:length(euler_result) + euler_result[i] = euler_result[i-1] - 3.0*euler_result[i-1]*timestep + end + return euler_result +end + +function check_result(euler_result::Vector{Float64}, threshold::Float64, + timestep::Float64) + is_approx = true + + for i = 1:length(euler_result) + time = (i - 1)*timestep + solution = exp(-3*time); + if (abs(euler_result[i] - solution) > threshold) + println(euler_result[i], solution) + is_approx = false + end + end + + return is_approx +end + +function main() + timestep = 0.01 + n = 100 + threshold = 0.01 + + euler_result = solve_euler(timestep,n) + is_approx = check_result(euler_result, threshold, timestep) + + println(is_approx) +end + +main() diff --git a/contents/forward_euler_method/code/matlab/euler.m b/contents/forward_euler_method/code/matlab/euler.m new file mode 100644 index 000000000..fb6bf210f --- /dev/null +++ b/contents/forward_euler_method/code/matlab/euler.m @@ -0,0 +1,46 @@ +clc;clear;close all + +%========================================================================== +% Define Function +f =@(x) -3*x; + +% Define Initial and Final time +tInit=0; +tLast=5; + +% Define number of points +N=1e2; + +% Set Initial Conditions +yInit=1; +%========================================================================== + +dt=(tLast-tInit)/(N-1); % Calculate dt +t=[tInit:dt:tLast]; % Preallocate time array +y=zeros(1,length(t)); % Preallocate solution array +y(1)=yInit; % Impose Initial Conditions + +% Loop over time +for i=1:length(t)-1 + + t(i+1) = t(i) + dt; % Calculate next time + y(i+1) = y(i) + f( y(i) )*dt; % Update solution + +end + +% Plot numerical solution +plot(t,y) + +% Create analytical solution +g=@(x) exp(-3*x); +z=g(t); + +% Plot analytical solution on the same graph +hold on +plot(t,z,'--') + +% Set axis, title and legend +xlabel('t');ylabel('y(t)'); +title('Analytical VS Numerical Solution') +grid +legend('Numerical','Analytical') diff --git a/contents/forward_euler_method/code/nim/forwardeuler.nim b/contents/forward_euler_method/code/nim/forwardeuler.nim new file mode 100644 index 000000000..6199a9b89 --- /dev/null +++ b/contents/forward_euler_method/code/nim/forwardeuler.nim @@ -0,0 +1,32 @@ +import math + +proc solveEuler(timestep: float, n: int): seq[float] = + var res = newSeq[float](n) + + res[0] = 1.0 + + for i in 1 .. n - 1: + res[i] = res[i - 1] - 3 * res[i - 1] * timestep + + return res + +proc check(res: seq[float], timestep, threshold: float): bool = + var approx: bool = true; + + for i in 0 .. len(res) - 1: + let solution: float = exp(-3.0 * float(i) * timestep) + if abs(res[i]) - solution > threshold: + echo res[i] + echo solution + approx = false + + return approx + +const + timestep: float = 0.1 + n: int = 100 + threshold: float = 0.1 + eulerResult: seq[float] = solveEuler(timestep, n) + approx: bool = check(eulerResult, threshold, timestep) + +echo approx diff --git a/contents/forward_euler_method/code/python/euler.py b/contents/forward_euler_method/code/python/euler.py new file mode 100644 index 000000000..993be5b95 --- /dev/null +++ b/contents/forward_euler_method/code/python/euler.py @@ -0,0 +1,31 @@ +import math + + +def forward_euler(time_step, n): + result = [0] * n + result[0] = 1 + for i in range(1, n): + result[i] = result[i - 1] - 3 * result[i - 1] * time_step + return result + + +def check(result, threshold, time_step): + approx = True + for i in range(len(result)): + solution = math.exp(-3 * i * time_step) + if abs(result[i] - solution) > threshold: + print(result[i], solution) + approx = False + return approx + + +def main(): + time_step = 0.01 + n = 100 + threshold = 0.01 + + result = forward_euler(time_step, n) + approx = check(result, threshold, time_step) + print("All values within threshold") if approx else print("Value(s) not in threshold") + +main() diff --git a/contents/forward_euler_method/code/rust/euler.rs b/contents/forward_euler_method/code/rust/euler.rs new file mode 100644 index 000000000..cbfc44138 --- /dev/null +++ b/contents/forward_euler_method/code/rust/euler.rs @@ -0,0 +1,31 @@ +fn main() { + let mut result = [0.0; 100]; + let threshold = 0.01; + let timestep = 0.01; + + solve_euler(timestep, &mut result); + println!("{}", check_result(&result, threshold, timestep)); +} + +fn solve_euler(timestep: f64, result: &mut [f64]) { + let n = result.len(); + if n != 0 { + result[0] = 1.0; + for i in 1..n { + result[i] = result[i - 1] - 3.0 * result[i - 1] * timestep; + } + } +} + +fn check_result(result: &[f64], threshold: f64, timestep: f64) -> bool { + let mut is_approx: bool = true; + for (i, val) in result.iter().enumerate() { + let solution = (-3.0 * i as f64 * timestep).exp(); + if (val - solution).abs() > threshold { + println!("{} {}", val, solution); + is_approx = false; + } + } + + return is_approx; +} diff --git a/contents/forward_euler_method/code/swift/euler.swift b/contents/forward_euler_method/code/swift/euler.swift new file mode 100644 index 000000000..033b6d3fb --- /dev/null +++ b/contents/forward_euler_method/code/swift/euler.swift @@ -0,0 +1,42 @@ +import Foundation + +func solveEuler(timeStep: Double, n: Int) -> [Double] { + var result : [Double] = [1] + + for i in 1...n { + result.append(result[i - 1] - 3 * result[i - 1] * timeStep) + } + + return result +} + +func checkResult(result: [Double], threshold: Double, timeStep: Double) -> Bool { + var isApprox = true + + for i in 0.. threshold { + print(result[i], solution) + isApprox = false + } + } + + return isApprox +} + +func main() { + let timeStep = 0.01 + let n = 100 + let threshold = 0.01 + + let result = solveEuler(timeStep: timeStep, n: n) + let isApprox = checkResult(result: result, threshold: threshold, timeStep: timeStep) + + if isApprox { + print("All values within threshold") + } else { + print("Value(s) not in threshold") + } +} + +main() diff --git a/contents/forward_euler_method/code/v/euler.v b/contents/forward_euler_method/code/v/euler.v new file mode 100644 index 000000000..e590bbdcf --- /dev/null +++ b/contents/forward_euler_method/code/v/euler.v @@ -0,0 +1,37 @@ +import math + +fn forward_euler(timestep f64, n int) []f64 { + mut res := [f64(0.0)].repeat(n) + res[0] = f64(1) + for x := 1; x < n; x++ { + res[x] = res[x-1] - 3.0*res[x-1]*timestep + } + return res +} + +fn check(result []f64, threshold, timestep f64) bool { + mut approx := true + for x := 0; x < result.len; x++ { + solution := math.exp(-3.0 * f64(x) * timestep) + if math.abs(result[x]-solution) > threshold { + tmp := result[x] + println("There is a mismatch: abs($tmp-$solution) > $threshold!") + approx = false + } + } + return approx +} + +fn main() { + timestep := .01 + threshold := .01 + n := 100 + + result := forward_euler(timestep, n) + + if check(result, threshold, timestep) { + println("All values within threshold") + } else { + println("Value(s) not within threshold") + } +} \ No newline at end of file diff --git a/contents/forward_euler_method/forward_euler_method.md b/contents/forward_euler_method/forward_euler_method.md new file mode 100644 index 000000000..f97ff27a2 --- /dev/null +++ b/contents/forward_euler_method/forward_euler_method.md @@ -0,0 +1,174 @@ +# The Forward Euler Method + +The Euler methods are some of the simplest methods to solve ordinary differential equations numerically. +They introduce a new set of methods called the Runge Kutta methods, which will be discussed in the near future! + +As a physicist, I tend to understand things through methods that I have learned before. +In this case, it makes sense for me to see Euler methods as extensions of the [Taylor Series Expansion](../taylor_series_expansion/taylor_series_expansion.md). +These expansions basically approximate functions based on their derivatives, like so: + +$$ +f(x) \approx f(a) + \frac{1}{1!}\frac{df(a)}{da}(x-a) + + \frac{1}{2!}\frac{d^2f(a)}{da^2}(x-a)^2 + + \frac{1}{3!}\frac{d^3f(a)}{da^3}(x-a)^3 + \cdots +$$ + +Like before, $$f(x)$$ is some function along real or complex space, $$a$$ is the point that we are expanding from, and $$f^{(n)}(x)$$ denotes the $$n^{\text{th}}$$ derivative of $$f(x)$$. + +So, what does this mean? Well, as mentioned, we can think of this similarly to the kinematic equation: +$$ +x = x_0 + vt + \frac{1}{2}at^2 +$$ +where $$x$$ is position, $$v$$ is velocity, and $$a$$ is acceleration. +This equation allows us to find the position of an object based on it's previous position ($$x_0$$), the derivative of it's position with respect to time ($$\frac{dx}{dt} = v_0$$) and one derivative on top of that ($$\frac{d^2x}{dx^2} = a$$). +As stated in the Tayor Series Expansion, the acceleration term must also have $$\frac{1}{2!}$$ in front of it. + +Now, how does this relate to the Euler methods? +Well, with these methods, we assume that we are looking for a position in some space, usually denoted as $$y(t)$$, but we can use any variable. +The methods assume that we have some function to evaluate the derivative of $$y(t)$$. In other words, we know that $$\frac{dy(t)}{dt} = f(t,y(t))$$. +For the kinematic equation, we know what this is! + +$$ +\frac{dy(t)}{dt} = v = f(t,y(t)) = v_0 + a(t) +$$ + +So, we can iteratively solve for position by first solving for velocity. By following the kinematic equation (or Taylor Series Expansion), we find that + +$$ +y_{n+1} = y_n + f(t_n, y_n) dt +$$ + +For any timestep $$dt$$. This means that if we are solving the kinematic equation, we simply have the following equations: + +$$ +\begin{align} + x_{n+1} &= x_n + v dt \\ + v_{n+1} &= a (t_n) +\end{align} +$$ + +Now, solving this set of equations in this way is known as the *forward* Euler Method. +In fact, there is another method known as the *backward* Euler Method, which we will get to soon enough. +For now, it is important to note that the error of these methods depend on the timestep chosen. + +

+ +

+ +For example, here we see dramatically different results for different timesteps for solving the ODE $$y' = \frac{x^3}{6}$$, whose solution is $$y = \frac{x^2}{2}$$. +The blue line is the analytical solution, the green is with a timestep of 0.5 and the red is with a timestep of 1. +To be clear: the larger the timestep, the worse the error becomes; however, there is at least one more problem with using the forward Euler method on real problems: instabilities. + +As we mentioned, the forward Euler method approximates the solution to an Ordinary Differential Equation (ODE) by using only the first derivative. +This is (rather expectedly) a poor approximation. +In fact, the approximation is so poor that the error associated with running this algorithm can add up and result in incredibly incorrect results. +As you might imagine, the only solution to this is decreasing the timestep and hoping for the best or using a similar method with different stability regions, like the backward Euler method. + +Let's assume we are solving a simple ODE: $$y' = -3y, y(0) = 1$$. +The solution here is $$y(t) = e^{-3t}$$ and we can find this solution somewhat easily with the forward Euler method shown below. +That said, by choosing a larger timestep, we see the Euler method's solution oscillate above and below 0, which should *never* happen. +If we were to take the Euler method's solution as valid, we would incorrectly assume that $$e^{-3t}$$ will become negative! + +

+ +

+ +Like above, the blue line is the analytical solution, the green is with a timestep of 0.5 and the red is with a timestep of 1. +Here, it's interesting that we see 2 different instability patterns. +The green is initially unstable, but converges onto the correct solution, but the red is wrong from the get-go and only gets more wrong as time goes on. + +In truth, the stability region of the forward Euler method for the case where $$y' = ky$$ can be found with the following inequality: +$$ +|kdt + 1 | \leq 1 +$$ +Which means that the forward Euler method is actually unstable for most values! +If we want to stick to using the forward Euler method exclusively, the only solution is to decrease the timestep until it is within this stability region, and that's not necessarily easy for all cases. +So now it might be obvious that another, more stable method should be used instead; however, many other stable methods are *implicit*, which means that in order to find the solution, we need to solve a system of equations via the [Thomas Algorithm](../thomas_algorithm/thomas_algorithm.md) or [Gaussian Elimination](../gaussian_elimination/gaussian_elimination.md). +Which is an entire layer of complexity that most people don't want to mess with! + +Now, here is where we might want to relate the method to another algorithm that is sometimes used for a similar use-case: [Verlet Integration](../verlet_integration/verlet_integration.md). +Verlet integration has a distinct advantage over the forward Euler method in both error and stability with more coarse-grained timesteps; however, Euler methods are powerful in that they may be used for cases other than simple kinematics. +That said, in practice, due to the instability of the forward Euler method and the error with larger timesteps, this method is rarely used in practice. +That said, variations of this method *are* certainly used (for example Crank-Nicolson and [Runge-Kutta](../runge_kutta_methods/runge_kutta_methods.md), so the time spent reading this chapter is not a total waste! + +## Video Explanation + +Here is a video describing the forward Euler method: + +
+ +
+ +## Example Code + +Like in the case of [Verlet Integration](../verlet_integration/verlet_integration.md), the easiest way to test to see if this method works is to test it against a simple test-case. +Here, the most obvious test-case would be dropping a ball from 5 meters, which is my favorite example, but proved itself to be slightly less enlightening than I would have thought. +So, this time, let's remove ourselves from any physics and instead solve the following ODE: $$y(t)' = -3t$$ with the initial condition that $$y(0) = 1$$. +Note that in this case, the velocity is directly given by the ODE and the acceleration is not part of the model. + +{% method %} +{% sample lang="jl" %} +[import, lang:"julia"](code/julia/euler.jl) +{% sample lang="c" %} +[import, lang:"c"](code/c/euler.c) +{% sample lang="cpp" %} +[import, lang:"cpp"](code/cpp/euler.cpp) +{% sample lang="rs" %} +[import, lang:"rust"](code/rust/euler.rs) +{% sample lang="elm" %} +[import:78-91, lang:"elm"](code/elm/src/Euler.elm) +[import:236-252, lang:"elm"](code/elm/src/Euler.elm) + +Full code for the visualization follows: +[import, lang:"elm"](code/elm/src/Euler.elm) + +{% sample lang="py" %} +[import, lang:"python"](code/python/euler.py) +{% sample lang="hs" %} +[import, lang:"haskell"](code/haskell/euler.hs) +{% sample lang="m" %} +[import, lang:"matlab"](code/matlab/euler.m) +{% sample lang="swift" %} +[import, lang:"swift"](code/swift/euler.swift) +{% sample lang="js" %} +[import, lang:"javascript"](code/javascript/euler.js) +{% sample lang="f90" %} +[import, lang:"fortran"](code/fortran/euler.f90) +{% sample lang="go" %} +[import, lang:"go"](code/go/euler.go) +{% sample lang="v" %} +[import, lang:"v"](code/v/euler.v) +{% sample lang="asm-x64" %} +[import, lang:"asm-x64"](code/asm-x64/euler.s) +{% sample lang="java" %} +[import, lang:"java"](code/java/ForwardEuler.java) +{% sample lang="nim" %} +[import, lang:"nim"](code/nim/forwardeuler.nim) +{% sample lang="lisp" %} +[import, lang="lisp"](code/clisp/euler.lisp) +{% endmethod %} + + + +## License + +##### Code Examples + +The code examples are licensed under the MIT license (found in [LICENSE.md](https://github.com/algorithm-archivists/algorithm-archive/blob/master/LICENSE.md)). + +##### Text + +The text of this chapter was written by [James Schloss](https://github.com/leios) and is licensed under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +[

](https://creativecommons.org/licenses/by-sa/4.0/) + +##### Images/Graphics +- The image "[FEerror](res/error.png)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). +- The image "[FEinstability](res/instability.png)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode). + +##### Pull Requests + +After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter: +- none diff --git a/contents/forward_euler_method/res/error.png b/contents/forward_euler_method/res/error.png new file mode 100644 index 0000000000000000000000000000000000000000..4d5e4feb9879d1089e7a57894d479833fbf83cd1 GIT binary patch literal 8736 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 Y{l&-Y3MEy*kq?lzhQXr>HT#JF13qopyZ`_I literal 0 HcmV?d00001 diff --git a/contents/forward_euler_method/res/instability.png b/contents/forward_euler_method/res/instability.png new file mode 100644 index 0000000000000000000000000000000000000000..e1c2b8c476b0e77621896ae99d60aba4d90050cc GIT binary patch literal 10680 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 literal 0 HcmV?d00001 From 46a791b1c829d46000d78d3587d9ae7ef376e420 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Sun, 7 Nov 2021 20:16:39 +0100 Subject: [PATCH 3/3] just removing chapter from summary --- SUMMARY.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index d76af219d..e41ba39d4 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -32,8 +32,6 @@ * [FFT](contents/cooley_tukey/cooley_tukey.md) * [Decision Problems](contents/decision_problems/decision_problems.md) * [Stable Marriage Problem](contents/stable_marriage_problem/stable_marriage_problem.md) -* [Differential Equation Solvers](contents/differential_equations/differential_equations.md) - * [Forward Euler Method](contents/forward_euler_method/forward_euler_method.md) * [Physics Solvers](contents/physics_solvers/physics_solvers.md) * [Verlet Integration](contents/verlet_integration/verlet_integration.md) * [Quantum Systems](contents/quantum_systems/quantum_systems.md)