y_petri 2.0.7 → 2.0.14.p1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Mixin for timed Petri net transitions.
4
+ #
5
+ module YPetri::Transition::Timed
6
+ # Transition's action (before validation). Requires Δt as an argument.
7
+ #
8
+ def action Δt
9
+ if has_rate? then
10
+ if stoichiometric? then
11
+ rate = rate_closure.( *domain_marking )
12
+ stoichiometry.map { |coeff| rate * coeff * Δt }
13
+ else # assuming that rate closure return value has correct arity
14
+ rate_closure.( *domain_marking ).map { |e| component * Δt }
15
+ end
16
+ else # timed rateless
17
+ if stoichiometric? then
18
+ rslt = action_closure.( Δt, *domain_marking )
19
+ stoichiometry.map { |coeff| rslt * coeff }
20
+ else
21
+ action_closure.( Δt, *domain_marking ) # caveat result arity!
22
+ end
23
+ end
24
+ end
25
+
26
+ # Fires the transition, honoring cocking. Returns true if the transition
27
+ # fired, false if it wasn't cocked.
28
+ #
29
+ def fire Δt
30
+ cocked?.tap { |x| ( uncock; fire! Δt ) if x }
31
+ end
32
+
33
+ # Fires the transition regardless of cocking. For timed transitions, takes
34
+ # Δt as an argument.
35
+ #
36
+ def fire! Δt
37
+ try "to call #fire method" do
38
+ act = note "action", is: Array( action Δt )
39
+ codomain.each_with_index do |codomain_place, i|
40
+ note "adding action element no. #{i} to place #{codomain_place}"
41
+ codomain_place.add( note "marking change", is: act.fetch( i ) )
42
+ end
43
+ end
44
+ return nil
45
+ end
46
+
47
+ # YPetri transitions are _enabled_ if and only if the intended action would
48
+ # lead to a legal codomain marking. For timed transitions, +#enabled?+ method
49
+ # takes Δt as an argument.
50
+ #
51
+ def enabled? Δt
52
+ codomain.zip( action Δt ).all? do |place, change|
53
+ begin; place.guard.( place.marking + change )
54
+ rescue YPetri::GuardError; false end
55
+ end
56
+ end
57
+ end # class YPetri::Transition::Timed
@@ -3,124 +3,124 @@
3
3
  require_relative 'dependency_injection'
4
4
  require_relative 'transition/arcs'
5
5
  require_relative 'transition/cocking'
6
- require_relative 'transition/constructor_syntax'
7
-
8
- # A Petri net transition. There are 6 basic types of YPetri transitions:
9
- #
10
- # * <b>ts</b> – timeless nonstoichiometric
11
- # * <b>tS</b> timeless stoichiometric
12
- # * <b>Tsr</b> timed rateless nonstoichiometric
13
- # * <b>TSr</b> timed rateless stoichiometric
14
- # * <b>sR</b> – nonstoichiometric with rate
15
- # * <b>SR</b> – stoichiometric with rate
16
- #
17
- # These 6 kinds of YPetri transitions correspond to the vertices of a cube,
18
- # whose 3 dimensions are:
19
- #
20
- # - stoichiometric (S) / nonstoichiometric (s)
21
- # - timed (T) / timeless (t)
22
- # - having rate (R) / not having rate (r)
23
- #
24
- # I. For stoichiometric transitions:
25
- # 1. Rate vector is computed as rate * stoichiometry vector, or
26
- # 2. Δ vector is computed a action * stoichiometry vector.
27
- # II. For non-stoichiometric transitions:
28
- # 1. Rate vector is obtained as the rate closure result, or
29
- # 2. action vector is obtained as the action closure result.
30
- #
31
- # Conclusion: stoichiometricity distinguishes *need to multiply the
32
- # rate/action closure result by stoichiometry*.
33
- #
34
- # I. For transitions with rate, the closure result has to be
35
- # multiplied by the time step duration (delta_t) to get action.
36
- # II. For rateless transitions, the closure result is used as is.
37
- #
38
- # Conclusion: has_rate? distinguishes *need to multiply the closure
39
- # result by delta time* -- differentiability of action by time.
6
+ require_relative 'transition/construction'
7
+ require_relative 'transition/timed'
8
+ require_relative 'transition/ordinary_timeless'
9
+ require_relative 'transition/assignment'
10
+
11
+ # A Petri net transition. Usually depicted as square boxes, transitions
12
+ # represent operations over the net's marking vector -- how the marking changes
13
+ # when the transition activates (_fires_).
40
14
  #
