y_petri 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/lib/y_petri/demonstrator.rb +164 -0
- data/lib/y_petri/demonstrator_2.rb +176 -0
- data/lib/y_petri/demonstrator_3.rb +150 -0
- data/lib/y_petri/demonstrator_4.rb +217 -0
- data/lib/y_petri/manipulator.rb +598 -0
- data/lib/y_petri/net.rb +458 -0
- data/lib/y_petri/place.rb +189 -0
- data/lib/y_petri/simulation.rb +1313 -0
- data/lib/y_petri/timed_simulation.rb +281 -0
- data/lib/y_petri/transition.rb +921 -0
- data/lib/y_petri/version.rb +3 -0
- data/lib/y_petri/workspace/instance_methods.rb +254 -0
- data/lib/y_petri/workspace/parametrized_subclassing.rb +26 -0
- data/lib/y_petri/workspace.rb +16 -0
- data/lib/y_petri.rb +141 -0
- data/test/simple_manual_examples.rb +28 -0
- data/test/y_petri_graph.png +0 -0
- data/test/y_petri_test.rb +1521 -0
- data/y_petri.gemspec +21 -0
- metadata +112 -0
@@ -0,0 +1,1521 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
#encoding: 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
|
+
|
9
|
+
# require 'sy'
|
10
|
+
|
11
|
+
include Pyper if require 'pyper'
|
12
|
+
|
13
|
+
# **************************************************************************
|
14
|
+
# Test of Place class, part I.
|
15
|
+
# **************************************************************************
|
16
|
+
#
|
17
|
+
describe ::YPetri::Place do
|
18
|
+
before do
|
19
|
+
# skip "to speed up testing"
|
20
|
+
@pç = pç = Class.new ::YPetri::Place
|
21
|
+
@p = pç.new! default_marking: 3.2,
|
22
|
+
marking: 1.1,
|
23
|
+
quantum: 0.1,
|
24
|
+
name: "P1"
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "place behavior" do
|
28
|
+
before do
|
29
|
+
@p.m = 1.1
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should have constant magic included" do
|
33
|
+
assert_respond_to @p, :name
|
34
|
+
assert_equal @p.name, :P1
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have own marking and be able to update it" do
|
38
|
+
assert_equal 1.1, @p.marking
|
39
|
+
assert_equal 0.1, @p.quantum
|
40
|
+
assert_equal :P1, @p.name
|
41
|
+
@p.add 1
|
42
|
+
assert_equal 2.1, @p.value # alias for #marking
|
43
|
+
@p.subtract 0.5
|
44
|
+
assert_equal 1.6, @p.m
|
45
|
+
@p.reset_marking
|
46
|
+
assert_equal 3.2, @p.marking
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should respond to the arc getters" do
|
50
|
+
# #action_arcs & aliases
|
51
|
+
assert_equal [], @p.upstream_arcs
|
52
|
+
assert_equal [], @p.upstream_transitions
|
53
|
+
assert_equal [], @p.ϝ
|
54
|
+
# #test_arcs & aliases
|
55
|
+
assert_equal [], @p.downstream_arcs
|
56
|
+
assert_equal [], @p.downstream_transitions
|
57
|
+
# #arcs & aliasesnn
|
58
|
+
assert_equal [], @p.arcs
|
59
|
+
assert_equal [], @p.connectivity
|
60
|
+
# #precedents & aliases
|
61
|
+
assert_equal [], @p.precedents
|
62
|
+
assert_equal [], @p.upstream_places
|
63
|
+
# #dependents & aliases
|
64
|
+
assert_equal [], @p.dependents
|
65
|
+
assert_equal [], @p.downstream_places
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should respond to register and fire conn. transitions methods" do
|
69
|
+
assert_respond_to @p, :fire_upstream!
|
70
|
+
assert_respond_to @p, :fire_downstream!
|
71
|
+
assert_respond_to @p, :fire_upstream_recursively
|
72
|
+
assert_respond_to @p, :fire_downstream_recursively
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# **************************************************************************
|
78
|
+
# Test of Transition class, part I.
|
79
|
+
# **************************************************************************
|
80
|
+
#
|
81
|
+
describe ::YPetri::Transition do
|
82
|
+
before do
|
83
|
+
# skip "to speed up testing"
|
84
|
+
@ç = ç = Class.new ::YPetri::Transition
|
85
|
+
@pç = pç = Class.new ::YPetri::Place
|
86
|
+
[ ç, pç ].each { |ç|
|
87
|
+
ç.class_exec {
|
88
|
+
define_method :Place do pç end
|
89
|
+
define_method :Transition do ç end
|
90
|
+
private :Place, :Transition
|
91
|
+
}
|
92
|
+
}
|
93
|
+
@p1 = pç.new default_marking: 1.0
|
94
|
+
@p2 = pç.new default_marking: 2.0
|
95
|
+
@p3 = pç.new default_marking: 3.0
|
96
|
+
@p4 = pç.new default_marking: 4.0
|
97
|
+
@p5 = pç.new default_marking: 5.0
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "1. timeless nonstoichiometric (ts) transitions" do
|
101
|
+
|
102
|
+
# Note that timeless nonstoichiometric transitions require a function
|
103
|
+
# block, and thus are always functional
|
104
|
+
|
105
|
+
before do
|
106
|
+
@t1 = @ç.new codomain: [ @p1, @p3 ], domain: @p2, action: λ { |a| [ a, a ] }
|
107
|
+
# saying that the trans. is timed saves the day here:
|
108
|
+
@t2 = @ç.new codomain: [ @p1, @p3 ], action: λ { |t| [ t, t ] }, timed: true
|
109
|
+
# Only with domain is 1-ary closure allowed to be timeless:
|
110
|
+
@t3 = @ç.new codomain: [ @p1, @p3 ], action: λ { |t| [ t, t ] }, timed: false, domain: [ @p2 ]
|
111
|
+
# With nullary action closure, timeless is implied, so this is allowed
|
112
|
+
@t4 = @ç.new action: λ { [ 0.5, 0.5 ] }, codomain: [ @p1, @p3 ]
|
113
|
+
# ... also for stoichiometric variety
|
114
|
+
@t5 = @ç.new action: λ { 0.5 }, codomain: [ @p1, @p3 ], s: [ 1, 1 ]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should raise errors for bad parameters" do
|
118
|
+
# omitting the domain should raise ArgumentError about too much ambiguity:
|
119
|
+
assert_raises AErr do @ç.new codomain: [ @p1, @p3 ], action: λ { |t| [ t, t ] } end
|
120
|
+
# saying that the transition is timeless points to a conflict:
|
121
|
+
assert_raises AErr do @ç.new codomain: [ @p1, @p3 ], action: λ { |t| [ t, t ] }, timeless: true end
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should initi and perform" do
|
125
|
+
assert_equal [ @p2 ], @t1.domain
|
126
|
+
assert_equal [ @p1, @p3 ], @t1.action_arcs
|
127
|
+
assert @t1.functional?
|
128
|
+
assert @t1.timeless?
|
129
|
+
assert @t2.timed?
|
130
|
+
assert [@t3, @t4, @t5].all? { |t| t.timeless? }
|
131
|
+
assert @t2.rateless?
|
132
|
+
# that's enough, now let's flex them:
|
133
|
+
@t1.fire!
|
134
|
+
assert_equal [3, 5], [ @p1.marking, @p3.marking ]
|
135
|
+
@t3.fire!
|
136
|
+
assert_equal [5, 7], [ @p1.marking, @p3.marking ]
|
137
|
+
@t4.fire!
|
138
|
+
assert_equal [5.5, 7.5], [ @p1.marking, @p3.marking ]
|
139
|
+
@t5.fire!
|
140
|
+
assert_equal [6, 8], [ @p1.marking, @p3.marking ]
|
141
|
+
# now t2 for firing requires delta time
|
142
|
+
@t2.fire! 1
|
143
|
+
assert_equal [7, 9], [ @p1.marking, @p3.marking ]
|
144
|
+
@t2.fire! 0.1
|
145
|
+
assert_equal [7.1, 9.1], [@p1.marking, @p3.marking ]
|
146
|
+
# let's change @p2 marking
|
147
|
+
@p2.marking = 0.1
|
148
|
+
@t1.fire!
|
149
|
+
assert_in_epsilon 7.2, @p1.marking, 1e-9
|
150
|
+
assert_in_epsilon 9.2, @p3.marking, 1e-9
|
151
|
+
# let's test #domain_marking, #codomain_marking, #zero_action
|
152
|
+
assert_equal [ @p1.marking, @p3.marking ], @t1.codomain_marking
|
153
|
+
assert_equal [ @p2.marking ], @t1.domain_marking
|
154
|
+
assert_equal [ 0, 0 ], @t1.zero_action
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "2. timed rateless non-stoichiometric (Tsr) transitions" do
|
159
|
+
#LATER: To save time, I omit the full test suite.
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "3. timeless stoichiometric (tS) transitions" do
|
163
|
+
describe "functionless tS transitions" do
|
164
|
+
|
165
|
+
# For transitions with no function given (ie. functionless), it is
|
166
|
+
# required that their stoichiometric vector be given and their action
|
167
|
+
# closure is then automatically generated from the stoichio. vector
|
168
|
+
|
169
|
+
before do
|
170
|
+
# timeless transition with stoichiometric vector only, as hash
|
171
|
+
@ftS1 = @ç.new stoichiometry: { @p1 => 1 }
|
172
|
+
# timeless transition with stoichiometric vector as array + codomain
|
173
|
+
@ftS2 = @ç.new stoichiometry: 1, codomain: @p1
|
174
|
+
# :stoichiometric_vector is aliased as :sv
|
175
|
+
@ftS3 = @ç.new s: 1, codomain: @p1
|
176
|
+
# :codomain is aliased as :action_arcs
|
177
|
+
@ftS4 = @ç.new s: 1, action_arcs: @p1
|
178
|
+
# dropping of square brackets around size 1 vectors is optional
|
179
|
+
@ftS5 = @ç.new s: [ 1 ], downstream: [ @p1 ]
|
180
|
+
# another alias for :codomain is :downstream_places
|
181
|
+
@ftS6 = @ç.new s: [ 1 ], downstream_places: [ @p1 ]
|
182
|
+
# and now, all of the above transitions...
|
183
|
+
@tt = @ftS1, @ftS2, @ftS3, @ftS4, @ftS5, @ftS6
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should work" do
|
187
|
+
# ...should be the same, having a single action arc:
|
188
|
+
assert @tt.all?{ |t| t.action_arcs == [ @p1 ] }
|
189
|
+
# timeless:
|
190
|
+
assert @tt.all?{ |t| t.timeless? }
|
191
|
+
# rateless:
|
192
|
+
assert @tt.all?{ |t| t.rateless? }
|
193
|
+
assert @tt.all?{ |t| not t.has_rate? }
|
194
|
+
# no assignment action
|
195
|
+
assert @tt.all?{ |t| not t.assignment_action? }
|
196
|
+
# not considered functional
|
197
|
+
assert @tt.all?{ |t| t.functionless? }
|
198
|
+
assert @tt.all?{ |t| not t.functional? }
|
199
|
+
# and having nullary action closure
|
200
|
+
assert @tt.all?{ |t| t.action_closure.arity == 0 }
|
201
|
+
# the transitions should be able to #fire!
|
202
|
+
@ftS1.fire!
|
203
|
+
# the difference is apparent: marking of place @p1 jumped to 2:
|
204
|
+
assert_equal 2, @p1.marking
|
205
|
+
# but should not #fire (no exclamation mark) unless cocked
|
206
|
+
assert !@ftS1.cocked?
|
207
|
+
@ftS1.fire
|
208
|
+
assert_equal 2, @p1.marking
|
209
|
+
# cock it
|
210
|
+
@ftS1.cock
|
211
|
+
assert @ftS1.cocked?
|
212
|
+
# uncock again, just to test cocking
|
213
|
+
@ftS1.uncock
|
214
|
+
assert @ftS1.uncocked?
|
215
|
+
@ftS1.cock
|
216
|
+
assert !@ftS1.uncocked?
|
217
|
+
@ftS1.fire
|
218
|
+
assert_equal 3, @p1.marking
|
219
|
+
# enough playing, we'll reset @p1 marking
|
220
|
+
@p1.reset_marking
|
221
|
+
assert_equal 1, @p1.marking
|
222
|
+
# #action
|
223
|
+
assert @tt.all?{ |t| t.action == [ 1 ] }
|
224
|
+
# #zero_action
|
225
|
+
assert @tt.all?{ |t| t.zero_action }
|
226
|
+
# #action_after_feasibility_check
|
227
|
+
assert @tt.all?{ |t| t.action_after_feasibility_check == [ 1 ] }
|
228
|
+
# #domain_marking
|
229
|
+
assert @tt.all?{ |t| t.domain_marking == [] }
|
230
|
+
# #codomain_marking
|
231
|
+
assert @tt.all?{ |t| t.codomain_marking == [ @p1.marking ] }
|
232
|
+
# #enabled?
|
233
|
+
assert @tt.all?{ |t| t.enabled? == true }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "functional tS transitions" do
|
238
|
+
|
239
|
+
# If function block is supplied to tS transitions, it governs
|
240
|
+
# their action based on marking of the domain places.
|
241
|
+
|
242
|
+
before do
|
243
|
+
# stoichiometric vector given as hash
|
244
|
+
@FtS1 = @ç.new action_closure: λ { 1 }, s: { @p1 => 1 }
|
245
|
+
# instead of :action_closure, just saying :action is enough
|
246
|
+
@FtS2 = @ç.new action: λ { 1 }, s: { @p1 => 1 }
|
247
|
+
# stoichiometric vector given as coeff. array + codomain
|
248
|
+
@FtS3 = @ç.new s: 1, codomain: @p1, action: λ { 1 }
|
249
|
+
# while saying timed: false and timeless: true should be ok
|
250
|
+
@FtS4 = @ç.new s: { @p1 => 1 }, action: λ { 1 }, timed: false
|
251
|
+
@FtS5 = @ç.new s: { @p1 => 1 }, action: λ { 1 }, timeless: true
|
252
|
+
# even both are ok
|
253
|
+
@FtS6 = @ç.new s: { @p1 => 1 }, action: λ { 1 }, timed: false, timeless: true
|
254
|
+
@tt = @FtS1, @FtS2, @FtS3, @FtS4, @FtS5, @FtS6
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should raise errors for bad parameters" do
|
258
|
+
# saying timed: true should raise a complaint:
|
259
|
+
assert_raises AErr do @ç.new sv: { @p1 => 1 }, action: λ{ 1 }, timed: true end
|
260
|
+
# same for saying timeless: false
|
261
|
+
assert_raises AErr do
|
262
|
+
@ç.new sv: { @p1 => 1 }, action: λ{ 1 }, timeless: false end
|
263
|
+
# while conflicting values will raise error
|
264
|
+
assert_raises AErr do
|
265
|
+
@ç.new sv: { @p1 => 1 }, action: λ { 1 }, timeless: true, timed: true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should init and perform" do
|
270
|
+
assert @tt.all?{ |t| t.action_arcs == [ @p1 ] }
|
271
|
+
assert @tt.all?{ |t| t.timeless? }
|
272
|
+
assert @tt.all?{ |t| not t.has_rate? }
|
273
|
+
assert @tt.all?{ |t| t.rateless? }
|
274
|
+
assert @tt.all?{ |t| not t.assignment_action? }
|
275
|
+
assert @tt.all?{ |t| not t.functionless? }
|
276
|
+
assert @tt.all?{ |t| t.functional? }
|
277
|
+
# and having nullary action closure
|
278
|
+
assert @tt.all?{ |t| t.action_closure.arity == 0 }
|
279
|
+
# the transitions should be able to #fire!
|
280
|
+
@FtS1.fire!
|
281
|
+
# no need for more testing here
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "4. timed rateless stoichiometric (TSr) transitions" do
|
287
|
+
|
288
|
+
# Rateless stoichiometric transitions have action closure, and they
|
289
|
+
# require a function block, and thus are always functional. Their
|
290
|
+
# function block must take Δt as its first argument.
|
291
|
+
|
292
|
+
#LATER: To save time, I omit the tests of TSr transitions for now.
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "5. nonstoichiometric transitions with rate (sR transitions)" do
|
296
|
+
|
297
|
+
# They require a function block with arity equal to their domain, whose
|
298
|
+
# output is an array of rates of the size equal to that of codomain.
|
299
|
+
|
300
|
+
#LATER: To save time, I omit the full test suite.
|
301
|
+
end
|
302
|
+
|
303
|
+
describe "6. stoichiometric transitions with rate (SR transitions)" do
|
304
|
+
before do
|
305
|
+
# now this should give standard mass action by magic:
|
306
|
+
@SR1 = @ç.new s: { @p1 => -1, @p2 => -1, @p4 => 1 }, flux_closure: 0.1
|
307
|
+
# while this has custom flux closure
|
308
|
+
@SR2 = @ç.new s: { @p1 => -1, @p3 => 1 }, flux_closure: λ { |a| a * 0.5 }
|
309
|
+
# while this one even has domain specified:
|
310
|
+
@SR3 = @ç.new s: { @p1 => -1, @p2 => -1, @p4 => 1 }, upstream_arcs: @p3, flux: λ { |a| a * 0.5 }
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should init and work" do
|
314
|
+
assert_equal true, @SR1.has_rate?
|
315
|
+
assert_equal [ @p1, @p2 ], @SR1.upstream_arcs
|
316
|
+
assert_equal [ @p1, @p2, @p4 ], @SR1.action_arcs
|
317
|
+
assert_equal [ @p1 ], @SR2.domain
|
318
|
+
assert_equal [ @p1, @p3 ], @SR2.action_arcs
|
319
|
+
assert_equal [ @p3 ], @SR3.domain
|
320
|
+
assert_equal [ @p1, @p2, @p4 ], @SR3.action_arcs
|
321
|
+
# and flex them
|
322
|
+
@SR1.fire! 1.0
|
323
|
+
assert_equal [ 0.8, 1.8, 4.2 ], [ @p1, @p2, @p4 ].map( &:marking )
|
324
|
+
@SR2.fire! 1.0
|
325
|
+
assert_equal [ 0.4, 3.4 ], [ @p1, @p3 ].map( &:marking )
|
326
|
+
# the action t3 cannot fire with delta time 1.0
|
327
|
+
assert_raises RuntimeError do @SR3.fire! 1.0 end
|
328
|
+
assert_equal [ 0.4, 1.8, 3.4, 4.2 ], [ @p1, @p2, @p3, @p4 ].map( &:marking )
|
329
|
+
# but it can fire with eg. delta time 0.1
|
330
|
+
@SR3.fire! 0.1
|
331
|
+
assert_in_epsilon 0.23, @p1.marking, 1e-15
|
332
|
+
assert_in_epsilon 1.63, @p2.marking, 1e-15
|
333
|
+
assert_in_epsilon 3.4, @p3.marking, 1e-15
|
334
|
+
assert_in_epsilon 4.37, @p4.marking, 1e-15
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
# **************************************************************************
|
341
|
+
# Test of mutual knowedge of upstream/downstream arcs of places/transitions.
|
342
|
+
# **************************************************************************
|
343
|
+
#
|
344
|
+
describe "upstream and downstream reference mτs of places and transitions" do
|
345
|
+
before do
|
346
|
+
# skip "to speed up testing"
|
347
|
+
@tç = tç = Class.new ::YPetri::Transition
|
348
|
+
@pç = pç = Class.new ::YPetri::Place
|
349
|
+
[ tç, pç ].each { |ç|
|
350
|
+
ç.class_exec {
|
351
|
+
define_method :Place do pç end
|
352
|
+
define_method :Transition do tç end
|
353
|
+
private :Place, :Transition
|
354
|
+
}
|
355
|
+
}
|
356
|
+
@a = @pç.new( dflt_m: 1.0 )
|
357
|
+
@b = @pç.new( dflt_m: 2.0 )
|
358
|
+
@c = @pç.new( dflt_m: 3.0 )
|
359
|
+
end
|
360
|
+
|
361
|
+
describe "Place" do
|
362
|
+
it "should have #register_ustream/downstream_transition methods" do
|
363
|
+
@t1 = @tç.new s: {}
|
364
|
+
@a.instance_variable_get( :@upstream_arcs ).must_equal []
|
365
|
+
@a.instance_variable_get( :@downstream_arcs ).must_equal []
|
366
|
+
@a.send :register_upstream_transition, @t1
|
367
|
+
@a.instance_variable_get( :@upstream_arcs ).must_equal [ @t1 ]
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
describe "upstream and downstream reference methods" do
|
372
|
+
before do
|
373
|
+
@t1 = @tç.new s: { @a => -1, @b => 1 }, rate: 1
|
374
|
+
end
|
375
|
+
|
376
|
+
it "should show on the referencers" do
|
377
|
+
@a.upstream_arcs.must_equal [ @t1 ]
|
378
|
+
@b.downstream_arcs.must_equal [ ]
|
379
|
+
@b.ϝ.must_equal [ @t1 ]
|
380
|
+
@t1.upstream_arcs.must_equal [ @a ]
|
381
|
+
@t1.action_arcs.must_equal [ @a, @b ]
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
describe "assignment action transitions" do
|
386
|
+
before do
|
387
|
+
@p = @pç.new default_marking: 1.0
|
388
|
+
@t = @tç.new codomain: @p, action: λ { 1 }, assignment_action: true
|
389
|
+
end
|
390
|
+
|
391
|
+
it "should work" do
|
392
|
+
@p.marking = 3
|
393
|
+
assert_equal 3, @p.marking
|
394
|
+
assert @t.assignment_action?
|
395
|
+
assert_equal @t.domain, []
|
396
|
+
assert_equal 0, @t.action_closure.arity
|
397
|
+
@t.fire!
|
398
|
+
assert_equal 1, @p.marking
|
399
|
+
end
|
400
|
+
end # context assignment action transiotions
|
401
|
+
end
|
402
|
+
|
403
|
+
|
404
|
+
# **************************************************************************
|
405
|
+
# Test of Net class.
|
406
|
+
# **************************************************************************
|
407
|
+
#
|
408
|
+
describe ::YPetri::Net do
|
409
|
+
before do
|
410
|
+
# skip "to speed up testing"
|
411
|
+
@tç = tç = Class.new ::YPetri::Transition
|
412
|
+
@pç = pç = Class.new ::YPetri::Place
|
413
|
+
@nç = nç = Class.new ::YPetri::Net
|
414
|
+
[ tç, pç, nç ].each { |ç|
|
415
|
+
ç.class_exec {
|
416
|
+
define_method :Place do pç end
|
417
|
+
define_method :Transition do tç end
|
418
|
+
define_method :Net do nç end
|
419
|
+
private :Place, :Transition, :Net
|
420
|
+
}
|
421
|
+
}
|
422
|
+
@p1 = pç.new ɴ: "A", quantum: 0.1, marking: 1.1
|
423
|
+
@p2 = pç.new ɴ: "B", quantum: 0.1, marking: 2.2
|
424
|
+
@p3 = pç.new ɴ: "C", quantum: 0.1, marking: 3.3
|
425
|
+
@net = nç.new
|
426
|
+
[ @p1, @p2, @p3 ].each { |p| @net.include_place! p }
|
427
|
+
@p_not_included = pç.new ɴ: "X", m: 0
|
428
|
+
end
|
429
|
+
|
430
|
+
describe "net of 3 places and no transitions" do
|
431
|
+
before do
|
432
|
+
@p1.m = 1.1
|
433
|
+
@p2.m = 2.2
|
434
|
+
@p3.m = 3.3
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should expose its elements" do
|
438
|
+
assert_equal [@p1, @p2, @p3], @net.places
|
439
|
+
assert_equal [:A, :B, :C], @net.pp
|
440
|
+
assert_equal [], @net.transitions
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should expose transition groups" do
|
444
|
+
assert_equal [], @net.transitions_with_rate
|
445
|
+
assert_equal [], @net.rateless_transitions
|
446
|
+
assert_equal [], @net.transitions_without_rate
|
447
|
+
assert_equal [], @net.stoichiometric_transitions
|
448
|
+
assert_equal [], @net.nonstoichiometric_transitions
|
449
|
+
end
|
450
|
+
|
451
|
+
it "should tell its qualities" do
|
452
|
+
assert_equal true, @net.functional?
|
453
|
+
assert_equal true, @net.timed?
|
454
|
+
assert @net.include?( @p1 ) && !@net.include?( nil )
|
455
|
+
end
|
456
|
+
|
457
|
+
it "should have 'standard equipment' methods" do
|
458
|
+
assert @net == @net.dup
|
459
|
+
assert @net.inspect.start_with? "#<Net:"
|
460
|
+
assert @net.include?( @p1 )
|
461
|
+
assert ! @net.include?( @p_not_included )
|
462
|
+
begin
|
463
|
+
@net.exclude_place! @p_not_included
|
464
|
+
@net.include_transition! YPetri::Transition.new( s: { @p_not_included => -1 } )
|
465
|
+
flunk "Attempt to include illegal transition fails to raise"
|
466
|
+
rescue; end
|
467
|
+
end
|
468
|
+
|
469
|
+
describe "plus 1 stoichio. transition with rate" do
|
470
|
+
before do
|
471
|
+
@t1 = @tç.new!( ɴ: "T1",
|
472
|
+
s: { @p1 => 1, @p2 => -1, @p3 => -1 },
|
473
|
+
rate: 0.01 )
|
474
|
+
@net.include_transition! @t1
|
475
|
+
end
|
476
|
+
|
477
|
+
it "should expose its elements" do
|
478
|
+
assert_equal [@t1], @net.transitions
|
479
|
+
assert_equal [:T1], @net.tt
|
480
|
+
end
|
481
|
+
|
482
|
+
it "should expose transition groups" do
|
483
|
+
assert_equal true, @t1.has_rate?
|
484
|
+
assert_equal [@t1], @net.transitions_with_rate
|
485
|
+
assert_equal [], @net.rateless_transitions
|
486
|
+
assert_equal [@t1], @net.stoichiometric_transitions
|
487
|
+
assert_equal [], @net.nonstoichiometric_transitions
|
488
|
+
end
|
489
|
+
|
490
|
+
it "should tell its qualities" do
|
491
|
+
assert_equal true, @net.functional?
|
492
|
+
assert_equal true, @net.timed?
|
493
|
+
assert @net.include?( @t1 )
|
494
|
+
end
|
495
|
+
|
496
|
+
it "should have #place & #transition for safe access to the said elements" do
|
497
|
+
@net.send( :place, @p1 ).must_equal @p1
|
498
|
+
@net.send( :transition, @t1 ).must_equal @t1
|
499
|
+
end
|
500
|
+
|
501
|
+
it "has #new_simulation & #new_timed_simulation constructors" do
|
502
|
+
@net.must_respond_to :new_simulation
|
503
|
+
@net.must_respond_to :new_timed_simulation
|
504
|
+
end
|
505
|
+
|
506
|
+
it "should have other methods" do
|
507
|
+
assert_equal [1.1, 2.2, 3.3], [@p1, @p2, @p3].map( &:marking ).map{ |n| n.round 6 }
|
508
|
+
assert_equal 2.2 * 3.3 * 0.01, @t1.rate_closure.call( @p2.marking, @p3.marking )
|
509
|
+
assert_equal [ @p2, @p3 ], @t1.domain
|
510
|
+
@t1.fire! 1
|
511
|
+
assert_equal [1.1726, 2.1274, 3.2274], [@p1, @p2, @p3].map( &:marking ).map{ |n| n.round 6 }
|
512
|
+
end
|
513
|
+
|
514
|
+
describe "plus 1 more nameless timeless functionless transition" do
|
515
|
+
before do
|
516
|
+
@t2 = @tç.new s: { @p2 => -1, @p3 => 1 }
|
517
|
+
@net.include_transition! @t2
|
518
|
+
end
|
519
|
+
|
520
|
+
it "should expose its elements" do
|
521
|
+
assert_equal [@t1, @t2], @net.transitions
|
522
|
+
assert_equal [:T1, nil], @net.tt
|
523
|
+
@net.tap{ |n| n.exclude_transition! @t1 }.exclude_transition! @t2
|
524
|
+
@net.tap{ |n| n.exclude_place! @p3 }.pp.must_equal [:A, :B]
|
525
|
+
end
|
526
|
+
|
527
|
+
it "should expose transition groups" do
|
528
|
+
assert_equal [], @net.timeless_nonstoichiometric_transitions
|
529
|
+
assert_equal [], @net.timeless_nonstoichiometric_tt
|
530
|
+
assert_equal [@t2], @net.timeless_stoichiometric_transitions
|
531
|
+
assert_equal [nil], @net.timeless_stoichiometric_tt
|
532
|
+
assert_equal [], @net.timed_nonstoichiometric_transitions_without_rate
|
533
|
+
assert_equal [], @net.timed_rateless_nonstoichiometric_transitions
|
534
|
+
assert_equal [], @net.timed_nonstoichiometric_tt_without_rate
|
535
|
+
assert_equal [], @net.timed_rateless_nonstoichiometric_tt
|
536
|
+
assert_equal [], @net.timed_nonstoichiometric_transitions_without_rate
|
537
|
+
assert_equal [], @net.timed_rateless_nonstoichiometric_transitions
|
538
|
+
assert_equal [], @net.timed_nonstoichiometric_tt_without_rate
|
539
|
+
assert_equal [], @net.timed_rateless_nonstoichiometric_tt
|
540
|
+
assert_equal [], @net.nonstoichiometric_transitions_with_rate
|
541
|
+
assert_equal [], @net.nonstoichiometric_tt_with_rate
|
542
|
+
assert_equal [@t1], @net.stoichiometric_transitions_with_rate
|
543
|
+
assert_equal [:T1], @net.stoichiometric_tt_with_rate
|
544
|
+
assert_equal [], @net.transitions_with_explicit_assignment_action
|
545
|
+
assert_equal [], @net.transitions_with_assignment_action
|
546
|
+
assert_equal [], @net.assignment_transitions
|
547
|
+
assert_equal [], @net.tt_with_explicit_assignment_action
|
548
|
+
assert_equal [], @net.tt_with_assignment_action
|
549
|
+
assert_equal [], @net.assignment_tt
|
550
|
+
assert_equal [@t1, @t2], @net.stoichiometric_transitions
|
551
|
+
assert_equal [:T1, nil], @net.stoichiometric_tt
|
552
|
+
assert_equal [], @net.nonstoichiometric_transitions
|
553
|
+
assert_equal [], @net.nonstoichiometric_tt
|
554
|
+
assert_equal [@t1], @net.timed_transitions
|
555
|
+
assert_equal [:T1], @net.timed_tt
|
556
|
+
assert_equal [@t2], @net.timeless_transitions
|
557
|
+
assert_equal [nil], @net.timeless_tt
|
558
|
+
assert_equal [@t1], @net.transitions_with_rate
|
559
|
+
assert_equal [:T1], @net.tt_with_rate
|
560
|
+
assert_equal [@t2], @net.rateless_transitions
|
561
|
+
assert_equal [nil], @net.rateless_tt
|
562
|
+
end
|
563
|
+
|
564
|
+
it "should tell its qualities" do
|
565
|
+
assert_equal false, @net.functional?
|
566
|
+
assert_equal false, @net.timed?
|
567
|
+
@net.exclude_transition! @t2
|
568
|
+
assert_equal true, @net.functional?
|
569
|
+
assert_equal true, @net.timed?
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
|
577
|
+
# **************************************************************************
|
578
|
+
# Test of Simulation class.
|
579
|
+
# **************************************************************************
|
580
|
+
#
|
581
|
+
describe ::YPetri::Simulation do
|
582
|
+
before do
|
583
|
+
# skip "to make the testing faster"
|
584
|
+
@pç = pç = Class.new( ::YPetri::Place )
|
585
|
+
@tç = tç = Class.new( ::YPetri::Transition )
|
586
|
+
@nç = nç = Class.new( ::YPetri::Net )
|
587
|
+
[ @pç, @tç, @nç ].each { |klass|
|
588
|
+
klass.class_exec {
|
589
|
+
private
|
590
|
+
define_method :Place do pç end
|
591
|
+
define_method :Transition do tç end
|
592
|
+
define_method :Net do nç end
|
593
|
+
}
|
594
|
+
}
|
595
|
+
@p1 = @pç.new name: "P1", default_marking: 1
|
596
|
+
@p2 = @pç.new name: "P2", default_marking: 2
|
597
|
+
@p3 = @pç.new name: "P3", default_marking: 3
|
598
|
+
@p4 = @pç.new name: "P4", default_marking: 4
|
599
|
+
@p5 = @pç.new name: "P5", default_marking: 5
|
600
|
+
@t1 = @tç.new name: "T1",
|
601
|
+
s: { @p1 => -1, @p2 => -1, @p4 => 1 },
|
602
|
+
flux_closure: 0.1
|
603
|
+
@t2 = @tç.new name: "T2",
|
604
|
+
s: { @p1 => -1, @p3 => 1 },
|
605
|
+
flux_closure: λ { |a| a * 0.5 }
|
606
|
+
@t3 = @tç.new name: "T3",
|
607
|
+
s: { @p1 => -1, @p2 => -1, @p4 => 1 },
|
608
|
+
domain: @p3, flux: λ { |a| a * 0.5 }
|
609
|
+
@net = @nç.new << @p1 << @p2 << @p3 << @p4 << @p5
|
610
|
+
@net.include_transition! @t1
|
611
|
+
@net.include_transition! @t2
|
612
|
+
@net << @t3
|
613
|
+
@s = YPetri::Simulation.new net: @net,
|
614
|
+
place_clamps: { @p1 => 2.0, @p5 => 2.0 },
|
615
|
+
initial_marking: { @p2 => @p2.default_marking,
|
616
|
+
@p3 => @p3.default_marking,
|
617
|
+
@p4 => @p4.default_marking }
|
618
|
+
end
|
619
|
+
|
620
|
+
it "exposes the net" do
|
621
|
+
@s.net.must_equal @net
|
622
|
+
@s.net.places.size.must_equal 5
|
623
|
+
@s.net.transitions.size.must_equal 3
|
624
|
+
assert @net.include? @t1
|
625
|
+
assert @s.net.include? @t1
|
626
|
+
assert @net.include? @t2
|
627
|
+
assert @s.net.include? @t2
|
628
|
+
assert @net.include? @t3
|
629
|
+
assert @s.net.include? @t3
|
630
|
+
@s.net.transitions.size.must_equal 3
|
631
|
+
end
|
632
|
+
|
633
|
+
it "exposes Petri net places" do
|
634
|
+
@s.places.must_equal [ @p1, @p2, @p3, @p4, @p5 ]
|
635
|
+
@s.pp.must_equal [ :P1, :P2, :P3, :P4, :P5 ]
|
636
|
+
@s.places( :pp ).must_equal( { @p1 => :P1, @p2 => :P2, @p3 => :P3,
|
637
|
+
@p4 => :P4, @p5 => :P5 } )
|
638
|
+
@s.pp( :pp ).must_equal( { P1: :P1, P2: :P2, P3: :P3, P4: :P4, P5: :P5 } )
|
639
|
+
end
|
640
|
+
|
641
|
+
it "exposes Petri net transitions" do
|
642
|
+
@s.transitions.must_equal [ @t1, @t2, @t3 ]
|
643
|
+
@s.tt.must_equal [ :T1, :T2, :T3 ]
|
644
|
+
@s.transitions( :tt ).must_equal( { @t1 => :T1, @t2 => :T2, @t3 => :T3 } )
|
645
|
+
@s.tt( :tt ).must_equal( { T1: :T1, T2: :T2, T3: :T3 } )
|
646
|
+
end
|
647
|
+
|
648
|
+
it "exposes place clamps" do
|
649
|
+
@s.clamped_places( :place_clamps ).must_equal( { @p1 => 2, @p5 => 2 } )
|
650
|
+
@s.clamped_pp( :place_clamps ).must_equal( { P1: 2, P5: 2 } )
|
651
|
+
end
|
652
|
+
|
653
|
+
it "presents free places" do
|
654
|
+
@s.free_places.must_equal [ @p2, @p3, @p4 ]
|
655
|
+
@s.free_pp.must_equal [ :P2, :P3, :P4 ]
|
656
|
+
@s.free_places( :free_pp )
|
657
|
+
.must_equal( { @p2 => :P2, @p3 => :P3, @p4 => :P4 } )
|
658
|
+
@s.free_pp( :free_pp )
|
659
|
+
.must_equal( { P2: :P2, P3: :P3, P4: :P4 } )
|
660
|
+
end
|
661
|
+
|
662
|
+
it "presents clamped places" do
|
663
|
+
@s.clamped_places.must_equal [ @p1, @p5 ]
|
664
|
+
@s.clamped_pp.must_equal [ :P1, :P5 ]
|
665
|
+
@s.clamped_places( :clamped_pp ).must_equal( { @p1 => :P1, @p5 => :P5 } )
|
666
|
+
@s.clamped_pp( :clamped_pp ).must_equal( { P1: :P1, P5: :P5 } )
|
667
|
+
end
|
668
|
+
|
669
|
+
it "exposes initial marking" do
|
670
|
+
@s.free_places( :im ).must_equal( { @p2 => 2, @p3 => 3, @p4 => 4 } )
|
671
|
+
@s.free_pp( :im ).must_equal( { P2: 2, P3: 3, P4: 4 } )
|
672
|
+
@s.im.must_equal [ 2, 3, 4 ]
|
673
|
+
@s.im_vector.must_equal Matrix[[2], [3], [4]]
|
674
|
+
@s.im_vector.must_equal @s.iᴍ
|
675
|
+
end
|
676
|
+
|
677
|
+
it "exposes marking (simulation state)" do
|
678
|
+
@s.m.must_equal [2, 3, 4] # (we're after reset)
|
679
|
+
@s.free_places( :m ).must_equal( { @p2 => 2, @p3 => 3, @p4 => 4 } )
|
680
|
+
@s.free_pp( :m ).must_equal( { P2: 2, P3: 3, P4: 4 } )
|
681
|
+
@s.ᴍ.must_equal Matrix[[2], [3], [4]]
|
682
|
+
end
|
683
|
+
|
684
|
+
it "separately exposes marking of clamped places" do
|
685
|
+
@s.m_clamped.must_equal [ 2, 2 ]
|
686
|
+
@s.clamped_places( :m_clamped ).must_equal( { @p1 => 2, @p5 => 2 } )
|
687
|
+
@s.clamped_pp( :m_clamped ).must_equal( { P1: 2, P5: 2 } )
|
688
|
+
@s.ᴍ_clamped.must_equal Matrix[[2], [2]]
|
689
|
+
end
|
690
|
+
|
691
|
+
it "exposes marking of all places (with capitalized M)" do
|
692
|
+
@s.marking.must_equal [ 2, 2, 3, 4, 2 ]
|
693
|
+
@s.places( :marking )
|
694
|
+
.must_equal( { @p1 => 2, @p2 => 2, @p3 => 3, @p4 => 4, @p5 => 2 } )
|
695
|
+
@s.pp( :marking ).must_equal( { P1: 2, P2: 2, P3: 3, P4: 4, P5: 2 } )
|
696
|
+
@s.marking_vector.must_equal Matrix[[2], [2], [3], [4], [2]]
|
697
|
+
end
|
698
|
+
|
699
|
+
it "has #S_for / #stoichiometry_matrix_for" do
|
700
|
+
assert_equal Matrix.empty(3, 0), @s.S_for( [] )
|
701
|
+
assert_equal Matrix[[-1], [0], [1]], @s.S_for( [@t1] )
|
702
|
+
x = Matrix[[-1, -1], [0, 0], [1, 1]]
|
703
|
+
x.must_equal @s.S_for( [@t1, @t3] )
|
704
|
+
x.must_equal( @s.S_for( [@t1, @t3] ) )
|
705
|
+
@s.stoichiometry_matrix_for( [] ).must_equal Matrix.empty( 5, 0 )
|
706
|
+
end
|
707
|
+
|
708
|
+
it "has stoichiometry matrix for 3. tS transitions" do
|
709
|
+
@s.S_for_tS.must_equal Matrix.empty( 3, 0 )
|
710
|
+
end
|
711
|
+
|
712
|
+
it "has stoichiometry matrix for 4. Sr transitions" do
|
713
|
+
@s.S_for_TSr.must_equal Matrix.empty( 3, 0 )
|
714
|
+
end
|
715
|
+
|
716
|
+
it "has stoichiometry matrix for 6. SR transitions" do
|
717
|
+
@s.S_for_SR.must_equal Matrix[[-1, 0, -1], [0, 1, 0], [1, 0, 1]]
|
718
|
+
@s.S.must_equal @s.S_for_SR
|
719
|
+
end
|
720
|
+
|
721
|
+
it "presents 1. ts" do
|
722
|
+
assert_equal [], @s.ts_transitions
|
723
|
+
assert_equal( {}, @s.ts_transitions( :ts_transitions ) )
|
724
|
+
assert_equal [], @s.ts_tt
|
725
|
+
assert_equal( {}, @s.ts_tt( :ts_tt ) )
|
726
|
+
end
|
727
|
+
|
728
|
+
it "presents 2. tS transitions" do
|
729
|
+
assert_equal [], @s.tS_transitions
|
730
|
+
assert_equal( {}, @s.tS_transitions( :tS_transitions ) )
|
731
|
+
assert_equal [], @s.tS_tt
|
732
|
+
assert_equal( {}, @s.tS_tt( :tS_tt ) )
|
733
|
+
end
|
734
|
+
|
735
|
+
it "presents 3. Tsr transitions" do
|
736
|
+
assert_equal [], @s.Tsr_transitions
|
737
|
+
assert_equal( {}, @s.Tsr_transitions( :Tsr_transitions ) )
|
738
|
+
assert_equal [], @s.Tsr_tt
|
739
|
+
assert_equal( {}, @s.Tsr_tt( :Tsr_tt ) )
|
740
|
+
end
|
741
|
+
|
742
|
+
it "presents 4. TSr transitions" do
|
743
|
+
assert_equal [], @s.TSr_transitions
|
744
|
+
assert_equal( {}, @s.TSr_transitions( :TSr_tt ) )
|
745
|
+
assert_equal [], @s.TSr_tt
|
746
|
+
assert_equal( {}, @s.TSr_tt( :TSr_tt ) )
|
747
|
+
end
|
748
|
+
|
749
|
+
it "presents 5. sR transitions" do
|
750
|
+
assert_equal [], @s.sR_transitions
|
751
|
+
assert_equal( {}, @s.sR_transitions( :sR_transitions ) )
|
752
|
+
assert_equal [], @s.sR_tt
|
753
|
+
assert_equal( {}, @s.sR_tt( :sR_tt ) )
|
754
|
+
end
|
755
|
+
|
756
|
+
it "presents SR transitions" do
|
757
|
+
assert_equal [@t1, @t2, @t3], @s.SR_transitions
|
758
|
+
assert_equal( { @t1 => :T1, @t2 => :T2, @t3 => :T3 },
|
759
|
+
@s.SR_transitions( :SR_tt ) )
|
760
|
+
assert_equal [:T1, :T2, :T3], @s.SR_tt
|
761
|
+
assert_equal( { T1: :T1, T2: :T2, T3: :T3 }, @s.SR_tt( :SR_tt ) )
|
762
|
+
end
|
763
|
+
|
764
|
+
it "presents A transitions" do
|
765
|
+
assert_equal [], @s.A_transitions
|
766
|
+
assert_equal( {}, @s.A_transitions( :A_tt ) )
|
767
|
+
assert_equal [], @s.A_tt
|
768
|
+
assert_equal( {}, @s.A_tt( :A_tt ) )
|
769
|
+
end
|
770
|
+
|
771
|
+
it "presents S transitions" do
|
772
|
+
assert_equal [@t1, @t2, @t3], @s.S_transitions
|
773
|
+
assert_equal [:T1, :T2, :T3], @s.S_tt
|
774
|
+
assert_equal( { T1: :T1, T2: :T2, T3: :T3 }, @s.S_tt( :S_tt ) )
|
775
|
+
end
|
776
|
+
|
777
|
+
it "presents s transitions" do
|
778
|
+
assert_equal [], @s.s_transitions
|
779
|
+
assert_equal [], @s.s_tt
|
780
|
+
assert_equal( {}, @s.s_tt( :s_tt ) )
|
781
|
+
end
|
782
|
+
|
783
|
+
it "presents R transitions" do
|
784
|
+
assert_equal [@t1, @t2, @t3], @s.R_transitions
|
785
|
+
assert_equal [:T1, :T2, :T3], @s.R_tt
|
786
|
+
assert_equal( { T1: :T1, T2: :T2, T3: :T3 }, @s.R_tt( :R_tt ) )
|
787
|
+
end
|
788
|
+
|
789
|
+
it "presents r transitions" do
|
790
|
+
assert_equal [], @s.r_transitions
|
791
|
+
assert_equal [], @s.r_tt
|
792
|
+
end
|
793
|
+
|
794
|
+
it "1. handles ts transitions" do
|
795
|
+
@s.Δ_closures_for_ts.must_equal []
|
796
|
+
@s.Δ_if_ts_fire_once.must_equal Matrix.zero( @s.free_pp.size, 1 )
|
797
|
+
end
|
798
|
+
|
799
|
+
it "2. handles Tsr transitions" do
|
800
|
+
@s.Δ_closures_for_Tsr.must_equal []
|
801
|
+
@s.Δ_for_Tsr( 1.0 ).must_equal Matrix.zero( @s.free_pp.size, 1 )
|
802
|
+
end
|
803
|
+
|
804
|
+
it "3. handles tS transitions" do
|
805
|
+
@s.action_closures_for_tS.must_equal []
|
806
|
+
@s.action_vector_for_tS.must_equal Matrix.column_vector( [] )
|
807
|
+
@s.α_for_t.must_equal Matrix.column_vector( [] )
|
808
|
+
@s.Δ_if_tS_fire_once.must_equal Matrix.zero( @s.free_pp.size, 1 )
|
809
|
+
end
|
810
|
+
|
811
|
+
it "4. handles TSr transitions" do
|
812
|
+
@s.action_closures_for_TSr.must_equal []
|
813
|
+
@s.action_closures_for_Tr.must_equal []
|
814
|
+
@s.action_vector_for_TSr( 1.0 ).must_equal Matrix.column_vector( [] )
|
815
|
+
@s.action_vector_for_Tr( 1.0 ).must_equal Matrix.column_vector( [] )
|
816
|
+
@s.Δ_for_TSr( 1.0 ).must_equal Matrix.zero( @s.free_pp.size, 1 )
|
817
|
+
end
|
818
|
+
|
819
|
+
it "5. handles sR transitions" do
|
820
|
+
assert_equal [], @s.rate_closures_for_sR
|
821
|
+
assert_equal [], @s.rate_closures_for_s
|
822
|
+
@s.gradient_for_sR.must_equal Matrix.zero( @s.free_pp.size, 1 )
|
823
|
+
@s.Δ_Euler_for_sR( 1.0 ).must_equal Matrix.zero( @s.free_pp.size, 1 )
|
824
|
+
end
|
825
|
+
|
826
|
+
it "6. handles stoichiometric transitions with rate" do
|
827
|
+
@s.rate_closures_for_SR.size.must_equal 3
|
828
|
+
@s.rate_closures_for_S.size.must_equal 3
|
829
|
+
@s.rate_closures.size.must_equal 3
|
830
|
+
@s.flux_vector_for_SR.must_equal Matrix.column_vector( [ 0.4, 1.0, 1.5 ] )
|
831
|
+
@s.φ_for_SR.must_equal @s.flux_vector
|
832
|
+
@s.SR_tt( :φ_for_SR ).must_equal( { T1: 0.4, T2: 1.0, T3: 1.5 } )
|
833
|
+
@s.Euler_action_vector_for_SR( 1 )
|
834
|
+
.must_equal Matrix.column_vector [ 0.4, 1.0, 1.5 ]
|
835
|
+
@s.SR_tt( :Euler_action_for_SR, 1 ).must_equal( T1: 0.4, T2: 1.0, T3: 1.5 )
|
836
|
+
@s.Δ_Euler_for_SR( 1 ).must_equal Matrix[[-1.9], [1.0], [1.9]]
|
837
|
+
@s.free_pp( :Δ_Euler_for_SR, 1 ).must_equal( { P2: -1.9, P3: 1.0, P4: 1.9 } )
|
838
|
+
end
|
839
|
+
|
840
|
+
it "presents sparse stoichiometry vectors for its transitions" do
|
841
|
+
@s.sparse_σ( @t1 ).must_equal Matrix.cv( [-1, 0, 1] )
|
842
|
+
@s.sparse_stoichiometry_vector( @t1 )
|
843
|
+
.must_equal Matrix.cv( [-1, -1, 0, 1, 0] )
|
844
|
+
end
|
845
|
+
|
846
|
+
it "presents correspondence matrices free, clamped => all places" do
|
847
|
+
@s.F2A.must_equal Matrix[[0, 0, 0], [1, 0, 0], [0, 1, 0],
|
848
|
+
[0, 0, 1], [0, 0, 0]]
|
849
|
+
@s.C2A.must_equal Matrix[[1, 0], [0, 0], [0, 0], [0, 0], [0, 1]]
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
|
854
|
+
# **************************************************************************
|
855
|
+
# Test of TimedSimulation class.
|
856
|
+
# **************************************************************************
|
857
|
+
#
|
858
|
+
describe ::YPetri::TimedSimulation do
|
859
|
+
before do
|
860
|
+
# skip "to speed up testing"
|
861
|
+
@a = ::YPetri::Place.new default_marking: 1.0
|
862
|
+
@b = ::YPetri::Place.new default_marking: 2.0
|
863
|
+
@c = ::YPetri::Place.new default_marking: 3.0
|
864
|
+
end
|
865
|
+
|
866
|
+
describe "timed assembly a + b >> c" do
|
867
|
+
before do
|
868
|
+
@t1 = ::YPetri::Transition.new s: { @a => -1, @b => -1, @c => 1 }, rate: 0.1
|
869
|
+
@net = ::YPetri::Net.new << @a << @b << @c << @t1
|
870
|
+
@im_collection = [@a, @b, @c].τBmχHτ &:default_marking
|
871
|
+
end
|
872
|
+
|
873
|
+
describe "simulation with step size 1" do
|
874
|
+
before do
|
875
|
+
@sim = ::YPetri::TimedSimulation.new net: @net,
|
876
|
+
initial_marking: @im_collection,
|
877
|
+
step: 1,
|
878
|
+
sampling: 10,
|
879
|
+
target_time: 100
|
880
|
+
end
|
881
|
+
|
882
|
+
it "should #step! with expected results" do
|
883
|
+
m = @sim.step!.marking
|
884
|
+
assert_in_delta 0.8, m[ 0 ], 1e-9
|
885
|
+
assert_in_delta 1.8, m[ 1 ], 1e-9
|
886
|
+
assert_in_delta 3.2, m[ 2 ], 1e-9
|
887
|
+
end
|
888
|
+
|
889
|
+
it "should behave" do
|
890
|
+
assert_in_delta 0, ( Matrix.column_vector( [-0.02, -0.02, 0.02] ) -
|
891
|
+
@sim.ΔE( 0.1 ) ).column( 0 ).norm, 1e-9
|
892
|
+
@sim.step! 0.1
|
893
|
+
assert_in_delta 0, ( Matrix.column_vector( [0.98, 1.98, 3.02] ) -
|
894
|
+
@sim.marking_vector ).column( 0 ).norm, 1e-9
|
895
|
+
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
describe "simulation with step size 0.1" do
|
900
|
+
before do
|
901
|
+
@sim = ::YPetri::TimedSimulation.new net: @net,
|
902
|
+
initial_marking: @im_collection,
|
903
|
+
step: 0.1,
|
904
|
+
sampling: 10,
|
905
|
+
target_time: 100
|
906
|
+
end
|
907
|
+
|
908
|
+
it "should behave" do
|
909
|
+
m = @sim.step!.marking
|
910
|
+
assert_equal 10, @sim.sampling_period
|
911
|
+
assert_in_delta 0.98, m[ 0 ], 1e-9
|
912
|
+
assert_in_delta 1.98, m[ 1 ], 1e-9
|
913
|
+
assert_in_delta 3.02, m[ 2 ], 1e-9
|
914
|
+
end
|
915
|
+
|
916
|
+
it "should behave" do
|
917
|
+
@sim.run_until_target_time! 31
|
918
|
+
expected_recording = {
|
919
|
+
0 => [ 1, 2, 3 ],
|
920
|
+
10 => [ 0.22265, 1.22265, 3.77735 ],
|
921
|
+
20 => [ 0.07131, 1.07131, 3.92869 ],
|
922
|
+
30 => [ 0.02496, 1.02496, 3.97503 ]
|
923
|
+
}
|
924
|
+
assert_equal expected_recording.keys, @sim.recording.keys
|
925
|
+
assert_in_delta 0, expected_recording.values.zip( @sim.recording.values )
|
926
|
+
.map{ |expected, actual| ( Vector[ *expected ] -
|
927
|
+
Vector[ *actual ] ).norm }.reduce( :+ ), 1e-4
|
928
|
+
expected_recording_string =
|
929
|
+
"0.0,1.0,2.0,3.0\n" +
|
930
|
+
"10.0,0.22265,1.22265,3.77735\n" +
|
931
|
+
"20.0,0.07131,1.07131,3.92869\n" +
|
932
|
+
"30.0,0.02496,1.02496,3.97504\n"
|
933
|
+
assert_equal expected_recording_string, @sim.recording_csv_string
|
934
|
+
end
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
describe "timed 'isomerization' with flux given as λ" do
|
939
|
+
before do
|
940
|
+
@t2 = ::YPetri::Transition.new s: { @a => -1, @c => 1 },
|
941
|
+
rate_closure: λ { |a| a * 0.5 }
|
942
|
+
@net = ::YPetri::Net.new << @a << @b << @c << @t2
|
943
|
+
end
|
944
|
+
|
945
|
+
describe "behavior of #step" do
|
946
|
+
before do
|
947
|
+
@sim = ::YPetri::TimedSimulation.new net: @net,
|
948
|
+
initial_marking: [ @a, @b, @c ].τBᴍHτ( &:default_marking ),
|
949
|
+
step: 1,
|
950
|
+
sampling: 10
|
951
|
+
end
|
952
|
+
|
953
|
+
it "should have expected stoichiometry matrix" do
|
954
|
+
@sim.S.must_equal Matrix[ [-1, 0, 1] ].t
|
955
|
+
m = @sim.step!.marking
|
956
|
+
m[ 0 ].must_be_within_epsilon( 0.5, 1e-6 )
|
957
|
+
m[ 1 ].must_equal 2
|
958
|
+
m[ 2 ].must_be_within_delta( 3.5, 1e-9 )
|
959
|
+
end
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
describe "timed controlled isomerization" do
|
964
|
+
before do
|
965
|
+
@t3 = ::YPetri::Transition.new s: { @a => -1, @c => 1 },
|
966
|
+
domain: @b,
|
967
|
+
rate: λ { |a| a * 0.5 }
|
968
|
+
@net = ::YPetri::Net.new << @a << @b << @c << @t3
|
969
|
+
@sim = ::YPetri::TimedSimulation.new net: @net,
|
970
|
+
initial_marking: { @a => 1, @b => 0.6, @c => 3 },
|
971
|
+
step: 1,
|
972
|
+
sampling: 10,
|
973
|
+
target_time: 2
|
974
|
+
end
|
975
|
+
|
976
|
+
it "should exhibit correct behavior of #step" do
|
977
|
+
@sim.marking.must_equal [1.0, 0.6, 3.0]
|
978
|
+
@t3.stoichiometric?.must_equal true
|
979
|
+
@t3.timed?.must_equal true
|
980
|
+
@t3.has_rate?.must_equal true
|
981
|
+
@sim.gradient.must_equal Matrix.cv [-0.3, 0.0, 0.3]
|
982
|
+
@sim.Δ_Euler.must_equal Matrix.cv [-0.3, 0.0, 0.3]
|
983
|
+
@sim.step!
|
984
|
+
@sim.marking_vector.must_equal Matrix.cv [0.7, 0.6, 3.3]
|
985
|
+
@sim.euler_step!
|
986
|
+
@sim.run!
|
987
|
+
@sim.marking_vector.map( &[:round, 5] )
|
988
|
+
.must_equal Matrix.cv [0.4, 0.6, 3.6]
|
989
|
+
end
|
990
|
+
end
|
991
|
+
end
|
992
|
+
|
993
|
+
|
994
|
+
# **************************************************************************
|
995
|
+
# Test of Workspace class.
|
996
|
+
# **************************************************************************
|
997
|
+
#
|
998
|
+
describe ::YPetri::Workspace do
|
999
|
+
before do
|
1000
|
+
# skip "to speed up testing"
|
1001
|
+
@w = ::YPetri::Workspace.new
|
1002
|
+
a = @w.Place.new!( default_marking: 1.0, name: "AA" )
|
1003
|
+
b = @w.Place.new!( default_marking: 2.0, name: "BB" )
|
1004
|
+
c = @w.Place.new!( ɴ: "CC", default_marking: 3.0 )
|
1005
|
+
t1 = @w.Transition.new! s: { a => -1, b => -1, c => 1 },
|
1006
|
+
rate: 0.1,
|
1007
|
+
ɴ: "AA_BB_assembly"
|
1008
|
+
t2 = @w.Transition.new! ɴ: "AA_appearing",
|
1009
|
+
codomain: a,
|
1010
|
+
rate: λ{ 0.1 },
|
1011
|
+
stoichiometry: 1
|
1012
|
+
@pp, @tt = [a, b, c], [t1, t2]
|
1013
|
+
@f_name = "test_output.csv"
|
1014
|
+
@w.set_imc @pp.τBᴍHτ( &:default_marking )
|
1015
|
+
@w.set_ssc step: 0.1, sampling: 10, target_time: 50
|
1016
|
+
@w.set_cc( {} )
|
1017
|
+
@sim = @w.new_timed_simulation
|
1018
|
+
File.delete @f_name rescue nil
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
it "should present places, transitions, nets, simulations" do
|
1022
|
+
assert_kind_of ::YPetri::Net, @w.Net::Top
|
1023
|
+
assert_equal @pp[0], @w.place( "AA" )
|
1024
|
+
assert_equal :AA, @w.p( @pp[0] )
|
1025
|
+
assert_equal @tt[0], @w.transition( "AA_BB_assembly" )
|
1026
|
+
assert_equal :AA_appearing, @w.t( @tt[1] )
|
1027
|
+
assert_equal @pp, @w.places
|
1028
|
+
assert_equal @tt, @w.transitions
|
1029
|
+
assert_equal 1, @w.nets.size
|
1030
|
+
assert_equal 1, @w.simulations.size
|
1031
|
+
assert_equal 0, @w.cc.size
|
1032
|
+
assert_equal 3, @w.imc.size
|
1033
|
+
assert [0.1, 10, 50].each { |e| @w.ssc.include? e }
|
1034
|
+
assert_equal @sim, @w.simulation
|
1035
|
+
assert_equal [:Base], @w.clamp_collections.keys
|
1036
|
+
assert_equal [:Base], @w.initial_marking_collections.keys
|
1037
|
+
assert_equal [:Base], @w.simulation_settings_collections.keys
|
1038
|
+
assert_equal [:AA, :BB, :CC], @w.pp
|
1039
|
+
assert_equal [:AA_BB_assembly, :AA_appearing], @w.tt
|
1040
|
+
assert_equal [:Top], @w.nn
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
it "should simulate" do
|
1044
|
+
assert_equal 1, @w.simulations.size
|
1045
|
+
assert_kind_of( ::YPetri::Simulation, @w.simulation )
|
1046
|
+
assert_equal 2, @w.simulation.SR_transitions.size
|
1047
|
+
@tt[0].domain.must_equal [ @pp[0], @pp[1] ]
|
1048
|
+
@tt[1].domain.must_equal []
|
1049
|
+
assert_equal [0.2, 0.1], @w.simulation.φ.column_to_a
|
1050
|
+
@w.simulation.step!
|
1051
|
+
@w.simulation.run!
|
1052
|
+
rec_string = @w.simulation.recording_csv_string
|
1053
|
+
expected_recording_string =
|
1054
|
+
"0.0,1.0,2.0,3.0\n" +
|
1055
|
+
"10.0,0.86102,0.86102,4.13898\n" +
|
1056
|
+
"20.0,1.29984,0.29984,4.70016\n"
|
1057
|
+
assert rec_string.start_with?( expected_recording_string )
|
1058
|
+
end
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
# **************************************************************************
|
1062
|
+
# Test of Manipulator class.
|
1063
|
+
# **************************************************************************
|
1064
|
+
#
|
1065
|
+
describe ::YPetri::Manipulator do
|
1066
|
+
before do
|
1067
|
+
# skip "for now"
|
1068
|
+
@m = ::YPetri::Manipulator.new
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
it "has net basic points" do
|
1072
|
+
# --- net point related assets ---
|
1073
|
+
@m.net_point_reset
|
1074
|
+
@m.net_point_to @m.workspace.net( :Top )
|
1075
|
+
@m.net.must_equal @m.workspace.Net::Top
|
1076
|
+
# --- simulation point related assets ---
|
1077
|
+
@m.simulation_point_reset
|
1078
|
+
@m.simulation_point_to nil
|
1079
|
+
@m.simulation.must_equal nil
|
1080
|
+
@m.simulation_point_position.must_equal nil
|
1081
|
+
# --- cc point related assets ---
|
1082
|
+
@m.cc_point_reset
|
1083
|
+
@m.cc_point_to :Base
|
1084
|
+
@m.cc.must_equal @m.workspace.clamp_collection
|
1085
|
+
@m.cc.wont_equal :Base
|
1086
|
+
@m.cc_point_position.must_equal :Base
|
1087
|
+
# --- imc point related assets ---
|
1088
|
+
@m.imc_point_reset
|
1089
|
+
@m.imc_point_to :Base
|
1090
|
+
@m.imc.must_equal @m.workspace.initial_marking_collection
|
1091
|
+
@m.imc.wont_equal :Base
|
1092
|
+
@m.imc_point_position.must_equal :Base
|
1093
|
+
# --- ssc point related assets ---
|
1094
|
+
@m.ssc_point_reset
|
1095
|
+
@m.ssc_point_to :Base
|
1096
|
+
@m.ssc.must_equal @m.workspace.simulation_settings_collection
|
1097
|
+
@m.ssc.wont_equal :Base
|
1098
|
+
@m.ssc_point_position.must_equal :Base
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
it "has basic selections" do
|
1102
|
+
@m.net_selection_clear
|
1103
|
+
@m.simulation_selection_clear
|
1104
|
+
@m.cc_selection_clear
|
1105
|
+
@m.imc_selection_clear
|
1106
|
+
@m.ssc_selection_clear
|
1107
|
+
@m.net_selection.must_equal []
|
1108
|
+
@m.simulation_selection.must_equal []
|
1109
|
+
@m.ssc_selection.must_equal []
|
1110
|
+
@m.cc_selection.must_equal []
|
1111
|
+
@m.imc_selection.must_equal []
|
1112
|
+
[ :net, :simulation, :cc, :imc, :ssc ].each { |sym1|
|
1113
|
+
[ :select!, :select, :unselect ].each { |sym2|
|
1114
|
+
@m.must_respond_to "#{sym1}_#{sym2}"
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
it "presents some methods from workspace" do
|
1120
|
+
[ @m.places, @m.transitions, @m.nets, @m.simulations ].map( &:size )
|
1121
|
+
.must_equal [ 0, 0, 1, 0 ]
|
1122
|
+
[ @m.clamp_collections,
|
1123
|
+
@m.initial_marking_collections,
|
1124
|
+
@m.simulation_settings_collections ].map( &:size ).must_equal [ 1, 1, 1 ]
|
1125
|
+
[ @m.clamp_collections,
|
1126
|
+
@m.initial_marking_collections,
|
1127
|
+
@m.simulation_settings_collections ]
|
1128
|
+
.map( &:keys ).must_equal [[:Base]] * 3
|
1129
|
+
@m.pp.must_equal []
|
1130
|
+
@m.tt.must_equal []
|
1131
|
+
@m.nn.must_equal [ :Top ] # ie. :Top net spanning whole workspace
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
describe "slightly more complicated case" do
|
1135
|
+
before do
|
1136
|
+
@p = @m.Place ɴ: "P", default_marking: 1
|
1137
|
+
@q = @m.Place ɴ: "Q", default_marking: 1
|
1138
|
+
@decay_t = @m.Transition ɴ: "Tp", s: { P: -1 }, rate: 0.1
|
1139
|
+
@constant_flux_t = @m.Transition ɴ: "Tq", s: { Q: 1 }, rate: λ{ 0.02 }
|
1140
|
+
@m.initial_marking @p => 1.2
|
1141
|
+
@m.initial_marking @q => 2
|
1142
|
+
@m.set_step 0.01
|
1143
|
+
@m.set_sampling 1
|
1144
|
+
@m.set_time 30
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
it "works" do
|
1148
|
+
@m.run!
|
1149
|
+
@m.simulation.places.must_equal [ @p, @q ]
|
1150
|
+
@m.simulation.transitions.must_equal [ @decay_t, @constant_flux_t ]
|
1151
|
+
@m.simulation.SR_tt.must_equal [ :Tp, :Tq ]
|
1152
|
+
@m.simulation.sparse_stoichiometry_vector( :Tp )
|
1153
|
+
.must_equal Matrix.column_vector( [-1, 0] )
|
1154
|
+
@m.simulation.stoichiometry_matrix_for( @m.transitions ).column_size
|
1155
|
+
.must_equal 2
|
1156
|
+
@m.simulation.stoichiometry_matrix_for( @m.transitions ).row_size
|
1157
|
+
.must_equal 2
|
1158
|
+
@m.simulation.flux_vector.row_size.must_equal 2
|
1159
|
+
# @m.plot_recording
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
|
1165
|
+
# **************************************************************************
|
1166
|
+
# Test of YPetri class itself.
|
1167
|
+
# **************************************************************************
|
1168
|
+
#
|
1169
|
+
describe ::YPetri do
|
1170
|
+
before do
|
1171
|
+
# skip "to speed up testing"
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
it "should have basic classes" do
|
1175
|
+
[ :Place, :Transition, :Net,
|
1176
|
+
:Simulation, :TimedSimulation,
|
1177
|
+
:Workspace, :Manipulator ].each { |ß|
|
1178
|
+
assert_kind_of Module, ::YPetri.const_get( ß ) }
|
1179
|
+
end
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
|
1183
|
+
# **************************************************************************
|
1184
|
+
# ACCEPTANCE TESTS
|
1185
|
+
# **************************************************************************
|
1186
|
+
|
1187
|
+
# describe "Token game" do
|
1188
|
+
# before do
|
1189
|
+
# @m = YPetri::Manipulator.new
|
1190
|
+
# @m.Place name: "A"
|
1191
|
+
# @m.Place name: "B"
|
1192
|
+
# @m.Place name: "C", marking: 7.77
|
1193
|
+
# @m.Transition name: "A2B", stoichiometry: { A: -1, B: 1 }
|
1194
|
+
# @m.Transition name: "C_decay", stoichiometry: { C: -1 }, rate: 0.05
|
1195
|
+
# end
|
1196
|
+
|
1197
|
+
# it "should work" do
|
1198
|
+
# @m.place( :A ).marking = 2
|
1199
|
+
# @m.place( :B ).marking = 5
|
1200
|
+
# @m.places.map( &:name ).must_equal [:A, :B, :C]
|
1201
|
+
# @m.places.map( &:marking ).must_equal [2, 5, 7.77]
|
1202
|
+
# @m.transition( :A2B ).connectivity.must_equal [ @m.place( :A ), @m.place( :B ) ]
|
1203
|
+
# @m.transition( :A2B ).fire!
|
1204
|
+
# @m.places.map( &:marking ).must_equal [1, 6, 7.77]
|
1205
|
+
# @m.transition( :A2B ).fire!
|
1206
|
+
# @m.place( :A ).marking.must_equal 0
|
1207
|
+
# @m.place( :B ).marking.must_equal 7
|
1208
|
+
# 2.times do @m.transition( :C_decay ).fire! 1 end
|
1209
|
+
# @m.transition( :C_decay ).fire! 0.1
|
1210
|
+
# 200.times do @m.transition( :C_decay ).fire! 1 end
|
1211
|
+
# assert_in_delta @m.place( :C ).marking, 0.00024, 0.00001
|
1212
|
+
# end
|
1213
|
+
# end
|
1214
|
+
|
1215
|
+
# describe "Basic use of TimedSimulation" do
|
1216
|
+
# before do
|
1217
|
+
# @m = YPetri::Manipulator.new
|
1218
|
+
# @m.Place( name: "A", default_marking: 0.5 )
|
1219
|
+
# @m.Place( name: "B", default_marking: 0.5 )
|
1220
|
+
# @m.Transition( name: "A_pump",
|
1221
|
+
# stoichiometry: { A: -1 },
|
1222
|
+
# rate: proc { 0.005 } )
|
1223
|
+
# @m.Transition( name: "B_decay",
|
1224
|
+
# stoichiometry: { B: -1 },
|
1225
|
+
# rate: 0.05 )
|
1226
|
+
# end
|
1227
|
+
|
1228
|
+
# it "should work" do
|
1229
|
+
# @m.net.must_be_kind_of ::YPetri::Net
|
1230
|
+
# @m.run!
|
1231
|
+
# @m.simulation.must_be_kind_of ::YPetri::TimedSimulation
|
1232
|
+
# @m.plot_recording
|
1233
|
+
# sleep 3
|
1234
|
+
# end
|
1235
|
+
# end
|
1236
|
+
|
1237
|
+
# describe "Graphviz visualization" do
|
1238
|
+
# before do
|
1239
|
+
# @m = YPetri::Manipulator.new
|
1240
|
+
# @m.Place name: :A, m!: 1
|
1241
|
+
# @m.Place name: :B, m!: 1.5
|
1242
|
+
# @m.Place name: :C, m!: 2
|
1243
|
+
# @m.Place name: :D, m!: 2.5
|
1244
|
+
# @m.Transition name: :A_pump, s: { A: -1 }, rate: proc { 0.005 }
|
1245
|
+
# @m.Transition name: :B_decay, s: { B: -1 }, rate: 0.05
|
1246
|
+
# @m.Transition name: :C_guard, assignment: true, codomain: :C, action: λ { 2 }
|
1247
|
+
# end
|
1248
|
+
|
1249
|
+
# it "should work" do
|
1250
|
+
# @m.net.visualize
|
1251
|
+
# end
|
1252
|
+
# end
|
1253
|
+
|
1254
|
+
# describe "Simplified dTTP pathway used for demo with Dr. Chang" do
|
1255
|
+
# before do
|
1256
|
+
# @m = YPetri::Manipulator.new
|
1257
|
+
# Cytoplasm_volume_in_litres = 5.0e-11
|
1258
|
+
# NA = 6.022e23
|
1259
|
+
# Pieces_per_micromolar = NA / 1_000_000 * Cytoplasm_volume_in_litres
|
1260
|
+
# @m.set_step 60
|
1261
|
+
# @m.set_sampling 300
|
1262
|
+
# @m.set_target_time 60 * 60 * 2
|
1263
|
+
# AMP = @m.Place( name: :AMP, m!: 8695.0 )
|
1264
|
+
# ADP = @m.Place( name: :ADP, m!: 6521.0 )
|
1265
|
+
# ATP = @m.Place( name: :ATP, m!: 3152.0 )
|
1266
|
+
# Deoxycytidine = @m.Place( name: :Deoxycytidine, m!: 0.5 )
|
1267
|
+
# DeoxyCTP = @m.Place( name: :DeoxyCTP, m!: 1.0 )
|
1268
|
+
# DeoxyGMP = @m.Place( name: :DeoxyGMP, m!: 1.0 )
|
1269
|
+
# UMP_UDP_pool = @m.Place( name: :UMP_UDP_pool, m!: 2737.0 )
|
1270
|
+
# DeoxyUMP_DeoxyUDP_pool = @m.Place( name: :DeoxyUMP_DeoxyUDP_pool, m!: 0.0 )
|
1271
|
+
# DeoxyTMP = @m.Place( name: :DeoxyTMP, m!: 3.3 )
|
1272
|
+
# DeoxyTDP_DeoxyTTP_pool = @m.Place( name: :DeoxyTDP_DeoxyTTP_pool, m!: 5.0 )
|
1273
|
+
# Thymidine = @m.Place( name: :Thymidine, m!: 0.5 )
|
1274
|
+
# TK1 = @m.Place( name: :TK1, m!: 100_000 )
|
1275
|
+
# TYMS = @m.Place( name: :TYMS, m!: 100_000 )
|
1276
|
+
# RNR = @m.Place( name: :RNR, m!: 100_000 )
|
1277
|
+
# TMPK = @m.Place( name: :TMPK, m!: 100_000 )
|
1278
|
+
# TK1_kDa = 24.8
|
1279
|
+
# TYMS_kDa = 66.0
|
1280
|
+
# RNR_kDa = 140.0
|
1281
|
+
# TMPK_kDa = 50.0
|
1282
|
+
# TK1_a = 5.40
|
1283
|
+
# TYMS_a = 3.80
|
1284
|
+
# RNR_a = 1.00
|
1285
|
+
# TMPK_a = 0.83
|
1286
|
+
# @m.clamp AMP: 8695.0, ADP: 6521.0, ATP: 3152.0
|
1287
|
+
# @m.clamp Deoxycytidine: 0.5, DeoxyCTP: 1.0, DeoxyGMP: 1.0
|
1288
|
+
# @m.clamp Thymidine: 0.5
|
1289
|
+
# @m.clamp UMP_UDP_pool: 2737.0
|
1290
|
+
# # Functions
|
1291
|
+
# Vmax_per_minute_per_enzyme_molecule =
|
1292
|
+
# lambda { |enzyme_specific_activity_in_micromol_per_minute_per_mg,
|
1293
|
+
# enzyme_molecular_mass_in_kDa|
|
1294
|
+
# enzyme_specific_activity_in_micromol_per_minute_per_mg *
|
1295
|
+
# enzyme_molecular_mass_in_kDa }
|
1296
|
+
# Vmax_per_minute =
|
1297
|
+
# lambda { |specific_activity, kDa, enzyme_molecules_per_cell|
|
1298
|
+
# Vmax_per_minute_per_enzyme_molecule.( specific_activity, kDa ) *
|
1299
|
+
# enzyme_molecules_per_cell }
|
1300
|
+
# Vmax_per_second =
|
1301
|
+
# lambda { |specific_activity, kDa, enzyme_molecules_per_cell|
|
1302
|
+
# Vmax_per_minute.( specific_activity,
|
1303
|
+
# kDa,
|
1304
|
+
# enzyme_molecules_per_cell ) / 60 }
|
1305
|
+
# Km_reduced =
|
1306
|
+
# lambda { |km, ki_hash={}|
|
1307
|
+
# ki_hash.map { |concentration, ci_Ki|
|
1308
|
+
# concentration / ci_Ki
|
1309
|
+
# }.reduce( 1, :+ ) * km }
|
1310
|
+
# Occupancy =
|
1311
|
+
# lambda { |concentration, reactant_Km, compet_inh_w_Ki_hash={}|
|
1312
|
+
# concentration / ( concentration +
|
1313
|
+
# Km_reduced.( reactant_Km,
|
1314
|
+
# compet_inh_w_Ki_hash ) ) }
|
1315
|
+
# MM_with_inh_micromolars_per_second =
|
1316
|
+
# lambda { |reactant_concentration,
|
1317
|
+
# enzyme_specific_activity,
|
1318
|
+
# enzyme_mass_in_kDa,
|
1319
|
+
# enzyme_molecules_per_cell,
|
1320
|
+
# reactant_Km,
|
1321
|
+
# competitive_inh_w_Ki_hash={}|
|
1322
|
+
# Vmax_per_second.( enzyme_specific_activity,
|
1323
|
+
# enzyme_mass_in_kDa,
|
1324
|
+
# enzyme_molecules_per_cell ) *
|
1325
|
+
# Occupancy.( reactant_concentration,
|
1326
|
+
# reactant_Km,
|
1327
|
+
# competitive_inh_w_Ki_hash ) }
|
1328
|
+
# MMi = MM_with_inh_micromolars_per_second
|
1329
|
+
# TK1_Thymidine_Km = 5.0
|
1330
|
+
# TYMS_DeoxyUMP_Km = 2.0
|
1331
|
+
# RNR_UDP_Km = 1.0
|
1332
|
+
# DNA_creation_speed = 3_000_000_000 / ( 12 * 3600 )
|
1333
|
+
# TMPK_DeoxyTMP_Km = 12.0
|
1334
|
+
|
1335
|
+
# # transitions
|
1336
|
+
# @m.Transition name: :TK1_Thymidine_DeoxyTMP,
|
1337
|
+
# domain: [ Thymidine, TK1, DeoxyTDP_DeoxyTTP_pool, DeoxyCTP, Deoxycytidine, AMP, ADP, ATP ],
|
1338
|
+
# stoichiometry: { Thymidine: -1, DeoxyTMP: 1 },
|
1339
|
+
# rate: proc { |rc, e, pool1, ci2, ci3, master1, master2, master3|
|
1340
|
+
# ci1 = pool1 * master3 / ( master2 + master3 )
|
1341
|
+
# MMi.( rc, TK1_a, TK1_kDa, e, TK1_Thymidine_Km,
|
1342
|
+
# ci1 => 13.5, ci2 => 0.8, ci3 => 40.0 ) }
|
1343
|
+
# @m.Transition name: :TYMS_DeoxyUMP_DeoxyTMP,
|
1344
|
+
# domain: [ DeoxyUMP_DeoxyUDP_pool, TYMS, AMP, ADP, ATP ],
|
1345
|
+
# stoichiometry: { DeoxyUMP_DeoxyUDP_pool: -1, DeoxyTMP: 1 },
|
1346
|
+
# rate: proc { |pool, e, master1, master2, master3|
|
1347
|
+
# rc = pool * master2 / ( master1 + master2 )
|
1348
|
+
# MMi.( rc, TYMS_a, TYMS_kDa, e, TYMS_DeoxyUMP_Km ) }
|
1349
|
+
# @m.Transition name: :RNR_UDP_DeoxyUDP,
|
1350
|
+
# domain: [ UMP_UDP_pool, RNR, DeoxyUMP_DeoxyUDP_pool, AMP, ADP, ATP ],
|
1351
|
+
# stoichiometry: { UMP_UDP_pool: -1, DeoxyUMP_DeoxyUDP_pool: 1 },
|
1352
|
+
# rate: proc { |pool, e, master1, master2, master3|
|
1353
|
+
# rc = pool * master2 / ( master1 + master2 )
|
1354
|
+
# MMi.( rc, RNR_a, RNR_kDa, e, RNR_UDP_Km ) }
|
1355
|
+
# @m.Transition name: :DNA_polymerase_consumption_of_DeoxyTTP,
|
1356
|
+
# stoichiometry: { DeoxyTDP_DeoxyTTP_pool: -1 },
|
1357
|
+
# rate: proc { DNA_creation_speed / 4 }
|
1358
|
+
# @m.Transition name: :TMPK_DeoxyTMP_DeoxyTDP,
|
1359
|
+
# domain: [ DeoxyTMP, TMPK, ADP,
|
1360
|
+
# DeoxyTDP_DeoxyTTP_pool,
|
1361
|
+
# DeoxyGMP, AMP, ATP ],
|
1362
|
+
# stoichiometry: { DeoxyTMP: -1, TMPK: 0, DeoxyTDP_DeoxyTTP_pool: 1 },
|
1363
|
+
# rate: proc { |rc, e, ci1, pool, ci4, master1, master3|
|
1364
|
+
# master2 = ci1
|
1365
|
+
# ci2 = pool * master2 / ( master2 + master3 )
|
1366
|
+
# ci3 = pool * master3 / ( master2 + master3 )
|
1367
|
+
# MMi.( rc, TMPK_a, TMPK_kDa, e, TMPK_DeoxyTMP_Km,
|
1368
|
+
# ci1 => 250.0, ci2 => 30.0, ci3 => 750, ci4 => 117 ) }
|
1369
|
+
# end
|
1370
|
+
|
1371
|
+
# it "should work" do
|
1372
|
+
# @m.run!
|
1373
|
+
# @m.plot_recording
|
1374
|
+
# sleep 3
|
1375
|
+
# end
|
1376
|
+
# end
|
1377
|
+
|
1378
|
+
# describe "Use of TimedSimulation with units" do
|
1379
|
+
# before do
|
1380
|
+
# require 'sy'
|
1381
|
+
|
1382
|
+
# @m = YPetri::Manipulator.new
|
1383
|
+
|
1384
|
+
# # === General assumptions
|
1385
|
+
# Cytoplasm_volume = 5.0e-11.l
|
1386
|
+
# Pieces_per_concentration = SY::Nᴀ * Cytoplasm_volume
|
1387
|
+
|
1388
|
+
# # === Simulation settings
|
1389
|
+
# @m.set_step 60.s
|
1390
|
+
# @m.set_target_time 10.min
|
1391
|
+
# @m.set_sampling 120.s
|
1392
|
+
|
1393
|
+
# # === Places
|
1394
|
+
# AMP = @m.Place m!: 8695.0.µM
|
1395
|
+
# ADP = @m.Place m!: 6521.0.µM
|
1396
|
+
# ATP = @m.Place m!: 3152.0.µM
|
1397
|
+
# Deoxycytidine = @m.Place m!: 0.5.µM
|
1398
|
+
# DeoxyCTP = @m.Place m!: 1.0.µM
|
1399
|
+
# DeoxyGMP = @m.Place m!: 1.0.µM
|
1400
|
+
# U12P = @m.Place m!: 2737.0.µM
|
1401
|
+
# DeoxyU12P = @m.Place m!: 0.0.µM
|
1402
|
+
# DeoxyTMP = @m.Place m!: 3.3.µM
|
1403
|
+
# DeoxyT23P = @m.Place m!: 5.0.µM
|
1404
|
+
# Thymidine = @m.Place m!: 0.5.µM
|
1405
|
+
# TK1 = @m.Place m!: 100_000.unit.( SY::MoleAmount ) / Cytoplasm_volume
|
1406
|
+
# TYMS = @m.Place m!: 100_000.unit.( SY::MoleAmount ) / Cytoplasm_volume
|
1407
|
+
# RNR = @m.Place m!: 100_000.unit.( SY::MoleAmount ) / Cytoplasm_volume
|
1408
|
+
# TMPK = @m.Place m!: 100_000.unit.( SY::MoleAmount ) / Cytoplasm_volume
|
1409
|
+
|
1410
|
+
# # === Enzyme molecular masses
|
1411
|
+
# TK1_m = 24.8.kDa
|
1412
|
+
# TYMS_m = 66.0.kDa
|
1413
|
+
# RNR_m = 140.0.kDa
|
1414
|
+
# TMPK_m = 50.0.kDa
|
1415
|
+
|
1416
|
+
# # === Specific activities of the enzymes
|
1417
|
+
# TK1_a = 5.40.µmol.min⁻¹.mg⁻¹
|
1418
|
+
# TYMS_a = 3.80.µmol.min⁻¹.mg⁻¹
|
1419
|
+
# RNR_a = 1.00.µmol.min⁻¹.mg⁻¹
|
1420
|
+
# TMPK_a = 0.83.µmol.min⁻¹.mg⁻¹
|
1421
|
+
|
1422
|
+
# # === Clamps
|
1423
|
+
# @m.clamp AMP: 8695.0.µM, ADP: 6521.0.µM, ATP: 3152.0.µM
|
1424
|
+
# @m.clamp Deoxycytidine: 0.5.µM, DeoxyCTP: 1.0.µM, DeoxyGMP: 1.0.µM
|
1425
|
+
# @m.clamp Thymidine: 0.5.µM
|
1426
|
+
# @m.clamp U12P: 2737.0.µM
|
1427
|
+
|
1428
|
+
# # === Function closures
|
1429
|
+
|
1430
|
+
# # Vmax of an enzyme.
|
1431
|
+
# #
|
1432
|
+
# Vmax_enzyme = lambda { |specific_activity, mass, enzyme_conc|
|
1433
|
+
# specific_activity * mass * enzyme_conc.( SY::Molecularity )
|
1434
|
+
# }
|
1435
|
+
|
1436
|
+
# # Michaelis constant reduced for competitive inhibitors.
|
1437
|
+
# #
|
1438
|
+
# Km_reduced = lambda { |km, ki_hash={}|
|
1439
|
+
# ki_hash.map { |concentration, ci_Ki|
|
1440
|
+
# concentration / ci_Ki }
|
1441
|
+
# .reduce( 1, :+ ) * km
|
1442
|
+
# }
|
1443
|
+
|
1444
|
+
# # Occupancy of enzyme active sites at given concentration of reactants
|
1445
|
+
# # and competitive inhibitors.
|
1446
|
+
# #
|
1447
|
+
# Occupancy = lambda { |ʀ_conc, ʀ_Km, cɪ_Kɪ={}|
|
1448
|
+
# ʀ_conc / ( ʀ_conc + Km_reduced.( ʀ_Km, cɪ_Kɪ ) )
|
1449
|
+
# }
|
1450
|
+
|
1451
|
+
# # Michaelis and Menten equation with competitive inhibitors.
|
1452
|
+
# #
|
1453
|
+
# MMi = MM_equation_with_inhibitors = lambda {
|
1454
|
+
# |ʀ_conc, ᴇ_specific_activity, ᴇ_mass, ᴇ_conc, ʀ_Km, cɪ_Kɪ={}|
|
1455
|
+
# Vmax_enzyme.( ᴇ_specific_activity, ᴇ_mass, ᴇ_conc ) *
|
1456
|
+
# Occupancy.( ʀ_conc, ʀ_Km, cɪ_Kɪ )
|
1457
|
+
# }
|
1458
|
+
|
1459
|
+
# # === Michaelis constants of the enzymes involved.
|
1460
|
+
|
1461
|
+
# TK1_Thymidine_Km = 5.0.µM
|
1462
|
+
# TYMS_DeoxyUMP_Km = 2.0.µM
|
1463
|
+
# RNR_UDP_Km = 1.0.µM
|
1464
|
+
# TMPK_DeoxyTMP_Km = 12.0.µM
|
1465
|
+
|
1466
|
+
# # === DNA synthesis speed.
|
1467
|
+
|
1468
|
+
# DNA_creation_speed = 3_000_000_000.unit.( SY::MoleAmount ) / 12.h / Cytoplasm_volume
|
1469
|
+
|
1470
|
+
# # === Transitions
|
1471
|
+
|
1472
|
+
# # Synthesis of TMP by TK1.
|
1473
|
+
# #
|
1474
|
+
# TK1_Thymidine_DeoxyTMP = @m.Transition s: { Thymidine: -1, DeoxyTMP: 1 },
|
1475
|
+
# domain: [ Thymidine, TK1, DeoxyT23P, DeoxyCTP, Deoxycytidine, AMP, ADP, ATP ],
|
1476
|
+
# rate: proc { |rc, e, pool1, ci2, ci3, master1, master2, master3|
|
1477
|
+
# ci1 = pool1 * master3 / ( master2 + master3 )
|
1478
|
+
# MMi.( rc, TK1_a, TK1_m, e, TK1_Thymidine_Km,
|
1479
|
+
# ci1 => 13.5.µM, ci2 => 0.8.µM, ci3 => 40.0.µM )
|
1480
|
+
# }
|
1481
|
+
|
1482
|
+
# # Methylation of DeoxyUMP into TMP by TYMS.
|
1483
|
+
# TYMS_DeoxyUMP_DeoxyTMP = @m.Transition s: { DeoxyU12P: -1, DeoxyTMP: 1 },
|
1484
|
+
# domain: [ DeoxyU12P, TYMS, AMP, ADP, ATP ],
|
1485
|
+
# rate: proc { |pool, e, master1, master2, master3|
|
1486
|
+
# rc = pool * master2 / ( master1 + master2 )
|
1487
|
+
# MMi.( rc, TYMS_a, TYMS_m, e, TYMS_DeoxyUMP_Km )
|
1488
|
+
# }
|
1489
|
+
|
1490
|
+
# # Reduction of UDP into DeoxyUDP by RNR.
|
1491
|
+
# RNR_UDP_DeoxyUDP = @m.Transition s: { U12P: -1, DeoxyU12P: 1 },
|
1492
|
+
# domain: [ U12P, RNR, DeoxyU12P, AMP, ADP, ATP ],
|
1493
|
+
# rate: proc { |pool, e, master1, master2, master3|
|
1494
|
+
# rc = pool * master2 / ( master1 + master2 )
|
1495
|
+
# MMi.( rc, RNR_a, RNR_m, e, RNR_UDP_Km )
|
1496
|
+
# }
|
1497
|
+
|
1498
|
+
# # Consumption of TTP by DNA synthesis.
|
1499
|
+
# DeoxyTTP_to_DNA = @m.Transition s: { DeoxyT23P: -1 },
|
1500
|
+
# rate: proc { DNA_creation_speed / 4 }
|
1501
|
+
|
1502
|
+
# # Phosphorylation of TMP into TDP-TTP pool.
|
1503
|
+
# TMPK_DeoxyTMP_DeoxyTDP = @m.Transition s: { DeoxyTMP: -1, TMPK: 0, DeoxyT23P: 1 },
|
1504
|
+
# domain: [ DeoxyTMP, TMPK, ADP, DeoxyT23P, DeoxyGMP, AMP, ATP ],
|
1505
|
+
# rate: proc { |rc, e, ci1, pool, ci4, master1, master3|
|
1506
|
+
# master2 = ci1
|
1507
|
+
# ci2 = pool * master2 / ( master2 + master3 )
|
1508
|
+
# ci3 = pool * master3 / ( master2 + master3 )
|
1509
|
+
# MMi.( rc, TMPK_a, TMPK_m, e, TMPK_DeoxyTMP_Km,
|
1510
|
+
# ci1 => 250.0.µM, ci2 => 30.0.µM, ci3 => 750.µM, ci4 => 117.µM )
|
1511
|
+
# }
|
1512
|
+
# end
|
1513
|
+
|
1514
|
+
# it "should work" do
|
1515
|
+
# # === Simulation execution
|
1516
|
+
# @m.run!
|
1517
|
+
# # === Plotting of the results
|
1518
|
+
# @m.plot_recording
|
1519
|
+
# sleep 20
|
1520
|
+
# end
|
1521
|
+
# end
|