Module: YPetri::Core::Timed::RungeKutta

Defined in:
lib/y_petri/core/timed/runge_kutta.rb

Overview

Runge-Kutta method. Like vanilla Euler method, assumes that only T transitions are in the net.

Instance Method Summary collapse

Instance Method Details

#alert_user!(object) ⇒ Object


108
109
110
111
112
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 108

def alert_user! object  # TODO: As soon as more core's method begin relying on core's own state,
  # this method will be moved to Core module.

  @user_alert_closure.call( object )
end

#delta(Δt) ⇒ Object Also known as: Δ

Computes delta by Runge-Kutta 4th order method.


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
60
61
62
63
64
65
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 8

def delta Δt  # The f below is from the equation state' = f( state )

  f = lambda do |mv| # mv is the marking vector of the free places
    # Delta from s transitions.
    # TODO: This is only array now. Make it something else. One possibility
    # would be to use simulation's MarkingVector class, but core should
    # actually have its own marking vector class, probably parametrized by
    # the net. It does not matter because alone I won't be able to exhaust
    # all the possibilities.
    delta_s = simulation.MarkingVector.zero( simulation.free_pp )    # Here, we get the nonstoichiometric transitions of the simulation.

    nonstoichio_tt = simulation.s_tt    # Now, let's get the delta contribution of the nonstoichio. tt.

    nonstoichio_tt.each { |t|
      domain, codomain = t.domain, t.codomain # transition's domain
      function = t.rate_closure # transition's function
      output = Array function.call( *domain.map { |p| mv.fetch p } )
      codomain.each_with_index do |p, i|
        delta_s.set( p, delta_s.fetch( p ) + output[i] )
      end      # TODO: The above code is suboptimal, needlessly computing
      # MarkingVector#index and #fetch( place ) each time.
      # The array incrementing might not be the best choice either,
      # and most of all, the whole thing would need to be compiled
      # into assembly language or at least FORTRAN.

    }

    # Delta from S transitions.
    # TODO: (Same remark as for s transitions, see above.)
    delta_S = simulation.MarkingVector.zero( simulation.free_pp )    # Here, we get the stoichiometric transitions of the simulation

    stoichio_tt = simulation.S_tt    # Now, let's get the delta contribution of the stoichio. tt.

    stoichio_tt.each { |t|
      domain, codomain = t.domain, t.codomain # transition's domain
      function = t.rate_closure # transition's function
      s = t.stoichiometry
      flux = function.call( *domain.map { |place| mv.fetch place } )
      codomain.each_with_index do |p, i|
        delta_S.set( p, delta_S.fetch( p ) + flux * s[i] )
      end      # TODO: Again, the above code is suboptimal.

    }

    return delta_s + delta_S
  end

  y = marking_of_free_places

  k1 = f.( y ) # puts "k1 ( = f( y ) ) is #{k1}"
  k2 = f.( y + Δt / 2 * k1 ) # puts "k2 is #{k2}"
  k3 = f.( y + Δt / 2 * k2 ) # puts "k3 is #{k3}"
  k4 = f.( y + Δt * k3 ) # puts "k4 is #{k4}"

  rslt = Δt / 6 * ( k1 + 2 * k2 + 2 * k3 + k4 ) # puts "rslt is #{rslt}"

  return rslt                 # Marking vector of free places
end

#increment_marking_of_free_places(by) ⇒ Object


79
80
81
82
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 79

def increment_marking_of_free_places by  # TODO: Same remark as above.

  @marking_of_free_places += by
end

#increment_time!(by) ⇒ Object


84
85
86
87
88
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 84

def increment_time! by  # TODO: Once other timed methods than runge_kutta are reasonable, this
  # should be moved to core/timed.rb

  @time += by
end

#reset_time!(to = 0.0) ⇒ Object


90
91
92
93
94
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 90

def reset_time! to=0.0  # TODO: Once other timed methods than runge_kutta are reasonable, this
  # should be moved to core/timed.rb

  @time = to
end

#set_user_alert_closure(&block) ⇒ Object


96
97
98
99
100
101
102
103
104
105
106
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 96

def set_user_alert_closure &block  # TODO: Core's runge_kutta method is special for now, and even
  # simulation recognizes that. With runge_kutta method, core uses
  # single @user_alert_closure which it calls whenever the state
  # of the core progresses. It is the business of the user to supply,
  # before using the core, that does what the user wants. It is also
  # imaginable that different core's modes of operation would have
  # different sensitivity with regard to alerting the user, but for now,
  # the user is alerted whenever anything happens at all.

  @user_alert_closure = block
end

#step!(Δt = simulation.step) ⇒ Object


68
69
70
71
72
73
74
75
76
77
# File 'lib/y_petri/core/timed/runge_kutta.rb', line 68

def step! Δt=simulation.step  # TODO: Thus far, runge_kutta method is an exception in the core in
  # that it works with core's own state. (Core used to work with
  # simulation's state before and rely on the simulation to provide
  # state increment and assign closures.) This is how whole core should
  # work.

  increment_marking_of_free_places Δ( Δt )
  increment_time! Δt
  alert_user! marking_of_free_places
end