41
- # I. For timed transitions, action is time-dependent. Transitions with
42
- # rate are thus always timed. In rateless transitions, timedness means
43
- # that the action closure expects time step length (delta_t) as its first
44
- # argument - its arity is thus codomain size + 1.
45
- # II. For timeless transitions, action is time-independent. Timeless
46
- # transitions are necessarily also rateless. Arity of the action closure
47
- # is expected to match the domain size.
48
- #
49
- # Conclusion: Transitions with rate are always timed. In rateless
50
- # transitions, timedness distinguishes the need to supply time step
51
- # duration as the first argument to the action closure.
52
- #
53
- # Since transitions with rate are always timed, and vice-versa, timeless
54
- # transitions cannot have rate, there are not 8, but only 6 permissible
55
- # combinations -- 6 basic transition types listed above.
56
- #
57
15
  # === Domain and codomin
58
16
  #
59
- # Each transition has a domain, or 'upstream places': A collection of places
17
+ # Each transition has a _domain_ -- upstream places. Upstream places are those,
60
18
  # whose marking directly affects the transition's operation. Also, each
61
- # transition has a codomain, or 'downstream places': A collection of places,
19
+ # transition has a _codomain_ -- downstream places. Downstream places are those,
62
20
  # whose marking is directly affected by the transition's operation.
63
21
  #
64
22
  # === Action and action vector
65
23
  #
66
- # Regardless of the type, every transition has <em>action</em>:
67
- # A prescription of how the transition changes the marking of its codomain
68
- # when it fires. With respect to the transition's codomain, we can also
69
- # talk about <em>action vector</em>. For non-stoichiometric transitions,
70
- # the action vector is directly the output of the action closure or rate
71
- # closure multiplied by Δtime, while for stoichiometric transitions, this
72
- # needs to be additionaly multiplied by the transitions stoichiometric
73
- # vector. Now we are finally equipped to talk about the exact meaning of
74
- # 3 basic transition properties.
24
+ # Every transition has an _action_ -- the operation it represents, the of what
25
+ # happens to the marking of its codomain when it fires. With respect to the
26
+ # transition's codomain, we can talk about the _action vector_ -- Δ state of the
27
+ # codomain. For _non-stoichiometric_ transitions, this action vector is given
28
+ # as the output of the _action closure_, or (for transitions with rate) of _rate
29
+ # vector_ * Δ_time. For _stoichiometric_ transitions, this output needs to be
30
+ # additionally multiplied by the transition's _stoichiometry vector_.
31
+ #
32
+ # === Basic types of transitions
33
+ #
34
+ # We have already mentioned different types of transitions _stoichiometric_ and
35
+ # _non-stoichometric_, with or without rate... In total, there are 6 basic types
36
+ # of transitions in *YPetri*:
75
37
  #
76
- # === Meaning of the 3 basic transition properties
38
+ # * *ts* _timeless nonstoichiometric_
39
+ # * *tS* – _timeless stoichiometric_
40
+ # * *Tsr* – _timed rateless nonstoichiometric_
41
+ # * *TSr* – _timed rateless stoichiometric_
42
+ # * *sR* – _nonstoichiometric with rate_
43
+ # * *SR* – _stoichiometric with rate_
77
44
  #
