y_petri 2.0.3 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/lib/y_petri/place.rb CHANGED
@@ -1,87 +1,103 @@
1
1
  # -*- coding: utf-8 -*-
2
- # This class represents Petri net places.
2
+
3
+ require_relative 'dependency_injection'
4
+ require_relative 'place/guard'
5
+ require_relative 'place/arcs'
6
+
7
+ # Represents a Petri net place.
3
8
  #
4
9
  class YPetri::Place
5
- USE_QUANTUM = false
6
10
  include NameMagic
11
+ include YPetri::DependencyInjection
7
12
 
8
13
  attr_reader :quantum
14
+ attr_reader :guards
9
15
  attr_accessor :default_marking
10
- attr_accessor :marking # instance-attached marking
11
- alias :value :marking
12
- alias :m :marking
13
-
14
- # Alias for #marking=
15
- #
16
- def value=( marking ); self.marking = marking end
17
-
18
- # Alias for #marking=
19
- #
20
- def m=( marking ); self.marking = marking end
21
-
22
- # Transitions that can directly add/remove tokens from this place.
23
- # It is aliased as #upstream_transitions and #ϝ. (ϝ (Greek digamma) looks
24
- # like "function", which we know from spreadsheet software: A collection
25
- # of transitions directly affecting marking of this place.)
26
- #
27
- attr_reader :upstream_arcs
28
- alias :upstream_transitions :upstream_arcs
29
- alias :ϝ :upstream_arcs
30
-
31
- # Transitions whose action directly depends on this place. Aliased
32
- # as #downstream_transitions.
33
- #
34
- attr_reader :downstream_arcs
35
- alias :downstream_transitions :downstream_arcs
16
+ attr_writer :marking
36
17
 
37
18
  # Named parameters supplied upon place initialization may include:
38
19
  #
39
- # * :marking (alias :m)
40
- # * :default_marking (alias :dflt_m or :m!)
41
- # * :quantum (alias :q)
42
- #
43
- # While 'marking' is a standard Petri net concept, and default marking
44
- # is self-explanatory, place quantum is the concept by which YPetri
45
- # reconciles with letter H in the abbreviation HFPN. YPetri places are
46
- # always considered discrete, and it is true insomuch, as their marking
47
- # is represented by a finite-digit number. The place quantum feature is
48
- # not supported yet, but in future, it should enable smooth transition
49
- # between continuous and stochastic modes of simulation.
50
- #
51
- def initialize *aa; oo = aa.extract_options!
52
- # set domain and codomain of the place empty
53
- @upstream_arcs = []
54
- @downstream_arcs = []
55
- @quantum = oo.may_have( :quantum, syn!: :q ) || 1
56
- @default_marking = oo.may_have( :default_marking, syn!: [ :dflt_m, :m! ] )
57
- @marking = oo.may_have( :marking, syn!: :m ) || @default_marking
20
+ # * :marking
21
+ # * :default_marking
22
+ # * :quantum
23
+ # * :guard
24
+ #
25
+ # Those familiar with Petri nets need no introduction into _marking_
26
+ # attribute of a Petri net place. However, _quantum_ is a relatively uncommon
27
+ # concept in the context of Petri nets. +YPetri+ introduces quantum as a
28
+ # replacement for the hybrid-ness of Hybrid Functional Petri Nets (HFPNs).
29
+ # Formally, +YPetri+ is a discrete functional Petri net (FPN). The quantum
30
+ # is a numeric representation of a token: The smallest number by which the
31
+ # numeric representation of the place's marking can change. This is intended
32
+ # to enable smooth transition between continuous and stochastic simulation
33
+ # depending on pre-defined statistical settings.
34
+ #
35
+ # The :guard named argument and optional block specification allows to specify
36
+ # one marking guard already upon place initialization. This is done by putting
37
+ # the NL assertion string of the guard under the :guard named argument, and
38
+ # supplying the guard block to the constructor. More guards can be defined
39
+ # later for the place using its +#guard+ method.
40
+ #
41
+ # If no guard block is supplied, default guards are constructed based on the
42
+ # type of the marking or default marking supplied upon initialization. For
43
+ # numeric marking except complex numbers, the default type guard allows all
44
+ # +Numeric+ types except complex numbers, and the default value guard prohibits
45
+ # negative values. For all other classes, there is just one guard enforcing
46
+ # the class compliance of the marking.
47
+ #
48
+ # To construct a place with no guards whatsoever, set :guard named argument
49
+ # to _false_.
50
+ #
51
+ def initialize quantum: 1,
52
+ default_marking: nil,
53
+ marking: nil,
54
+ guard: L!,
55
+ &block
56
+ @upstream_arcs, @downstream_arcs, @guards = [], [], [] # init to empty
57
+ @quantum, @default_marking = quantum, default_marking
58
+ @marking = marking || default_marking
59
+
60
+ # Check in :guard named argument and &block.
61
+ if guard.ℓ? then # guard NL assertion not given, use block or default guards
62
+ block ? guard( &block ) : add_default_guards!( @marking )
63
+ elsif guard then # guard NL assertion given
64
+ fail ArgumentError, "No guard block given!" unless block
65
+ guard( guard, &block )
66
+ else
67
+ fail ArgumentError, "Block given, but :guard set to falsey!" if block
68
+ end
69
+ end
70
+
71
+ # Getter of +@marking+ attribute.
72
+ #
73
+ def m; @marking end
74
+ alias value m
75
+
76
+ # This method, which acts as a simple getter of +@marking+ attribute if no
77
+ # block is supplied to it, is overloaded to act as +#guard+ method frontend
78
+ # if a guard block is supplied. The reason is because this
79
+ #
80
+ # marking "should be a number" do |m| fail unless m.is_a? Numeric end
81
+ #
82
+ # reads better than
83
+ #
84
+ # guard "should be a number" do |m| fail unless m.is_a? Numeric end
85
+ #
86
+ # {See #guard method}[rdoc-ref:YPetri::guard] for more information.
87
+ #
88
+ def marking *args, &block
89
+ return @marking if args.empty?
90
+ fail ArgumentError, "Too many arguments!" if args.size > 1
91
+ guard args[0], &block
58
92
  end
