y_petri 2.0.15 → 2.1.3

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.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/y_petri/{manipulator → agent}/hash_key_pointer.rb +2 -2
  3. data/lib/y_petri/agent/petri_net_related.rb +115 -0
  4. data/lib/y_petri/{manipulator → agent}/selection.rb +2 -1
  5. data/lib/y_petri/{manipulator/simulation_related_methods.rb → agent/simulation_related.rb} +93 -110
  6. data/lib/y_petri/agent.rb +22 -0
  7. data/lib/y_petri/core/timed/euler.rb +20 -0
  8. data/lib/y_petri/core/timed/pseudo_euler.rb +31 -0
  9. data/lib/y_petri/core/timed/quasi_euler.rb +23 -0
  10. data/lib/y_petri/core/timed.rb +70 -0
  11. data/lib/y_petri/core/timeless/pseudo_euler.rb +20 -0
  12. data/lib/y_petri/core/timeless.rb +12 -0
  13. data/lib/y_petri/core.rb +100 -0
  14. data/lib/y_petri/dsl.rb +66 -0
  15. data/lib/y_petri/fixed_assets.rb +7 -0
  16. data/lib/y_petri/net/element_access.rb +239 -0
  17. data/lib/y_petri/net/state/feature/delta.rb +88 -0
  18. data/lib/y_petri/net/state/feature/firing.rb +57 -0
  19. data/lib/y_petri/net/state/feature/flux.rb +58 -0
  20. data/lib/y_petri/net/state/feature/gradient.rb +75 -0
  21. data/lib/y_petri/net/state/feature/marking.rb +62 -0
  22. data/lib/y_petri/net/state/feature.rb +79 -0
  23. data/lib/y_petri/net/state/features/dataset.rb +135 -0
  24. data/lib/y_petri/net/state/features/record.rb +50 -0
  25. data/lib/y_petri/net/state/features.rb +126 -0
  26. data/lib/y_petri/net/state.rb +121 -0
  27. data/lib/y_petri/net/timed.rb +8 -0
  28. data/lib/y_petri/net/visualization.rb +3 -3
  29. data/lib/y_petri/net.rb +73 -77
  30. data/lib/y_petri/place.rb +8 -3
  31. data/lib/y_petri/simulation/dependency.rb +107 -0
  32. data/lib/y_petri/simulation/element_representation.rb +20 -0
  33. data/lib/y_petri/simulation/elements/access.rb +57 -0
  34. data/lib/y_petri/simulation/elements.rb +45 -0
  35. data/lib/y_petri/simulation/feature_set.rb +21 -0
  36. data/lib/y_petri/simulation/initial_marking/access.rb +55 -0
  37. data/lib/y_petri/simulation/initial_marking.rb +15 -0
  38. data/lib/y_petri/simulation/marking_clamps/access.rb +34 -0
  39. data/lib/y_petri/simulation/marking_clamps.rb +18 -0
  40. data/lib/y_petri/simulation/marking_vector/access.rb +106 -0
  41. data/lib/y_petri/simulation/marking_vector.rb +156 -0
  42. data/lib/y_petri/simulation/matrix.rb +64 -0
  43. data/lib/y_petri/simulation/place_mapping.rb +62 -0
  44. data/lib/y_petri/simulation/place_representation.rb +74 -0
  45. data/lib/y_petri/simulation/places/access.rb +121 -0
  46. data/lib/y_petri/simulation/places/clamped.rb +8 -0
  47. data/lib/y_petri/simulation/places/free.rb +8 -0
  48. data/lib/y_petri/simulation/places/types.rb +25 -0
  49. data/lib/y_petri/simulation/places.rb +41 -0
  50. data/lib/y_petri/simulation/recorder.rb +54 -0
  51. data/lib/y_petri/simulation/timed/recorder.rb +53 -0
  52. data/lib/y_petri/simulation/timed.rb +161 -261
  53. data/lib/y_petri/simulation/timeless/recorder.rb +25 -0
  54. data/lib/y_petri/simulation/timeless.rb +35 -0
  55. data/lib/y_petri/simulation/transition_representation/A.rb +58 -0
  56. data/lib/y_petri/simulation/transition_representation/S.rb +45 -0
  57. data/lib/y_petri/simulation/transition_representation/T.rb +80 -0
  58. data/lib/y_petri/simulation/transition_representation/TS.rb +46 -0
  59. data/lib/y_petri/simulation/transition_representation/Ts.rb +32 -0
  60. data/lib/y_petri/simulation/transition_representation/a.rb +30 -0
  61. data/lib/y_petri/simulation/transition_representation/s.rb +29 -0
  62. data/lib/y_petri/simulation/transition_representation/t.rb +37 -0
  63. data/lib/y_petri/simulation/transition_representation/tS.rb +38 -0
  64. data/lib/y_petri/simulation/transition_representation/ts.rb +32 -0
  65. data/lib/y_petri/simulation/transition_representation/types.rb +62 -0
  66. data/lib/y_petri/simulation/transition_representation.rb +79 -0
  67. data/lib/y_petri/simulation/transitions/A.rb +40 -0
  68. data/lib/y_petri/simulation/transitions/S.rb +24 -0
  69. data/lib/y_petri/simulation/transitions/T.rb +34 -0
  70. data/lib/y_petri/simulation/transitions/TS.rb +57 -0
  71. data/lib/y_petri/simulation/transitions/Ts.rb +60 -0
  72. data/lib/y_petri/simulation/transitions/a.rb +8 -0
  73. data/lib/y_petri/simulation/transitions/access.rb +186 -0
  74. data/lib/y_petri/simulation/transitions/s.rb +9 -0
  75. data/lib/y_petri/simulation/transitions/t.rb +22 -0
  76. data/lib/y_petri/simulation/transitions/tS.rb +55 -0
  77. data/lib/y_petri/simulation/transitions/ts.rb +58 -0
  78. data/lib/y_petri/simulation/transitions/types.rb +98 -0
  79. data/lib/y_petri/simulation/transitions.rb +21 -0
  80. data/lib/y_petri/simulation.rb +176 -781
  81. data/lib/y_petri/transition/assignment.rb +7 -5
  82. data/lib/y_petri/transition/construction.rb +119 -187
  83. data/lib/y_petri/transition/init.rb +311 -0
  84. data/lib/y_petri/transition/ordinary_timeless.rb +8 -6
  85. data/lib/y_petri/transition/timed.rb +11 -18
  86. data/lib/y_petri/transition.rb +104 -132
  87. data/lib/y_petri/version.rb +1 -1
  88. data/lib/y_petri/world/dependency.rb +40 -0
  89. data/lib/y_petri/world/petri_net_related.rb +61 -0
  90. data/lib/y_petri/{workspace/simulation_related_methods.rb → world/simulation_related.rb} +42 -49
  91. data/lib/y_petri/world.rb +27 -0
  92. data/lib/y_petri.rb +47 -99
  93. data/test/{manipulator_test.rb → agent_test.rb} +19 -17
  94. data/{lib/y_petri → test/examples}/demonstrator.rb +0 -0
  95. data/{lib/y_petri → test/examples}/demonstrator_2.rb +1 -0
  96. data/{lib/y_petri → test/examples}/demonstrator_3.rb +0 -0
  97. data/{lib/y_petri → test/examples}/demonstrator_4.rb +0 -0
  98. data/test/examples/example_2.rb +16 -0
  99. data/test/{manual_examples.rb → examples/manual_examples.rb} +0 -0
  100. data/test/net_test.rb +126 -121
  101. data/test/place_test.rb +1 -1
  102. data/test/sim_test +565 -0
  103. data/test/simulation_test.rb +338 -264
  104. data/test/transition_test.rb +77 -174
  105. data/test/world_mock.rb +12 -0
  106. data/test/{workspace_test.rb → world_test.rb} +19 -20
  107. data/test/y_petri_test.rb +4 -5
  108. metadata +101 -26
  109. data/lib/y_petri/dependency_injection.rb +0 -45
  110. data/lib/y_petri/manipulator/petri_net_related_methods.rb +0 -74
  111. data/lib/y_petri/manipulator.rb +0 -20
  112. data/lib/y_petri/net/selections.rb +0 -209
  113. data/lib/y_petri/simulation/collections.rb +0 -460
  114. data/lib/y_petri/workspace/parametrized_subclassing.rb +0 -22
  115. data/lib/y_petri/workspace/petri_net_related_methods.rb +0 -88
  116. data/lib/y_petri/workspace.rb +0 -16
  117. data/test/timed_simulation_test.rb +0 -153