78
- # ==== Stoichiometric / non-stoichiometric
79
- # * For stoichiometric transitions:
80
- # [Rate vector] is computed as rate * stoichiometry vector, or
81
- # vector] is computed a action * stoichiometry vector
82
- # * For non-stoichiometric transitions:
83
- # [Rate vector] is obtained as the rate closure result, or
84
- # [action vector] is obtained as the action closure result.
45
+ # These 6 kinds of YPetri transitions correspond to the vertices of a cube, with
46
+ # the following 3 dimensions:
47
+ #
48
+ # - *Stoichiometricity*: _stoichiometric_ (S) / _nonstoichiometric_ (s)
49
+ # - *Timedness*: _timed_ (T) / _timeless_ (t)
50
+ # - *Having rate*: _having rate_ (R) / _not having rate_, _rateless_ (r)
51
+ #
52
+ # ==== Stoichiometricity
53
+ #
54
+ # I. For stoichiometric transitions:
55
+ # 1. Either *rate vector* is computed as *rate * stoichiometry vector*,
56
+ # 2. or *action vector* is computed a *action * stoichiometry vector*.
57
+ # II. For non-stoichiometric transitions:
58
+ # 1. Either *Rate vector* is obtained as the *rate closure result*,
59
+ # 2. or *action vector* is obtained as the *action closure result*.
85
60
  #
86
- # Conclusion: stoichiometricity distinguishes <b>need to multiply the
87
- # rate/action closure result by stoichiometry</b>.
61
+ # Summary: stoichiometricity distinguishes the *need to multiply the rate/action
62
+ # closure result by stoichiometry*.
88
63
  #
89
- # ==== Having / not having rate
90
- # * For transitions with rate, the closure result has to be
91
- # multiplied by the time step duration (Δt) to get the action.
92
- # * For rateless transitions, the closure result is used as is.
64
+ # ==== Having rate
65
+ #
66
+ # I. For transitions with rate, the closure *returns the rate*. The rate has to
67
+ # be multiplied by the time step (Δt) to get the action value.
68
+ # II. For transitions without rate (_rateless transitions_), the closure result
69
+ # directly specifies the action.
93
70
  #
94
- # Conclusion: has_rate? distinguishes <b>the need to multiply the closure
95
- # result by delta time</b> - differentiability of action by time.
71
+ # Summary: Having vs. not having rate distinguishes the *need to multiply the
72
+ # closure result by Δ time* -- differentiability of the action by time.
96
73
  #
97
- # ==== Timed / Timeless
98
- # * For timed transitions, action is time-dependent. Transitions with
99
- # rate are thus always timed. In rateless transitions, timedness means
100
- # that the action closure expects time step length (delta_t) as its first
101
- # argument - its arity is thus codomain size + 1.
102
- # * For timeless transitions, action is time-independent. Timeless
103
- # transitions are necessarily also rateless. Arity of the action closure
104
- # is expected to match the domain size.
74
+ # ==== Timedness
75
+ #
76
+ # I. Timed transitions are defined as those, whose action has time as a
77
+ # parameter. Transitions with rate are thus always timed. For rateless
78
+ # transitions, being timed means that the action closure expects time step
79
+ # (Δt) as its first argument -- its arity is thus its codomain size + 1.
80
+ # II. Timeless transitions, in turn, are those, whose action is does not have
81
+ # time a parameter. Timeless transitions are necessarily also rateless.
82
+ # Arity of their action closure can be expected to match the domain size.
105
83
  #
106
- # Conclusion: Transitions with rate are always timed. In rateless
107
- # transitions, timedness distinguishes <b>the need to supply time step
108
- # duration as the first argument to the action closure</b>.
84
+ # Summary: In rateless transitions, timedness distinguishes the *need to supply
85
+ # time step duration as the first argument to the action closure*. Whereas the
86
+ # transitions with rate are always timed, and vice-versa, timeless transitions
87
+ # always rateless, there are only 6 instead of 2 ** 3 == 8 basic types.
109
88
  #