59
93
 
60
- # Returns an array of all the transitions connected to the place.
61
- #
62
- def arcs
63
- upstream_arcs | downstream_arcs
64
- end
65
-
66
- # Returns the union of domains of the transitions associated
67
- # with the upstream arcs of this place.
94
+ # Alias for #marking=
68
95
  #
69
- def precedents
70
- upstream_transitions
71
- .map( &:upstream_places )
72
- .reduce( [], :| )
73
- end
74
- alias :upstream_places :precedents
96
+ def value=( marking ); self.marking = marking end
75
97
 
76
- # Returns the union of codomains of the transitions associated
77
- # with the downstream arcs originating from this place.
98
+ # Alias for #marking=
78
99
  #
79
- def dependents
80
- downstream_transitions
81
- .map( &:downstream_places )
82
- .reduce( [], :| )
83
- end
84
- alias :downstream_places :dependents
100
+ def m=( marking ); self.marking = marking end
85
101
 
86
102
  # Adds tokens to the place.
87
103
  #
@@ -91,7 +107,7 @@ class YPetri::Place
91
107
 
92
108
  # Subtracts tokens from the place.
93
109
  #
94
- def subtract( amount_of_tokens)
110
+ def subtract( amount_of_tokens )
95
111
  @marking -= amount_of_tokens
96
112
  end
97
113
 
@@ -101,87 +117,28 @@ class YPetri::Place
101
117
  @marking = @default_marking
102
118
  end
103
119
 
104
- # Firing of upstream transitions regardless of cocking. (To #fire
105
- # transitions, they have to be cocked with #cock method; the firing
106
- # methods with exclamation marks disregard cocking.)
107
- #
108
- def fire_upstream!
109
- @upstream_arcs.each &:fire!
110
- end
111
- alias :fire! :fire_upstream!
112
-
113
- # Fires whole upstream portion of the net.
114
- #
115
- def fire_upstream_recursively
116
- # LATER: so far, implemented without concerns about infinite loops
117
- # LATER: This as a global hash { place => fire_list }
118
- @upstream_arcs.each &:fire_upstream_recursively
119
- end
120
- alias :fire_upstream! :fire_upstream_recursively
121
-
122
- # Firing of downstream transitions regardless of cocking. (To #fire
123
- # transitions, they have to be cocked with #cock method; the firing
124
- # methods with exclamation marks disregard cocking.)
125
- #
126
- def fire_downstream!
127
- @downstream_arcs.each &:fire!
128
- end
129
-
130
- # Fires whole downstream portion of the net.
131
- #
132
- def fire_downstream_recursively
133
- # LATER: so far, implemented withoud concerns about infinite loops
134
- # LATER: This as a global hash { place => fire_list }
135
- @downstream_arcs.each &:fire_downstream_recursively
136
- end
137
- alias :fire_downstream! :fire_downstream_recursively
138
-
139
120
  # Produces the inspect string of the place.
140
121
  #
141
122
  def inspect
142
123
  n, m, d, q = instance_description_strings
143
- "#<Place: #{ ( USE_QUANTUM ? [n, m, d, q] : [n, m, d] ).join ', ' } >"
124
+ "#<Place: #{ ( [n, m, d, q].compact ).join ', ' }>"
144
125
  end
145
126
 
146
127
  # Returns a string briefly describing the place.
147
128
  #
148
129
  def to_s
149
130
  n, m = name, marking
150
- "#{n.nil? ? 'Place' : n}[ #{m.nil? ? 'nil' : m} ]"
131
+ "#{n.nil? ? 'Place' : n}[#{m.nil? ? 'nil' : m}]"
151
132
  end
152
133
 
153
134
  private
154
135
 
155
- # Makes the place notice an upstream transition;
156
- # to be called from the connecting transitions.
157
- def register_upstream_transition( transition )
158
- @upstream_arcs << transition
159
- end
160
-
161
- # Makes the place notice a downstream transition;
162
- # to be called from the connecting transitions.
163
- def register_downstream_transition( transition )
164
- @downstream_arcs << transition
165
- end
166
-
167
136
  def instance_description_strings
168
137
  m, n, d, q = marking, name, default_marking, quantum
169
138
  nς = "name: #{n.nil? ? '∅' : n}"
170
139
  mς = "marking: #{m.nil? ? 'nil' : m}"
171
140
  dς = "default_marking: #{d.nil? ? '∅' : d}"
172
- qς = "quantum: #{q.nil? ? '∅' : q}"
141
+ qς = q == 1 ? nil : "quantum: #{q.nil? ? '∅' : q}"
173
142
  return nς, mς, dς, qς
174
143
  end
175
-
176
- # Place, Transition, Net class
177
- #
178
- def Place; ::YPetri::Place end
179
- def Transition; ::YPetri::Transition end
180
- def Net; ::YPetri::Net end
181
-
182
- # Instance identification methods.
183
- #
184
- def place( which ); Place().instance( which ) end
185
- def transition( which ); Transition().instance( which ) end
186
- def net( which ); Net().instance( which ) end
187
144
  end # class YPetri::Place