y_petri 2.0.15 → 2.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/y_petri/{manipulator → agent}/hash_key_pointer.rb +2 -2
- data/lib/y_petri/agent/petri_net_related.rb +115 -0
- data/lib/y_petri/{manipulator → agent}/selection.rb +2 -1
- data/lib/y_petri/{manipulator/simulation_related_methods.rb → agent/simulation_related.rb} +93 -110
- data/lib/y_petri/agent.rb +22 -0
- data/lib/y_petri/core/timed/euler.rb +20 -0
- data/lib/y_petri/core/timed/pseudo_euler.rb +31 -0
- data/lib/y_petri/core/timed/quasi_euler.rb +23 -0
- data/lib/y_petri/core/timed.rb +70 -0
- data/lib/y_petri/core/timeless/pseudo_euler.rb +20 -0
- data/lib/y_petri/core/timeless.rb +12 -0
- data/lib/y_petri/core.rb +100 -0
- data/lib/y_petri/dsl.rb +66 -0
- data/lib/y_petri/fixed_assets.rb +7 -0
- data/lib/y_petri/net/element_access.rb +239 -0
- data/lib/y_petri/net/state/feature/delta.rb +88 -0
- data/lib/y_petri/net/state/feature/firing.rb +57 -0
- data/lib/y_petri/net/state/feature/flux.rb +58 -0
- data/lib/y_petri/net/state/feature/gradient.rb +75 -0
- data/lib/y_petri/net/state/feature/marking.rb +62 -0
- data/lib/y_petri/net/state/feature.rb +79 -0
- data/lib/y_petri/net/state/features/dataset.rb +135 -0
- data/lib/y_petri/net/state/features/record.rb +50 -0
- data/lib/y_petri/net/state/features.rb +126 -0
- data/lib/y_petri/net/state.rb +121 -0
- data/lib/y_petri/net/timed.rb +8 -0
- data/lib/y_petri/net/visualization.rb +3 -3
- data/lib/y_petri/net.rb +73 -77
- data/lib/y_petri/place.rb +8 -3
- data/lib/y_petri/simulation/dependency.rb +107 -0
- data/lib/y_petri/simulation/element_representation.rb +20 -0
- data/lib/y_petri/simulation/elements/access.rb +57 -0
- data/lib/y_petri/simulation/elements.rb +45 -0
- data/lib/y_petri/simulation/feature_set.rb +21 -0
- data/lib/y_petri/simulation/initial_marking/access.rb +55 -0
- data/lib/y_petri/simulation/initial_marking.rb +15 -0
- data/lib/y_petri/simulation/marking_clamps/access.rb +34 -0
- data/lib/y_petri/simulation/marking_clamps.rb +18 -0
- data/lib/y_petri/simulation/marking_vector/access.rb +106 -0
- data/lib/y_petri/simulation/marking_vector.rb +156 -0
- data/lib/y_petri/simulation/matrix.rb +64 -0
- data/lib/y_petri/simulation/place_mapping.rb +62 -0
- data/lib/y_petri/simulation/place_representation.rb +74 -0
- data/lib/y_petri/simulation/places/access.rb +121 -0
- data/lib/y_petri/simulation/places/clamped.rb +8 -0
- data/lib/y_petri/simulation/places/free.rb +8 -0
- data/lib/y_petri/simulation/places/types.rb +25 -0
- data/lib/y_petri/simulation/places.rb +41 -0
- data/lib/y_petri/simulation/recorder.rb +54 -0
- data/lib/y_petri/simulation/timed/recorder.rb +53 -0
- data/lib/y_petri/simulation/timed.rb +161 -261
- data/lib/y_petri/simulation/timeless/recorder.rb +25 -0
- data/lib/y_petri/simulation/timeless.rb +35 -0
- data/lib/y_petri/simulation/transition_representation/A.rb +58 -0
- data/lib/y_petri/simulation/transition_representation/S.rb +45 -0
- data/lib/y_petri/simulation/transition_representation/T.rb +80 -0
- data/lib/y_petri/simulation/transition_representation/TS.rb +46 -0
- data/lib/y_petri/simulation/transition_representation/Ts.rb +32 -0
- data/lib/y_petri/simulation/transition_representation/a.rb +30 -0
- data/lib/y_petri/simulation/transition_representation/s.rb +29 -0
- data/lib/y_petri/simulation/transition_representation/t.rb +37 -0
- data/lib/y_petri/simulation/transition_representation/tS.rb +38 -0
- data/lib/y_petri/simulation/transition_representation/ts.rb +32 -0
- data/lib/y_petri/simulation/transition_representation/types.rb +62 -0
- data/lib/y_petri/simulation/transition_representation.rb +79 -0
- data/lib/y_petri/simulation/transitions/A.rb +40 -0
- data/lib/y_petri/simulation/transitions/S.rb +24 -0
- data/lib/y_petri/simulation/transitions/T.rb +34 -0
- data/lib/y_petri/simulation/transitions/TS.rb +57 -0
- data/lib/y_petri/simulation/transitions/Ts.rb +60 -0
- data/lib/y_petri/simulation/transitions/a.rb +8 -0
- data/lib/y_petri/simulation/transitions/access.rb +186 -0
- data/lib/y_petri/simulation/transitions/s.rb +9 -0
- data/lib/y_petri/simulation/transitions/t.rb +22 -0
- data/lib/y_petri/simulation/transitions/tS.rb +55 -0
- data/lib/y_petri/simulation/transitions/ts.rb +58 -0
- data/lib/y_petri/simulation/transitions/types.rb +98 -0
- data/lib/y_petri/simulation/transitions.rb +21 -0
- data/lib/y_petri/simulation.rb +176 -781
- data/lib/y_petri/transition/assignment.rb +7 -5
- data/lib/y_petri/transition/construction.rb +119 -187
- data/lib/y_petri/transition/init.rb +311 -0
- data/lib/y_petri/transition/ordinary_timeless.rb +8 -6
- data/lib/y_petri/transition/timed.rb +11 -18
- data/lib/y_petri/transition.rb +104 -132
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/world/dependency.rb +40 -0
- data/lib/y_petri/world/petri_net_related.rb +61 -0
- data/lib/y_petri/{workspace/simulation_related_methods.rb → world/simulation_related.rb} +42 -49
- data/lib/y_petri/world.rb +27 -0
- data/lib/y_petri.rb +47 -99
- data/test/{manipulator_test.rb → agent_test.rb} +19 -17
- data/{lib/y_petri → test/examples}/demonstrator.rb +0 -0
- data/{lib/y_petri → test/examples}/demonstrator_2.rb +1 -0
- data/{lib/y_petri → test/examples}/demonstrator_3.rb +0 -0
- data/{lib/y_petri → test/examples}/demonstrator_4.rb +0 -0
- data/test/examples/example_2.rb +16 -0
- data/test/{manual_examples.rb → examples/manual_examples.rb} +0 -0
- data/test/net_test.rb +126 -121
- data/test/place_test.rb +1 -1
- data/test/sim_test +565 -0
- data/test/simulation_test.rb +338 -264
- data/test/transition_test.rb +77 -174
- data/test/world_mock.rb +12 -0
- data/test/{workspace_test.rb → world_test.rb} +19 -20
- data/test/y_petri_test.rb +4 -5
- metadata +101 -26
- data/lib/y_petri/dependency_injection.rb +0 -45
- data/lib/y_petri/manipulator/petri_net_related_methods.rb +0 -74
- data/lib/y_petri/manipulator.rb +0 -20
- data/lib/y_petri/net/selections.rb +0 -209
- data/lib/y_petri/simulation/collections.rb +0 -460
- data/lib/y_petri/workspace/parametrized_subclassing.rb +0 -22
- data/lib/y_petri/workspace/petri_net_related_methods.rb +0 -88
- data/lib/y_petri/workspace.rb +0 -16
- data/test/timed_simulation_test.rb +0 -153
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class YPetri::Net::State
|
4
|
+
class Feature
|
5
|
+
# Marking of a Petri net place.
|
6
|
+
#
|
7
|
+
class Marking < Feature
|
8
|
+
attr_reader :place
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def parametrize *args
|
12
|
+
Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
|
13
|
+
ç.instance_variable_set( :@instances,
|
14
|
+
Hash.new do |hsh, id|
|
15
|
+
case id
|
16
|
+
when Marking then
|
17
|
+
hsh[ id.place ]
|
18
|
+
when ç.net.Place then
|
19
|
+
hsh[ id ] = ç.__new__( id )
|
20
|
+
else
|
21
|
+
hsh[ ç.net.place( id ) ]
|
22
|
+
end
|
23
|
+
end )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :instances
|
28
|
+
|
29
|
+
alias __new__ new
|
30
|
+
|
31
|
+
def new id
|
32
|
+
instances[ id ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def of id
|
36
|
+
new id
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize place
|
41
|
+
@place = net.place( place )
|
42
|
+
end
|
43
|
+
|
44
|
+
def extract_from arg, **nn
|
45
|
+
case arg
|
46
|
+
when YPetri::Simulation then
|
47
|
+
arg.m( [ place ] ).first
|
48
|
+
else
|
49
|
+
fail TypeError, "Argument type not supported!"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
place.name
|
55
|
+
end
|
56
|
+
|
57
|
+
def label
|
58
|
+
":#{place.name}"
|
59
|
+
end
|
60
|
+
end # class Marking
|
61
|
+
end # class Feature
|
62
|
+
end # YPetri::Net::State
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class YPetri::Net::State
|
4
|
+
# A feature of a Petri net.
|
5
|
+
#
|
6
|
+
class Feature
|
7
|
+
require_relative 'feature/marking'
|
8
|
+
require_relative 'feature/firing'
|
9
|
+
require_relative 'feature/gradient'
|
10
|
+
require_relative 'feature/flux'
|
11
|
+
require_relative 'feature/delta'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def parametrize parameters
|
15
|
+
Class.new( self ).tap do |ç|
|
16
|
+
parameters.each_pair { |symbol, value|
|
17
|
+
ç.define_singleton_method symbol do value end
|
18
|
+
}
|
19
|
+
sç = ç.State
|
20
|
+
ç.instance_variable_set :@Marking, Marking.parametrize( State: sç )
|
21
|
+
ç.instance_variable_set :@Firing, Firing.parametrize( State: sç )
|
22
|
+
ç.instance_variable_set :@Gradient, Gradient.parametrize( State: sç )
|
23
|
+
ç.instance_variable_set :@Flux, Flux.parametrize( State: sç )
|
24
|
+
ç.instance_variable_set :@Delta, Delta.parametrize( State: sç )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
delegate :net,
|
29
|
+
to: "State()"
|
30
|
+
|
31
|
+
def Marking id=L!
|
32
|
+
return @Marking if id.local_object?
|
33
|
+
case id
|
34
|
+
when Marking() then id
|
35
|
+
when Marking then Marking().of( id.place )
|
36
|
+
else Marking().of( id ) end # assume it's a place
|
37
|
+
end
|
38
|
+
|
39
|
+
def Firing id=L!
|
40
|
+
return @Firing if id.local_object?
|
41
|
+
case id
|
42
|
+
when Firing() then id
|
43
|
+
when Firing then Firing().of( id.transition )
|
44
|
+
else Firing().of( id ) end # assume it's a place
|
45
|
+
end
|
46
|
+
|
47
|
+
def Gradient id=L!, transitions: net.T_tt
|
48
|
+
return @Gradient if id.local_object?
|
49
|
+
case id
|
50
|
+
when Gradient() then id
|
51
|
+
when Gradient then
|
52
|
+
Gradient().of( id.place, transitions: id.transitions )
|
53
|
+
else Gradient().of( id, transitions: transitions ) end # assume it's a place
|
54
|
+
end
|
55
|
+
|
56
|
+
def Flux id=L!
|
57
|
+
return @Flux if id.local_object?
|
58
|
+
case id
|
59
|
+
when Flux() then id
|
60
|
+
when Flux then Flux().of( id.transition )
|
61
|
+
else Flux().of( id ) end # assume it's a place
|
62
|
+
end
|
63
|
+
|
64
|
+
def Delta id=L!, transitions: net.tt
|
65
|
+
return @Delta if id.local_object?
|
66
|
+
case id
|
67
|
+
when Delta() then id
|
68
|
+
when Delta then
|
69
|
+
Delta().of( id.place, transitions: id.transitions )
|
70
|
+
else Delta().of( id, transitions: transitions ) end # assume it's a place
|
71
|
+
end
|
72
|
+
end # class << self
|
73
|
+
|
74
|
+
delegate :net,
|
75
|
+
:State,
|
76
|
+
:Marking, :Firing, :Gradient, :Flux, :Delta,
|
77
|
+
to: "self.class"
|
78
|
+
end # class Feature
|
79
|
+
end # YPetri::Net::State
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class YPetri::Net::State
|
4
|
+
class Features
|
5
|
+
# Dataset is a collection of labeled state records.
|
6
|
+
#
|
7
|
+
class Dataset < Hash
|
8
|
+
class << self
|
9
|
+
alias __new__ new
|
10
|
+
|
11
|
+
def new
|
12
|
+
__new__ do |hsh, missing|
|
13
|
+
case missing
|
14
|
+
when Float then nil
|
15
|
+
else hsh[ missing.to_f ] end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias events keys
|
21
|
+
alias records values
|
22
|
+
|
23
|
+
delegate :features, to: "self.class"
|
24
|
+
delegate :net, to: :features
|
25
|
+
delegate :State, to: :net
|
26
|
+
|
27
|
+
# Revives records from values.
|
28
|
+
#
|
29
|
+
def records
|
30
|
+
values.map { |value| features.Record.new( value ) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Recreates the simulation at a given event label.
|
34
|
+
#
|
35
|
+
def reconstruct event: event, **settings # settings include marking clampls
|
36
|
+
interpolate( event ).reconstruct **settings
|
37
|
+
end
|
38
|
+
|
39
|
+
# Interpolates the recording an the given point (event).
|
40
|
+
#
|
41
|
+
def interpolate( event )
|
42
|
+
# TODO: This whole interpolation thing is unfinished.
|
43
|
+
begin
|
44
|
+
record( event )
|
45
|
+
rescue KeyError => msg
|
46
|
+
timed? or raise TypeError, "Event #{event} does not have a record!"
|
47
|
+
f_time, floor = floor( event ) # timed datasets support floor, ceiling
|
48
|
+
c_time, ceiling = ceiling( time )
|
49
|
+
floor + ( ceiling - floor ) / ( c_time - f_time ) * ( time - f_time )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the data series for the specified features.
|
54
|
+
#
|
55
|
+
def series arg=nil
|
56
|
+
return records.transpose if arg.nil?
|
57
|
+
reduce_features( State().features( arg ) ).series
|
58
|
+
end
|
59
|
+
|
60
|
+
# Expects a hash of features (:marking (alias :state) of places, :firing
|
61
|
+
# of tS transitions, :delta of places and/or transitions) and returns the
|
62
|
+
# corresponding mapping of the recording.
|
63
|
+
#
|
64
|
+
def reduce_features features
|
65
|
+
rf = net.State.features( features )
|
66
|
+
rr_class = rf.Record
|
67
|
+
rf.new_dataset.tap do |ds|
|
68
|
+
( events >> records ).each_pair { |event, record|
|
69
|
+
ds.update event => rr_class.load( rf.map { |f| record.fetch f } )
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def marking *args
|
75
|
+
return reduce_features features.select { |f| f.is_a? YPetri::Net::State::Feature::Marking } if args.empty?
|
76
|
+
reduce_features marking: args.first
|
77
|
+
end
|
78
|
+
|
79
|
+
def firing *args
|
80
|
+
return reduce_features features.select { |f| f.is_a? YPetri::Net::State::Feature::Firing } if args.empty?
|
81
|
+
reduce_features firing: args.first
|
82
|
+
end
|
83
|
+
|
84
|
+
def flux *args
|
85
|
+
return reduce_features features.select { |f| f.is_a? YPetri::Net::State::Feature::Flux } if args.empty?
|
86
|
+
reduce_features flux: args.first
|
87
|
+
end
|
88
|
+
|
89
|
+
def gradient *args
|
90
|
+
return reduce_features features.select { |f| f.is_a? YPetri::Net::State::Feature::Gradient } if args.empty?
|
91
|
+
reduce_features gradient: args
|
92
|
+
end
|
93
|
+
|
94
|
+
def delta *args
|
95
|
+
return reduce_features features.select { |f| f.is_a? YPetri::Net::State::Feature::Delta } if args.empty?
|
96
|
+
reduce_features delta: args
|
97
|
+
end
|
98
|
+
|
99
|
+
# Outputs the current recording in CSV format.
|
100
|
+
#
|
101
|
+
def to_csv
|
102
|
+
map { |lbl, rec| [ lbl, *rec ].join ',' }.join "\n"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Plots the dataset.
|
106
|
+
#
|
107
|
+
def plot time: nil, **nn
|
108
|
+
events = events()
|
109
|
+
data_ss = series
|
110
|
+
x_range = if time.is_a? Range then
|
111
|
+
"[#{time.begin}:#{time.end}]"
|
112
|
+
else
|
113
|
+
"[-0:#{SY::Time.magnitude( time ).amount rescue time}]"
|
114
|
+
end
|
115
|
+
|
116
|
+
Gnuplot.open do |gp|
|
117
|
+
Gnuplot::Plot.new gp do |plot|
|
118
|
+
plot.xrange x_range
|
119
|
+
plot.title nn[:title] || "#{net} plot"
|
120
|
+
plot.ylabel nn[:ylabel] || "Values"
|
121
|
+
plot.xlabel nn[:xlabel] || "Time [s]"
|
122
|
+
|
123
|
+
features.labels.zip( data_ss )
|
124
|
+
.each { |label, data_array|
|
125
|
+
plot.data << Gnuplot::DataSet.new( [ events, data_array ] ) { |ds|
|
126
|
+
ds.with = "linespoints"
|
127
|
+
ds.title = label
|
128
|
+
}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end # class Dataset
|
134
|
+
end # class Features
|
135
|
+
end # YPetri::Simulation::State
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class YPetri::Net::State
|
2
|
+
class Features
|
3
|
+
# A collection of values for a given set of state features.
|
4
|
+
#
|
5
|
+
class Record < Array
|
6
|
+
class << self
|
7
|
+
delegate :State,
|
8
|
+
:net,
|
9
|
+
to: "Features()"
|
10
|
+
|
11
|
+
# Construcs a new Record object from a given collection of values.
|
12
|
+
#
|
13
|
+
def load values
|
14
|
+
new( values.dup )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
delegate :Features,
|
19
|
+
:State,
|
20
|
+
:net,
|
21
|
+
:features,
|
22
|
+
to: "self.class"
|
23
|
+
|
24
|
+
# Outputs the record as a plain array.
|
25
|
+
#
|
26
|
+
def dump precision: nil
|
27
|
+
features.map { |f| fetch( f ).round( precision ) }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns an identified feature, or fails.
|
31
|
+
#
|
32
|
+
def fetch feature
|
33
|
+
super begin
|
34
|
+
Integer( feature )
|
35
|
+
rescue TypeError
|
36
|
+
features.index State().feature( feature )
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the state instance implied by the receiver record, and a set of
|
41
|
+
# complementary marking clamps supplied as the argument.
|
42
|
+
#
|
43
|
+
def state marking_clamps: {}
|
44
|
+
State.new self, marking_clamps: marking_clamps
|
45
|
+
end
|
46
|
+
|
47
|
+
delegate :reconstruct, to: :state
|
48
|
+
end # class Record
|
49
|
+
end # class Features
|
50
|
+
end # YPetri::Net::State
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class YPetri::Net::State
|
4
|
+
# A set of state features.
|
5
|
+
#
|
6
|
+
class Features < Array
|
7
|
+
require_relative 'features/record'
|
8
|
+
require_relative 'features/dataset'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Customization of the parametrize method for the Features class: Its
|
12
|
+
# dependents Record and Dataset are also parametrized.
|
13
|
+
#
|
14
|
+
def parametrize parameters
|
15
|
+
Class.new( self ).tap do |subclass|
|
16
|
+
parameters.each_pair { |symbol, value|
|
17
|
+
subclass.define_singleton_method symbol do value end
|
18
|
+
}
|
19
|
+
subclass.param_class( { Record: Record, Dataset: Dataset },
|
20
|
+
with: { Features: subclass } )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
delegate :net,
|
25
|
+
:Feature,
|
26
|
+
:feature,
|
27
|
+
to: "State()"
|
28
|
+
|
29
|
+
delegate :Marking,
|
30
|
+
:Firing,
|
31
|
+
:Gradient,
|
32
|
+
:Flux,
|
33
|
+
:Delta,
|
34
|
+
to: "Feature()"
|
35
|
+
|
36
|
+
delegate :load, to: :Record
|
37
|
+
|
38
|
+
alias __new__ new
|
39
|
+
|
40
|
+
def new features
|
41
|
+
ff = features.map &method( :feature )
|
42
|
+
__new__( ff ).tap do |inst|
|
43
|
+
# Parametrize them <em>one more time</em> with Features instance.
|
44
|
+
# Banged version of #param_class! ensures that #Record, #Dataset
|
45
|
+
# methods are shadowed.
|
46
|
+
inst.param_class!( { Record: Record(), Dataset: Dataset() },
|
47
|
+
with: { features: inst } )
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def marking places=net.pp
|
52
|
+
new net.pp( places ).map { |p| Marking( p ) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def firing transitions=net.tS_tt
|
56
|
+
new net.tS_tt( transitions ).map { |t| Firing( t ) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def gradient places=net.pp, transitions: net.T_tt
|
60
|
+
tt = net.T_tt( transitions )
|
61
|
+
new net.pp( places ).map { |p|
|
62
|
+
Gradient( p, transitions: tt )
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def flux transitions=net.TS_tt
|
67
|
+
new net.TS_tt( transitions ).map { |t| Flux( t ) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def delta places=net.pp, transitions: net.tt
|
71
|
+
transitions = net.tt( transitions )
|
72
|
+
new net.pp( places ).map { |p|
|
73
|
+
Delta( p, transitions: transitions )
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
delegate :State,
|
79
|
+
:net,
|
80
|
+
:Feature,
|
81
|
+
:feature,
|
82
|
+
:Marking,
|
83
|
+
:Firing,
|
84
|
+
:Gradient,
|
85
|
+
:Flux,
|
86
|
+
:Delta,
|
87
|
+
:load,
|
88
|
+
to: "self.class"
|
89
|
+
|
90
|
+
# Extracts the features from a given target
|
91
|
+
#
|
92
|
+
def extract_from target, **nn
|
93
|
+
Record().new( map { |feature| feature.extract_from( target, **nn ) } )
|
94
|
+
end
|
95
|
+
|
96
|
+
# Constructs a new dataset from these features.
|
97
|
+
#
|
98
|
+
def new_dataset
|
99
|
+
Dataset().new
|
100
|
+
end
|
101
|
+
|
102
|
+
# Feature summation -- of feature class.
|
103
|
+
#
|
104
|
+
def + other
|
105
|
+
self.class.new( super )
|
106
|
+
end
|
107
|
+
|
108
|
+
# Feature summation -- of feature class.
|
109
|
+
#
|
110
|
+
def - other
|
111
|
+
self.class.new( super )
|
112
|
+
end
|
113
|
+
|
114
|
+
# Feature summation -- of feature class.
|
115
|
+
#
|
116
|
+
def * other
|
117
|
+
self.class.new( super )
|
118
|
+
end
|
119
|
+
|
120
|
+
# Feature labels.
|
121
|
+
#
|
122
|
+
def labels
|
123
|
+
map &:label
|
124
|
+
end
|
125
|
+
end # class Features
|
126
|
+
end # YPetri::Net::State
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class YPetri::Net
|
4
|
+
# Petri net state (marking of all its places).
|
5
|
+
#
|
6
|
+
class State < Array
|
7
|
+
require_relative 'state/feature'
|
8
|
+
require_relative 'state/features'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Customization of the parametrize method for the State class: Its
|
12
|
+
# dependents Feature and Features (ie. feature set) are also parametrized.
|
13
|
+
#
|
14
|
+
def parametrize net: (fail ArgumentError, "No owning net!")
|
15
|
+
Class.new( self ).tap do |subclass|
|
16
|
+
subclass.define_singleton_method :net do net end
|
17
|
+
subclass.param_class( { Feature: Feature,
|
18
|
+
Features: Features },
|
19
|
+
with: { State: subclass } )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
delegate :Marking,
|
24
|
+
:Firing,
|
25
|
+
:Gradient,
|
26
|
+
:Flux,
|
27
|
+
:Delta,
|
28
|
+
to: "Feature()"
|
29
|
+
|
30
|
+
alias __new__ new
|
31
|
+
|
32
|
+
# Revives a state from a record and a given set of marking clamps.
|
33
|
+
#
|
34
|
+
def new record, marking_clamps: {}
|
35
|
+
cc = marking_clamps.with_keys { |k| net.place k }.with_values! do |v|
|
36
|
+
case v
|
37
|
+
when YPetri::Place then v.marking
|
38
|
+
when ~:call then v.call
|
39
|
+
else v end
|
40
|
+
end
|
41
|
+
|
42
|
+
record = features( marking: net.pp - cc.keys ).load( record )
|
43
|
+
|
44
|
+
__new__ net.pp.map do |p|
|
45
|
+
begin; cc.fetch p; rescue IndexError
|
46
|
+
record.fetch Marking().of( p )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the feature identified by the argument.
|
52
|
+
#
|
53
|
+
def feature id
|
54
|
+
case id
|
55
|
+
when Feature() then id
|
56
|
+
when Feature then id.class.new( id )
|
57
|
+
else
|
58
|
+
features( id ).tap do |ff|
|
59
|
+
ff.size == 1 or fail ArgumentError, "Argument #{id} must identify " +
|
60
|
+
"exactly 1 feature!"
|
61
|
+
end.first
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# If the argument is an array of features, or another Features instance,
|
66
|
+
# a feature set based on this array is returned. But the real purpose of
|
67
|
+
# this method is to allow hash-type argument, with keys +:marking+,
|
68
|
+
# +:firing+, +:gradient+, +:flux+ and +:delta+, specifying the respective
|
69
|
+
# features. For +:marking+, an array of places (or Marking features) is
|
70
|
+
# expected. For +:firing+ and +:flux+, an array of transitions (or Firing
|
71
|
+
# / Flux features) is expected. For +:gradient+ and +:delta+, a hash value
|
72
|
+
# is expected, containing keys +:places+ and +:transitions+, specifying
|
73
|
+
# for which place set / transition set should gradient / delta features
|
74
|
+
# be constructed. More in detail, values supplied under keys +:marking+,
|
75
|
+
# +:firing+, +:gradient+, +:flux+ and +:delta+ are delegated to
|
76
|
+
# +Features.marking+, +Features.firing+, +Features.gradient+ and
|
77
|
+
# +Features.flux+ methods, and their results are joined into a single
|
78
|
+
# feature set.
|
79
|
+
#
|
80
|
+
def features arg
|
81
|
+
case arg
|
82
|
+
when Features(), Array then Features().new( arg )
|
83
|
+
else # the real job of the method
|
84
|
+
marking = arg[:marking] || []
|
85
|
+
firing = arg[:firing] || [] # array of tS transitions
|
86
|
+
gradient = arg[:gradient] || [ [], transitions: [] ]
|
87
|
+
flux = arg[:flux] || [] # array of TS transitions
|
88
|
+
delta = arg[:delta] || [ [], transitions: [] ]
|
89
|
+
[ Features().marking( marking ),
|
90
|
+
Features().firing( firing ),
|
91
|
+
Features().gradient( *gradient ),
|
92
|
+
Features().flux( flux ),
|
93
|
+
Features().delta( *delta ) ].reduce :+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
delegate :marking, :firing, :gradient, :flux, :delta, to: "Features()"
|
98
|
+
end
|
99
|
+
|
100
|
+
# For non-parametrized vesion of the class, the class instance variables
|
101
|
+
# hold the non-parametrized dependent classes.
|
102
|
+
#
|
103
|
+
@Feature, @Features = Feature, Features
|
104
|
+
|
105
|
+
delegate :net,
|
106
|
+
:Feature,
|
107
|
+
:Features,
|
108
|
+
:features,
|
109
|
+
:marking, :firing, :gradient, :flux, :delta,
|
110
|
+
to: "self.class"
|
111
|
+
|
112
|
+
# Reconstructs a simulation from the current state instance, given marking
|
113
|
+
# clamps and other simulation settings.
|
114
|
+
#
|
115
|
+
def reconstruct marking_clamps: {}, **settings
|
116
|
+
net.simulation marking: to_hash,
|
117
|
+
marking_clamps: marking_clamps,
|
118
|
+
**settings
|
119
|
+
end
|
120
|
+
end # class State
|
121
|
+
end # YPetri::Net
|
@@ -19,10 +19,10 @@ class YPetri::Net
|
|
19
19
|
ꜧ[tr] = γ.add_nodes tr.name.to_s,
|
20
20
|
shape: 'box',
|
21
21
|
fillcolor: if tr.assignment? then 'yellow'
|
22
|
-
elsif tr.
|
22
|
+
elsif tr.type == :TS then 'lightcyan'
|
23
23
|
else 'ghostwhite' end,
|
24
24
|
color: if tr.assignment? then 'goldenrod'
|
25
|
-
elsif tr.
|
25
|
+
elsif tr.type == :TS then 'cyan'
|
26
26
|
else 'grey' end,
|
27
27
|
style: 'filled'
|
28
28
|
end
|
@@ -35,7 +35,7 @@ class YPetri::Net
|
|
35
35
|
( tr.domain - tr.codomain ).each { |pl|
|
36
36
|
γ.add_edges tr_node, place_nodes[pl], color: 'grey', arrowhead: 'none'
|
37
37
|
}
|
38
|
-
elsif tr.
|
38
|
+
elsif tr.type == :TS then
|
39
39
|
tr.codomain.each { |pl|
|
40
40
|
if tr.stoichio[pl] > 0 then # producing arc
|
41
41
|
γ.add_edges tr_node, place_nodes[pl], color: 'cyan'
|