y_petri 2.0.7 → 2.0.14.p1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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