Module: MyMathGem::ODESolver

Defined in:
lib/my_math_gem/ode_solver.rb

Class Method Summary collapse

Class Method Details

.euler(t0:, y0:, t_end:, steps:, f:, callback: nil, return_separate: false) ⇒ Object

Metode Euler untuk ODE y’ = f(t, y)



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/my_math_gem/ode_solver.rb', line 4

def self.euler(t0:, y0:, t_end:, steps:, f:, callback: nil, return_separate: false)
  validate_params!(t0, y0, t_end, steps, f)

  dt = (t_end - t0).to_f / steps
  ts = [t0]
  ys = [deep_copy(y0)]

  steps.times do |i|
    t, y = ts[-1], ys[-1]
    dy = f.call(t, y)
    y_next = vector_add(y, scalar_multiply(dy, dt))
    t_next = t + dt

    ts << t_next
    ys << y_next

    callback.call(t_next, y_next) if callback.is_a?(Proc)
  end

  return_separate ? { times: ts, values: ys } : ts.zip(ys)
end

.heun(t0:, y0:, t_end:, steps:, f:, callback: nil, return_separate: false) ⇒ Object

Metode Heun (Improved Euler)



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/my_math_gem/ode_solver.rb', line 62

def self.heun(t0:, y0:, t_end:, steps:, f:, callback: nil, return_separate: false)
  validate_params!(t0, y0, t_end, steps, f)

  dt = (t_end - t0).to_f / steps
  ts = [t0]
  ys = [deep_copy(y0)]

  steps.times do
    t, y = ts[-1], ys[-1]

    k1 = f.call(t, y)
    y_predict = vector_add(y, scalar_multiply(k1, dt))
    k2 = f.call(t + dt, y_predict)
    increment = scalar_multiply(vector_add(k1, k2), dt / 2.0)
    y_next = vector_add(y, increment)
    t_next = t + dt

    ts << t_next
    ys << y_next

    callback.call(t_next, y_next) if callback.is_a?(Proc)
  end

  return_separate ? { times: ts, values: ys } : ts.zip(ys)
end

.runge_kutta4(t0:, y0:, t_end:, steps:, f:, callback: nil, return_separate: false) ⇒ Object

Metode Runge-Kutta orde 4 (RK4)



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/my_math_gem/ode_solver.rb', line 27

def self.runge_kutta4(t0:, y0:, t_end:, steps:, f:, callback: nil, return_separate: false)
  validate_params!(t0, y0, t_end, steps, f)

  dt = (t_end - t0).to_f / steps
  ts = [t0]
  ys = [deep_copy(y0)]

  steps.times do
    t, y = ts[-1], ys[-1]

    k1 = f.call(t, y)
    k2 = f.call(t + dt/2.0, vector_add(y, scalar_multiply(k1, dt/2.0)))
    k3 = f.call(t + dt/2.0, vector_add(y, scalar_multiply(k2, dt/2.0)))
    k4 = f.call(t + dt, vector_add(y, scalar_multiply(k3, dt)))

    increment = scalar_multiply(
      vector_add(
        vector_add(k1, scalar_multiply(k2, 2)),
        vector_add(scalar_multiply(k3, 2), k4)
      ),
      dt / 6.0
    )
    y_next = vector_add(y, increment)
    t_next = t + dt

    ts << t_next
    ys << y_next

    callback.call(t_next, y_next) if callback.is_a?(Proc)
  end

  return_separate ? { times: ts, values: ys } : ts.zip(ys)
end