110
89
  # === Other transition types
111
90
  #
112
- # ==== Assignment transitions
113
- # Named argument :assignment_action set to true indicates that the
114
- # transitions acts by replacing the object stored as place marking by
115
- # the object supplied by the transition. (Same as in with spreadsheet
116
- # functions.) For numeric types, same effect can be achieved by subtracting
117
- # the old number from the place and subsequently adding the new value to it.
91
+ # ==== Assignment transitions (_A transitions_)
92
+ # If +:assignment_action+ is set to _true_, it indicates that the transition
93
+ # action entirely replaces the marking of its codomain with the result of its
94
+ # action closure -- like we are used to from spreadsheets. This behavior does
95
+ # not represent a truly novel type of a transition -- assignment transition is
96
+ # merely a *ts transition that cares to clear the codomain before adding the
97
+ # new value to it*. In other words, this behavior is (at least for numeric
98
+ # types) already achievable with ordinary ts transitions, and existence of
99
+ # specialized A transitions is just a convenience.
118
100
  #
119
101
  # ==== Functional / Functionless transitions
120
- # Original Petri net definition does not speak about transition "functions",
121
- # but it more or less assumes timeless action according to the stoichiometry.
122
- # So in YPetri, stoichiometric transitions with no action / rate closure
123
- # specified become functionless transitions as meant by Carl Adam Petri.
102
+ # YPetri is a domain model of _functional Petri nets_. Original Petri's
103
+ # definition does not speak about transition "functions". The transitions are
104
+ # defined as timeless and more or less assumed to be stoichiometric. Therefore,
105
+ # in +YPetri::Transition+ constructor, stoichiometric transitions with no
106
+ # function specified become functionless vanilla Petri net transitions.
107
+ #
108
+ # === "Discrete" vs. "continuous" in YPetri
109
+ #
110
+ # YPetri uses terminology of both "discrete" and "continuous" Petri nets. But
111
+ # in fact, in YPetri domain model, place marking is always considered discrete
112
+ # -- a discrete number of _tokens_, as defined by Carl Adam Petri. The meaning
113
+ # of _continuous_ in YPetri is different: A pragmatic measure of approximating
114
+ # this integer by a floating point number when the integer is so large, that the
115
+ # impact of this approximation is acceptable. The responsibility for the
116
+ # decision of how to represent the number of tokens is not a concern of the
117
+ # domain model, but only of the simulation method. Therefore, in YPetri, there
118
+ # are no _a priori_ "discrete" and "continuous" places or transitions.
119
+ #
120
+ # As for the transitions, terms _flux_ (flow), associated with continuous
121
+ # transitions, and _propensity_, associated with discrete stochastic
122
+ # transitions, are unified as _rate_. Again, the decision between "discrete"
123
+ # and "stochastic" is a concern of the simulation method, not the domain model.
124
124
  #
125
125
  class YPetri::Transition
126
126
  include NameMagic
@@ -197,14 +197,10 @@ class YPetri::Transition
197
197
  not has_rate?
198
198
  end
199
199
 
200
- # The term 'flux' (meaning flow) is associated with continuous transitions,
201
- # while term 'propensity' is used with discrete stochastic transitions.
202
- # By the design of YPetri, distinguishing between discrete and continuous
203
- # computation is the responsibility of the simulation method, considering
204
- # current marking of the transition's connectivity and quanta of its
205
- # codomain. To emphasize unity of 'flux' and 'propensity', term 'rate' is
206
- # used to represent both of them. Rate closure input arguments must
207
- # correspond to the domain places.
200
+ # In YPetri, _rate_ is a unifying term for both _flux_ and _propensity_,
201
+ # both of which are treated as aliases of _rate_. The decision between
202
+ # discrete and continuous computation is a concern of the simulation.
203
+ # Rate closure arity should correspond to the transition's domain.
208
204
  #
209
205
  attr_reader :rate_closure
210
206
  alias :rate :rate_closure
