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.
- 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'
|