@@ -0,0 +1,106 @@
1
+ #encoding: utf-8
2
+
3
+ # A mixin.
4
+ #
5
+ class YPetri::Simulation::MarkingVector
6
+ module Access
7
+ # Marking of all places (as a column vector).
8
+ #
9
+ def m_vector ids=nil
10
+ if ids.nil? then
11
+ msg = "Marking vector not established yet!"
12
+ @m_vector or fail TypeError, msg
13
+ else
14
+ m_vector.select( ids )
15
+ end
16
+ end
17
+
18
+ # Marking of all places (as array).
19
+ #
20
+ def m ids=nil
21
+ m_vector( ids ).to_a
22
+ end
23
+
24
+ # Marking of all places (as hash).
25
+ #
26
+ def place_m ids=nil
27
+ m_vector( ids ).to_hash
28
+ end
29
+
30
+ # Marking of all places (as hash with place names as keys).
31
+ #
32
+ def p_m ids=nil
33
+ places( ids ).names( true ) >> m( ids )
34
+ end
35
+ alias pn_m p_m
36
+ alias pm p_m
37
+
38
+ # Modifies the marking vector. Takes one argument. If the argument is a hash
39
+ # of pairs { place => new value }, only the specified places' markings are
40
+ # updated. If the argument is an array, it must match the number of places
41
+ # in the simulation, and all marking values are updated.
42
+ #
43
+ def update_m new_m
44
+ case new_m
45
+ when Hash then # assume { place => marking } hash
46
+ new_m.each_pair { |id, val| m_vector.set( id, val ) }
47
+ when Array then
48
+ msg = "T be a collection with size == number of net's places!"
49
+ fail TypeError, msg unless new_m.size == places.size
50
+ update_m( places >> new_m )
51
+ else # convert it with #each
52
+ update_m( new_m.each.to_a )
53
+ end
54
+ end
55
+
56
+ # Marking vector of free places.
57
+ #
58
+ def marking_vector ids=nil
59
+ m_vector free_places( ids )
60
+ end
61
+
62
+ # Marking of free places (as array).
63
+ #
64
+ def marking ids=nil
65
+ marking_vector( ids ).to_a
66
+ end
67
+
68
+ # Marking of free places (as hash).
69
+ #
70
+ def place_marking ids=nil
71
+ marking_vector( ids ).to_hash
72
+ end
73
+
74
+ # Marking of free places (as hash with place names as keys).
75
+ #
76
+ def p_marking ids=nil
77
+ marking_vector( ids ).to_h
78
+ end
79
+ alias pn_marking p_marking
80
+
81
+ # Modifies the marking vector. Like +#update_m+, but the places must be
82
+ # free places, and if the argument is an array, it must match the number
83
+ # of free places in the simulation's net.
84
+ #
85
+ def update_marking new_m
86
+ case new_m
87
+ when Hash then # assume { place => marking } hash
88
+ ( free_places( *new_m.keys ) >> new_m.values )
89
+ .each_pair { |id, val| m_vector.set( id, val ) }
90
+ when Array then
91
+ msg = "T be a collection with size == number of net's free places!"
92
+ fail TypeError, msg unless new_m.size == free_places.size
93
+ update_m( free_places >> new_m )
94
+ else # convert it with #each
95
+ update_marking( new_m.each.to_a )
96
+ end
97
+ end
98
+
99
+ # Expects a Δ marking vector for free places and performs the specified
100
+ # change on the marking vector of the simulation.
101
+ #
102
+ def increment_marking Δ_free
103
+ @m_vector += f2a * Δ_free
104
+ end
105
+ end # module Access
106
+ end # class YPetri::Simulation::MarkingVector
@@ -0,0 +1,156 @@
1
+ # Basic elements of a simulation, a mixin intended for YPetri::Simulation.
2
+ #
3
+ class YPetri::Simulation
4
+ class MarkingVector < Matrix
5
+ include Dependency
6
+
7
+ class << self
8
+ include Dependency
9
+
10
+ attr_reader :annotation
11
+
12
+ # Constructs a marking vector from a hash places >> values, or from
13
+ # an array, in which case, it is assumed that the marking vector
14
+ # corresponds to all the places in the simulation.
15
+ #
16
+ def [] arg
17
+ case arg
18
+ when Hash then annotated_with( arg.keys )[ arg.values ]
19
+ when Array then
20
+ if annotation then
21
+ msg = "The size of the argument (#{arg.size}) does not " +
22
+ "correspond to the annotation size (#{annotation.size})!"
23
+ fail ArgumentError, msg unless arg.size == annotation.size
24
+ column_vector arg
25
+ else
26
+ annotated_with( places )[ args ]
27
+ end
28
+ else
29
+ self[ args.each.to_a ]
30
+ end
31
+ end
32
+
33
+ # Returns a subclass of self annotated with the supplied places.
34
+ #
35
+ def annotated_with place_ids
36
+ annot = if annotation then
37
+ annotation.subset place_ids
38
+ else
39
+ places( place_ids )
40
+ end
41
+ Class.new self do @annotation = annot end
42
+ end
43
+
44
+ # Without arguments, constructs the starting marking vector for all places,
45
+ # using either initial values, or clamp values. Optionally, places can be
46
+ # specified, for which the starting vector is returned.
47
+ #
48
+ def starting place_ids=nil
49
+ st = -> p { p.free? ? p.initial_marking : p.clamp } # starting value
50
+ if place_ids.nil? then
51
+ return starting places if annotation.nil?
52
+ self[ annotation.map &st ]
53
+ else
54
+ annotated_with( place_ids ).starting
55
+ end
56
+ end
57
+
58
+ # Without arguments, constructs a zero marking vector for all places.
59
+ # Optionally, places can be specified, for which the zero vector is
60
+ # returned.
61
+ #
62
+ def zero( place_ids=nil )
63
+ starting( place_ids ) * 0
64
+ end
65
+ end
66
+
67
+ delegate :simulation, to: "self.class"
68
+
69
+ # Creates a subset of this marking vector.
70
+ #
71
+ def select place_ids, &block
72
+ if block_given? then
73
+ msg = "If block is given, arguments are not allowed!"
74
+ fail ArgumentError, msg unless place_ids.empty?
75
+ select annotation.select( &block )
76
+ else
77
+ pp = places( place_ids )
78
+ annotated_subcl = self.class.annotated_with( pp )
79
+ annotated_subcl[ pp.map { |p| fetch p } ]
80
+ end
81
+ end
82
+
83
+ # Modifying the vector elements.
84
+ #
85
+ def set id, value
86
+ self[ index( id ), 0 ] = value
87
+ end
88
+
89
+ # Whole vector is reset to a given collection of values. If no argument is
90
+ # given, starting vector is used.
91
+ #
92
+ def reset! arg=self.class.starting
93
+ arg.each.to_a.zip( annotation ).map { |value, place| set place, value }
94
+ end
95
+
96
+ # Access of the vector elements.
97
+ #
98
+ def fetch id
99
+ self[ index( id ), 0 ]
100
+ end
101
+
102
+ # Annotation.
103
+ #
104
+ def annotation
105
+ self.class.annotation
106
+ end
107
+
108
+ # Index of a place.
109
+ #
110
+ def index id
111
+ if id.is_a? Numeric then
112
+ fail RangeError, "Numeric index must be within 0..#{size}" unless
113
+ ( 0..size ) === id
114
+ else
115
+ annotation.index place( id )
116
+ end
117
+ end
118
+
119
+ # Marking vector size -- depends on the annotation.
120
+ #
121
+ def size
122
+ annotation.size
123
+ end
124
+
125
+ # Converts the marking vector (which is a column vector) into an array.
126
+ #
127
+ def to_a
128
+ ( 0..size - 1 ).map { |i| self[ i, 0 ] }
129
+ end
130
+
131
+ # Converts the marking vector into a hash annotation >> values.
132
+ #
133
+ def to_hash
134
+ annotation >> to_a
135
+ end
136
+
137
+ # Converts the marking vector into a hash annotation_names >> values.
138
+ #
139
+ def to_h
140
+ annotation.names( true ) >> to_a
141
+ end
142
+
143
+ # Converts the marking vector into a hash source places >> values.
144
+ #
145
+ def to_hash_with_source_places
146
+ annotation.sources >> to_a
147
+ end
148
+
149
+ # Builds the assignment closure.
150
+ #
151
+ def increment_closure
152
+ indices_of_free_places = annotation.free.map { |p| annotation.index p }
153
+ increment_at_indices_closure( indices: indices_of_free_places )
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,64 @@
1
+ #encoding: utf-8
2
+
3
+ require 'matrix'
4
+
5
+ # Patches the Matrix class with methods that generate code for direct vector
6
+ # access.
7
+ #
8
+ class Matrix
9
+ class << self
10
+ # Builds a code string for accessing the vector values at given indices.
11
+ #
12
+ def column_vector_access_code vector: (fail ArgumentError, "No vector!"),
13
+ indices: (fail ArgumentError, "No indices!")
14
+ indices.map { |i| "#{vector}[#{i}, 0]" }.join( ", " )
15
+ end
16
+
17
+ # Builds a code string for assigning to a vector at given indices.
18
+ #
19
+ def column_vector_assignment_code vector: (fail ArgumentError, "No vector!"),
20
+ indices: (fail ArgumentError, "No indices!"),
21
+ source: (fail ArgumentError, "No source!")
22
+ code_lines = indices.map.with_index do |i, source_pos|
23
+ "#{vector}.send( :[]=, #{i}, 0, #{source}.fetch( #{source_pos} ) )" if i
24
+ end
25
+ code_lines.compact.join( "\n" ) << "\n"
26
+ end
27
+
28
+ # Builds a code string for incrementing a vector at given indices. Source is
29
+ # a vector.
30
+ #
31
+ def column_vector_increment_code vector: (fail ArgumentError, "No vector!"),
32
+ indices: (fail ArgumentError, "No indices!"),
33
+ source: (fail ArgumentError, "No source!")
34
+ code_lines = indices.map.with_index do |i, source_pos|
35
+ "#{vector}.send( :[]=, #{i}, 0, %s )" %
36
+ "#{vector}[#{i}, 0] + #{source}[#{source_pos}, 0]" if i
37
+ end
38
+ code_lines.compact.join( "\n" ) << "\n"
39
+ end
40
+
41
+ # Builds a code string for incrementing a vector at given indices. Source is
42
+ # an array.
43
+ #
44
+ def column_vector_increment_by_array_code vector: (fail ArgumentError, "No vector!"),
45
+ indices: (fail ArgumentError, "No indices!"),
46
+ source: (fail ArgumentError, "No source!")
47
+ code_lines = indices.map.with_index do |i, source_pos|
48
+ "#{vector}.send( :[]=, #{i}, 0, %s )" %
49
+ "#{vector}[#{i}, 0] + #{source}[#{source_pos}]" if i
50
+ end
51
+ code_lines.compact.join( "\n" ) << "\n"
52
+ end
53
+ end
54
+
55
+ # Builds a closure for incrementing a column at given indices.
56
+ #
57
+ def increment_at_indices_closure indices: (fail ArgumentError, "No indices!")
58
+ v = self
59
+ eval "-> delta do\n%s\nend" %
60
+ self.class.column_vector_increment_code( vector: "v",
61
+ indices: indices,
62
+ source: "delta" )
63
+ end
64
+ end
@@ -0,0 +1,62 @@
1
+ # Manages the initial marking of a simulation.
2
+ #
3
+ class YPetri::Simulation
4
+ class PlaceMapping < Hash
5
+ include Dependency
6
+
7
+ class << self
8
+ # Initializes the initial marking from a hash.
9
+ #
10
+ def load hash
11
+ new.tap do |inst|
12
+ hash.with_values do |v|
13
+ v = v.marking if v.is_a? YPetri::Place
14
+ if v.is_a? Proc then v.call else v end
15
+ end.tap &inst.method( :load )
16
+ end
17
+ end
18
+ end
19
+
20
+ delegate :simulation, to: "self.class"
21
+
22
+ alias places keys
23
+
24
+ # Returns the initial marking as a column vector.
25
+ #
26
+ def vector
27
+ simulation.MarkingVector[ self ]
28
+ end
29
+ alias to_marking_vector vector
30
+
31
+ # Sets the mapping value for a given place to a given value.
32
+ #
33
+ def set place_id, to: (fail ArgumentError, "No :to value!")
34
+ update place( place_id ) => to
35
+ end
36
+
37
+ # Loads initial the mappings from a hash places >> values.
38
+ #
39
+ def load( hash )
40
+ hash.each { |place, value| set place, to: value }
41
+ end
42
+
43
+ # Fetches the value for a place.
44
+ #
45
+ def of place_id
46
+ fetch place( place_id )
47
+ end
48
+
49
+ # Deletes the value for a place.
50
+ #
51
+ def delete place_id
52
+ super place( place_id )
53
+ end
54
+
55
+ # Returns a hash, whose keys have been replaced with source places of
56
+ # the place representations in this place mapping.
57
+ #
58
+ def keys_to_source_places
59
+ with_keys do |key| key.source end
60
+ end
61
+ end # class InitialMarking
62
+ end # class YPetri::Simulation
@@ -0,0 +1,74 @@
1
+ #encoding: utf-8
2
+
3
+ # Representation of a YPetri::Place inside a YPetri::Simulation instance.
4
+ #
5
+ class YPetri::Simulation
6
+ class PlaceRepresentation < ElementRepresentation
7
+ # Index
8
+ def m_vector_index
9
+ places.index( self )
10
+ end
11
+
12
+ # Expect a single YPetri place as an argument.
13
+ #
14
+ def initialize net_place
15
+ super
16
+ end
17
+
18
+ # Setter of clamp.
19
+ #
20
+ def clamp=( value )
21
+ simulation.set_marking_clamp( of: self, to: value )
22
+ end
23
+
24
+ # Setter of initial marking.
25
+ #
26
+ def initial_marking=( value )
27
+ simulation.set_initial_marking( of: self, to: value )
28
+ end
29
+
30
+ # Marking clamp value (or nil, if the place is clamped).
31
+ #
32
+ def marking_clamp
33
+ simulation.marking_clamp of: self if clamped?
34
+ end
35
+ alias clamp marking_clamp
36
+
37
+ # Initial marking value (or nil, if the place is free).
38
+ #
39
+ def initial_marking
40
+ simulation.initial_marking[ self ] if free?
41
+ end
42
+
43
+ # Is the place free in the current simulation?
44
+ #
45
+ def free?
46
+ simulation.initial_marking.places.include? self
47
+ end
48
+
49
+ # Is the place clamped in the current simulation?
50
+ #
51
+ def clamped?
52
+ simulation.marking_clamps.places.include? self
53
+ end
54
+
55
+ # Set the marking of this place in the simulation.
56
+ #
57
+ def m=( value )
58
+ m_vector.set self, value
59
+ end
60
+
61
+ # Alias of #m=
62
+ #
63
+ def marking=( value )
64
+ m=( value )
65
+ end
66
+
67
+ # Get the current marking of this place in the simulation.
68
+ #
69
+ def m
70
+ m_vector[ self ]
71
+ end
72
+ alias marking m
73
+ end # class PlaceRepresentation
74
+ end # class YPetri::Simulation
@@ -0,0 +1,121 @@
1
+ #encoding: utf-8
2
+
3
+ # Simulation mixin providing access to places.
4
+ #
5
+ class YPetri::Simulation::Places
6
+ module Access
7
+ # With no arguments, a reader of @f2a -- the correspondence matrix between
8
+ # free places and all places. If argument is given, it is assumed to be
9
+ # a column vector, and multiplication is performed.
10
+ #
11
+ def f2a arg=nil
12
+ if arg.nil? then @f2a else @f2a * arg end
13
+ end
14
+
15
+ # With no arguments, a reader of @c2a -- the correspondence matrix between
16
+ # clamped places and all places. If argument is given, it is assumed to be
17
+ # a column vector, and multiplication is performed.
18
+ #
19
+ def c2a arg=nil
20
+ if arg.nil? then @c2a else @c2a * arg end
21
+ end
22
+
23
+ # Does a place belong to the simulation?
24
+ #
25
+ def includes_place? id
26
+ true.tap { begin; place id
27
+ rescue NameError, TypeError
28
+ return false
29
+ end }
30
+ end
31
+ alias include_place? includes_place?
32
+
33
+ # Place of the simulation (belonging to the net).
34
+ #
35
+ def p( id )
36
+ place( id ).source
37
+ end
38
+
39
+ # Places of the simulation (belonging to the net).
40
+ #
41
+ def pp( ids=nil )
42
+ places( ids ).sources
43
+ end
44
+
45
+ # Free places of the simulation (belonging to the net).
46
+ #
47
+ def free_pp( ids=nil )
48
+ free_places( ids ).sources
49
+ end
50
+
51
+ # Clamped places of the simulation (belonging to the net).
52
+ #
53
+ def clamped_pp( ids=nil )
54
+ clamped_places( ids ).sources
55
+ end
56
+
57
+ # Places' names. Arguments, if any, are treated as in +#places+ method.
58
+ #
59
+ def pn( ids=nil )
60
+ places( ids ).names
61
+ end
62
+
63
+ # Names of free places. Arguments are handled as with +#free_places+.
64
+ #
65
+ def nfree ids=nil
66
+ free_places( ids ).names
67
+ end
68
+ alias free_pn nfree
69
+
70
+ # Names of free places. Arguments are handled as with +#clamped_places+.
71
+ #
72
+ def nclamped ids=nil
73
+ clamped_places( ids ).names
74
+ end
75
+ alias clamped_pn nclamped
76
+
77
+ protected
78
+
79
+ # Place instance identification.
80
+ #
81
+ def place( id )
82
+ begin
83
+ Place().instance( id )
84
+ rescue NameError, TypeError
85
+ begin
86
+ pl = net.place( id )
87
+ places.find { |p_rep| p_rep.source == pl } ||
88
+ Place().instance( pl.name )
89
+ rescue NameError, TypeError => msg
90
+ raise
91
+ raise TypeError, "The argument #{id} (class #{id.class}) does not identify a " +
92
+ "place instance! (#{msg})"
93
+ end
94
+ end
95
+ end
96
+
97
+ # Without arguments, returns all the places. If arguments are given, they
98
+ # are converted to places before being returned.
99
+ #
100
+ def places( ids=nil )
101
+ return @places if ids.nil?
102
+ Places().load( ids.map { |id| place id } )
103
+ end
104
+
105
+ # Free places. If arguments are given, they must be identify free places,
106
+ # and are converted to them.
107
+ #
108
+ def free_places ids=nil
109
+ return places.free if ids.nil?
110
+ places.free.subset( ids )
111
+ end
112
+
113
+ # Clamped places. If arguments are given, they must be identify clamped
114
+ # places, and are converted to them.
115
+ #
116
+ def clamped_places ids=nil
117
+ return places.clamped if ids.nil?
118
+ places.clamped.subset( ids )
119
+ end
120
+ end # module Access
121
+ end # class YPetri::Simulation::Places
@@ -0,0 +1,8 @@
1
+ #encoding: utf-8
2
+
3
+ # A mixin for collections of free places.
4
+ #
5
+ class YPetri::Simulation::Places
6
+ module Type_clamped
7
+ end # Type_clamped
8
+ end # class YPetri::Simulation::Places
@@ -0,0 +1,8 @@
1
+ #encoding: utf-8
2
+
3
+ # A mixin for collections of free places.
4
+ #
5
+ class YPetri::Simulation::Places
6
+ module Type_free
7
+ end # Type_free
8
+ end # class YPetri::Simulation::Places
@@ -0,0 +1,25 @@
1
+ #encoding: utf-8
2
+
3
+ # A mixin with place type selectors.
4
+ #
5
+ class YPetri::Simulation
6
+ class Places < Elements
7
+ module Types
8
+ # Subset of free places, if any.
9
+ #
10
+ def free
11
+ ( @Type_free ||= Class.new( self.class ).tap do |klass|
12
+ klass.class_exec { include Type_free }
13
+ end ).load subset( &:free? )
14
+ end
15
+
16
+ # Subset of clamped places, if any.
17
+ #
18
+ def clamped
19
+ ( @Type_clamped ||= Class.new( self.class ).tap do |klass|
20
+ klass.class_exec { include Type_clamped }
21
+ end ).load subset( &:clamped? )
22
+ end
23
+ end # Types
24
+ end # class Places
25
+ end # class YPetri::Simulation
@@ -0,0 +1,41 @@
1
+ #encoding: utf-8
2
+
3
+ require_relative 'places/types'
4
+ require_relative 'places/free'
5
+ require_relative 'places/clamped'
6
+
7
+ # Place collection for YPetri::Simulation.
8
+ #
9
+ class YPetri::Simulation::Places
10
+ include Types
11
+
12
+ # Pushes a place to the collection.
13
+ #
14
+ def push place
15
+ p = begin
16
+ net.place( place )
17
+ rescue NameError, TypeError
18
+ return super place( place )
19
+ end
20
+ super p.name ? Place().new( p, name: p.name ) : Place().new( p )
21
+ end
22
+
23
+ # Ensures that all the places that are not clamped have their initial marking
24
+ # set. Optional argument :use_default_marking is set to _true_ by default, in
25
+ # which case own default marking of the source places is used if it was not
26
+ # specified when constructing the simulation. If set to _false_, then presence
27
+ # of places with missing initial marking simply raises errors.
28
+ #
29
+ def complete_initial_marking( use_default_marking: true )
30
+ missing = reject { |pl| ( free + clamped ).include? pl }
31
+ unless use_default_marking
32
+ fail TypeError, "All places must have default marking or clamp!" unless
33
+ missing.empty?
34
+ end
35
+ missing.each { |pl|
36
+ dflt = pl.source.default_marking
37
+ fail TypeError, "Source's default marking is missing (nil)!" if dflt.nil?
38
+ simulation.send :set_initial_marking, { of: pl, to: dflt }
39
+ }
40
+ end
41
+ end # class YPetri::Simulation::Places