@@ -285,125 +281,12 @@ class YPetri::Transition
285
281
  def assignment_action?; @assignment_action end
286
282
  alias :assignment? :assignment_action?
287
283
 
288
- # Result of the transition's "function", regardless of the #enabled? status.
289
- #
290
- def action Δt=nil
291
- raise ArgumentError, "Δtime argument required for timed transitions!" if
292
- timed? and Δt.nil?
293
- # the code here looks awkward, because I was trying to speed it up
294
- if has_rate? then
295
- if stoichiometric? then
296
- rate = rate_closure.( *domain_marking )
297
- stoichiometry.map { |coeff| rate * coeff * Δt }
298
- else # assuming correct return value arity from the rate closure:
299
- rate_closure.( *domain_marking ).map { |e| component * Δt }
300
- end
301
- else # rateless
302
- if timed? then
303
- if stoichiometric? then
304
- rslt = action_closure.( Δt, *domain_marking )
305
- stoichiometry.map { |coeff| rslt * coeff }
306
- else
307
- action_closure.( Δt, *domain_marking ) # caveat result arity!
308
- end
309
- else # timeless
310
- if stoichiometric? then
311
- rslt = action_closure.( *domain_marking )
312
- stoichiometry.map { |coeff| rslt * coeff }
313
- else
314
- action_closure.( *domain_marking ) # caveat result arity!
315
- end
316
- end
317
- end
318
- end # action
319
-
320
284
  # Zero action
321
285
  #
322
286
  def zero_action
323
287
  codomain.map { 0 }
324
288
  end
325
289
 
326
- # Changes to the marking of codomain, as they would happen if #fire! was
327
- # called right now (ie. honoring #enabled?, but not #cocked? status.
328
- #
329
- def action_after_feasibility_check( Δt=nil )
330
- raise AErr, "Δtime argument required for timed transitions!" if
331
- timed? and Δt.nil?
332
- act = Array( action Δt )
333
- # Assignment actions are always feasible - no need to check:
334
- return act if assignment?
335
- # check if the marking after the action would still be positive
336
- enabled = codomain
337
- .zip( act )
338
- .all? { |place, change| place.marking.to_f >= -change.to_f }
339
- if enabled then act else
340
- raise "firing of #{self}#{ Δt ? ' with Δtime %s' % Δt : '' } " +
341
- "would result in negative marking"
342
- zero_action
343
- end
344
- # LATER: This use of #zip here should be avoided for speed
345
- end
346
-
347
- # Applies transition's action (adding/taking tokens) on its downstream
348
- # places (aka. domain places). If the transition is timed, delta time has
349
- # to be supplied as argument. In order for this method to work, the
350
- # transition has to be cocked (#cock method), and firing uncocks the
351
- # transition, so it has to be cocked again before it can be fired for
352
- # the second time. If the transition is not cocked, this method has no
353
- # effect.
354
- #
355
- def fire( Δt=nil )
356
- raise ArgumentError, "Δtime argument required for timed transitions!" if
357
- timed? and Δt.nil?
358
- return false unless cocked?
359
- uncock
360
- fire! Δt
361
- return true
362
- end
363
-
364
- # Fires the transition just like #fire method, but disregards the cocked /
365
- # uncocked state of the transition.
366
- #
367
- def fire!( Δt=nil )
368
- raise ArgumentError, "Δt required for timed transitions!" if
369
- Δt.nil? if timed?
370
- try "to fire" do
371
- if assignment_action? then
372
- note has: "assignment action"
373
- act = note "action", is: Array( action( Δt ) )
374
- codomain.each_with_index do |place, i|
375
- "place #{place}".try "to assign marking #{i}" do
376
- place.marking = act[i]
377
- end
378
- end
379
- else
380
- act = note "action", is: action_after_feasibility_check( Δt )
381
- codomain.each_with_index do |place, i|
382
- "place #{place}".try "to assign marking #{i}" do
383
- place.add act[i]
384
- end
385
- end
386
- end
387
- end
388
- return nil
389
- end
390
-
391
- # Sanity of execution is ensured by Petri's notion of transitions being
392
- # "enabled" if and only if the intended action can immediately take
393
- # place without getting places into forbidden state (negative marking).
394
- #
395
- def enabled?( Δt=nil )
396
- fail ArgumentError, "Δtime argument compulsory for timed transitions!" if
397
- timed? && Δt.nil?
398
- codomain.zip( action Δt ).all? do |place, change|
399
- begin
400
- place.guard.( place.marking + change )
401
- rescue YPetri::GuardError
402
- false
403
- end
404
- end
405
- end
406
-
407
290
  # def lock
408
291
  # # LATER
409
292
  # end
@@ -445,7 +328,7 @@ class YPetri::Transition
445
328
  # Conversion to a string.
446
329
  #
447
330
  def to_s
448
- "#<Transition: %s >" %
331
+ "#<Transition: %s>" %
449
332
  "#{name.nil? ? '' : '%s ' % name }(#{basic_type}%s)%s" %
450
333
  [ "#{assignment_action? ? ' Assign.' : ''}",
451
334
  "#{name.nil? ? ' id:%s' % object_id : ''}" ]
@@ -1,4 +1,4 @@
1
1
  module YPetri
2
- VERSION = "2.0.7"
2
+ VERSION = "2.0.14.p1"
3
3
  DEBUG = false
4
4
  end
data/lib/y_petri.rb CHANGED
@@ -7,7 +7,7 @@ require 'y_support/respond_to'
7
7
  require 'y_support/name_magic'
8
8
  require 'y_support/unicode'
9
9
  require 'y_support/typing'
10
- require 'y_support/conscience'; include Conscience
10
+ require 'y_support/try'
11
11
  require 'y_support/core_ext/hash'
12
12
  require 'y_support/core_ext/array'
13
13
  require 'y_support/stdlib_ext/matrix'
@@ -0,0 +1,30 @@
1
+ #! /usr/bin/ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'minitest/spec'
5
+ require 'minitest/autorun'
6
+ require_relative '../../lib/y_petri' # tested component itself
7
+ # require 'y_petri'
8
+ # require 'sy'
9
+
10
+ describe "Basic use of TimedSimulation" do
11
+ before do
12
+ @m = YPetri::Manipulator.new
13
+ @m.Place( name: "A", default_marking: 0.5 )
14
+ @m.Place( name: "B", default_marking: 0.5 )
15
+ @m.Transition( name: "A_pump",
16
+ stoichiometry: { A: -1 },
17
+ rate: proc { 0.005 } )
18
+ @m.Transition( name: "B_decay",
19
+ stoichiometry: { B: -1 },
20
+ rate: 0.05 )
21
+ end
22
+
23
+ it "should work" do
24
+ @m.net.must_be_kind_of ::YPetri::Net
25
+ @m.run!
26
+ @m.simulation.must_be_kind_of ::YPetri::TimedSimulation
27
+ @m.plot_state
28
+ sleep 3
29
+ end
30
+ end
@@ -0,0 +1,132 @@
1
+ #! /usr/bin/ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'minitest/spec'
5
+ require 'minitest/autorun'
6
+ require_relative '../../lib/y_petri' # tested component itself
7
+ # require 'y_petri'
8
+ # require 'sy'
9
+
10
+ describe "Simplified dTTP pathway used for demo with Dr. Chang" do
11
+ before do
12
+ @m = YPetri::Manipulator.new
13
+ Cytoplasm_volume_in_litres = 5.0e-11
14
+ NA = 6.022e23
15
+ Pieces_per_micromolar = NA / 1_000_000 * Cytoplasm_volume_in_litres
16
+ @m.set_step 60
17
+ @m.set_sampling 300
18
+ @m.set_target_time 60 * 60 * 2
19
+ AMP = @m.Place( name: :AMP, m!: 8695.0 )
20
+ ADP = @m.Place( name: :ADP, m!: 6521.0 )
21
+ ATP = @m.Place( name: :ATP, m!: 3152.0 )
22
+ Deoxycytidine = @m.Place( name: :Deoxycytidine, m!: 0.5 )
23
+ DeoxyCTP = @m.Place( name: :DeoxyCTP, m!: 1.0 )
24
+ DeoxyGMP = @m.Place( name: :DeoxyGMP, m!: 1.0 )
25
+ UMP_UDP_pool = @m.Place( name: :UMP_UDP_pool, m!: 2737.0 )
26
+ DeoxyUMP_DeoxyUDP_pool = @m.Place( name: :DeoxyUMP_DeoxyUDP_pool, m!: 0.0 )
27
+ DeoxyTMP = @m.Place( name: :DeoxyTMP, m!: 3.3 )
28
+ DeoxyTDP_DeoxyTTP_pool = @m.Place( name: :DeoxyTDP_DeoxyTTP_pool, m!: 5.0 )
29
+ Thymidine = @m.Place( name: :Thymidine, m!: 0.5 )
30
+ TK1 = @m.Place( name: :TK1, m!: 100_000 )
31
+ TYMS = @m.Place( name: :TYMS, m!: 100_000 )
32
+ RNR = @m.Place( name: :RNR, m!: 100_000 )
33
+ TMPK = @m.Place( name: :TMPK, m!: 100_000 )
34
+ TK1_kDa = 24.8
35
+ TYMS_kDa = 66.0
36
+ RNR_kDa = 140.0
37
+ TMPK_kDa = 50.0
38
+ TK1_a = 5.40
39
+ TYMS_a = 3.80
40
+ RNR_a = 1.00
41
+ TMPK_a = 0.83
42
+ @m.clamp AMP: 8695.0, ADP: 6521.0, ATP: 3152.0
43
+ @m.clamp Deoxycytidine: 0.5, DeoxyCTP: 1.0, DeoxyGMP: 1.0
44
+ @m.clamp Thymidine: 0.5
45
+ @m.clamp UMP_UDP_pool: 2737.0
46
+ # Functions
47
+ Vmax_per_minute_per_enzyme_molecule =
48
+ lambda { |enzyme_specific_activity_in_micromol_per_minute_per_mg,
49
+ enzyme_molecular_mass_in_kDa|
50
+ enzyme_specific_activity_in_micromol_per_minute_per_mg *
51
+ enzyme_molecular_mass_in_kDa }
52
+ Vmax_per_minute =
53
+ lambda { |specific_activity, kDa, enzyme_molecules_per_cell|
54
+ Vmax_per_minute_per_enzyme_molecule.( specific_activity, kDa ) *
55
+ enzyme_molecules_per_cell }
56
+ Vmax_per_second =
57
+ lambda { |specific_activity, kDa, enzyme_molecules_per_cell|
58
+ Vmax_per_minute.( specific_activity,
59
+ kDa,
60
+ enzyme_molecules_per_cell ) / 60 }
61
+ Km_reduced =
62
+ lambda { |km, ki_hash={}|
63
+ ki_hash.map { |concentration, ci_Ki|
64
+ concentration / ci_Ki
65
+ }.reduce( 1, :+ ) * km }
66
+ Occupancy =
67
+ lambda { |concentration, reactant_Km, compet_inh_w_Ki_hash={}|
68
+ concentration / ( concentration +
69
+ Km_reduced.( reactant_Km,
70
+ compet_inh_w_Ki_hash ) ) }
71
+ MM_with_inh_micromolars_per_second =
72
+ lambda { |reactant_concentration,
73
+ enzyme_specific_activity,
74
+ enzyme_mass_in_kDa,
75
+ enzyme_molecules_per_cell,
76
+ reactant_Km,
77
+ competitive_inh_w_Ki_hash={}|
78
+ Vmax_per_second.( enzyme_specific_activity,
79
+ enzyme_mass_in_kDa,
80
+ enzyme_molecules_per_cell ) *
81
+ Occupancy.( reactant_concentration,
82
+ reactant_Km,
83
+ competitive_inh_w_Ki_hash ) }
84
+ MMi = MM_with_inh_micromolars_per_second
85
+ TK1_Thymidine_Km = 5.0
86
+ TYMS_DeoxyUMP_Km = 2.0
87
+ RNR_UDP_Km = 1.0
88
+ DNA_creation_speed = 3_000_000_000 / ( 12 * 3600 )
89
+ TMPK_DeoxyTMP_Km = 12.0
90
+
91
+ # transitions
92
+ @m.Transition name: :TK1_Thymidine_DeoxyTMP,
93
+ domain: [ Thymidine, TK1, DeoxyTDP_DeoxyTTP_pool, DeoxyCTP, Deoxycytidine, AMP, ADP, ATP ],
94
+ stoichiometry: { Thymidine: -1, DeoxyTMP: 1 },
95
+ rate: proc { |rc, e, pool1, ci2, ci3, master1, master2, master3|
96
+ ci1 = pool1 * master3 / ( master2 + master3 )
97
+ MMi.( rc, TK1_a, TK1_kDa, e, TK1_Thymidine_Km,
98
+ ci1 => 13.5, ci2 => 0.8, ci3 => 40.0 ) }
99
+ @m.Transition name: :TYMS_DeoxyUMP_DeoxyTMP,
100
+ domain: [ DeoxyUMP_DeoxyUDP_pool, TYMS, AMP, ADP, ATP ],
101
+ stoichiometry: { DeoxyUMP_DeoxyUDP_pool: -1, DeoxyTMP: 1 },
102
+ rate: proc { |pool, e, master1, master2, master3|
103
+ rc = pool * master2 / ( master1 + master2 )
104
+ MMi.( rc, TYMS_a, TYMS_kDa, e, TYMS_DeoxyUMP_Km ) }
105
+ @m.Transition name: :RNR_UDP_DeoxyUDP,
106
+ domain: [ UMP_UDP_pool, RNR, DeoxyUMP_DeoxyUDP_pool, AMP, ADP, ATP ],
107
+ stoichiometry: { UMP_UDP_pool: -1, DeoxyUMP_DeoxyUDP_pool: 1 },
108
+ rate: proc { |pool, e, master1, master2, master3|
109
+ rc = pool * master2 / ( master1 + master2 )
110
+ MMi.( rc, RNR_a, RNR_kDa, e, RNR_UDP_Km ) }
111
+ @m.Transition name: :DNA_polymerase_consumption_of_DeoxyTTP,
112
+ stoichiometry: { DeoxyTDP_DeoxyTTP_pool: -1 },
113
+ rate: proc { DNA_creation_speed / 4 }
114
+ @m.Transition name: :TMPK_DeoxyTMP_DeoxyTDP,
115
+ domain: [ DeoxyTMP, TMPK, ADP,
116
+ DeoxyTDP_DeoxyTTP_pool,
117
+ DeoxyGMP, AMP, ATP ],
118
+ stoichiometry: { DeoxyTMP: -1, TMPK: 0, DeoxyTDP_DeoxyTTP_pool: 1 },
119
+ rate: proc { |rc, e, ci1, pool, ci4, master1, master3|
120
+ master2 = ci1
121
+ ci2 = pool * master2 / ( master2 + master3 )
122
+ ci3 = pool * master3 / ( master2 + master3 )
123
+ MMi.( rc, TMPK_a, TMPK_kDa, e, TMPK_DeoxyTMP_Km,
124
+ ci1 => 250.0, ci2 => 30.0, ci3 => 750, ci4 => 117 ) }
125
+ end
126
+
127
+ it "should work" do
128
+ @m.run!
129
+ @m.plot_state
130
+ sleep 3
131
+ end
132
+ end