lib/y_petri/net/state.rb in y_petri-2.1.3 vs lib/y_petri/net/state.rb in y_petri-2.1.6
- old
+ new
@@ -1,121 +1,129 @@
# encoding: utf-8
-class YPetri::Net
- # Petri net state (marking of all its places).
- #
- class State < Array
- require_relative 'state/feature'
- require_relative 'state/features'
+# An array whose elements correspond to the full marking of the net's places.
+#
+class YPetri::Net::State < Array
+ require_relative 'state/feature'
+ require_relative 'state/features'
- class << self
- # Customization of the parametrize method for the State class: Its
- # dependents Feature and Features (ie. feature set) are also parametrized.
- #
- def parametrize net: (fail ArgumentError, "No owning net!")
- Class.new( self ).tap do |subclass|
- subclass.define_singleton_method :net do net end
- subclass.param_class( { Feature: Feature,
- Features: Features },
- with: { State: subclass } )
- end
+ class << self
+ # Customization of the parametrize method for the State class: Its
+ # dependents Feature and Features (ie. feature set) are also parametrized.
+ #
+ def parametrize net: ( fail ArgumentError, "No owning net!" )
+ Class.new( self ).tap do |ç|
+ ç.define_singleton_method :net do net end
+ ç.param_class( { Feature: Feature,
+ Features: Features },
+ with: { State: ç } )
end
+ end
- delegate :Marking,
- :Firing,
- :Gradient,
- :Flux,
- :Delta,
- to: "Feature()"
+ delegate :Marking,
+ :Firing,
+ :Gradient,
+ :Flux,
+ :Delta,
+ to: "Feature()"
- alias __new__ new
-
- # Revives a state from a record and a given set of marking clamps.
- #
- def new record, marking_clamps: {}
- cc = marking_clamps.with_keys { |k| net.place k }.with_values! do |v|
- case v
- when YPetri::Place then v.marking
- when ~:call then v.call
- else v end
+ # Returns the feature identified by the argument.
+ #
+ def feature *id
+ fail ArgumentError, "No feature identifier!" if id.empty?
+ case id.first
+ when Feature() then id.first
+ when Feature then id.first.class.new( id.first )
+ else
+ msg = "Malformed feature identifier!"
+ fail ArgumentError, msg unless id.size == 1 and id.first.is_a? Hash
+ ꜧ = id.first
+ fail ArgumentError, msg unless ꜧ.size == 1
+ key, val = ꜧ.keys.first, ꜧ.values.first
+ recognized = :marking, :firing, :gradient, :flux, :delta
+ msg = "Unrecognized feature: #{key}"
+ fail ArgumentError, msg unless recognized.include? key
+ # And now, with everything clean...
+ case key
+ when :marking then Marking( val )
+ when :firing then Firing( val )
+ when :flux then Flux( val )
+ when :gradient then Gradient( *val )
+ when :delta then Delta( *val )
end
-
- record = features( marking: net.pp - cc.keys ).load( record )
-
- __new__ net.pp.map do |p|
- begin; cc.fetch p; rescue IndexError
- record.fetch Marking().of( p )
- end
- end
end
+ end
- # Returns the feature identified by the argument.
- #
- def feature id
- case id
- when Feature() then id
- when Feature then id.class.new( id )
- else
- features( id ).tap do |ff|
- ff.size == 1 or fail ArgumentError, "Argument #{id} must identify " +
- "exactly 1 feature!"
- end.first
- end
+ # If the argument is an array of features, or another Features instance,
+ # a feature set based on this array is returned. But the real purpose of
+ # this method is to allow hash-type argument, with keys +:marking+,
+ # +:firing+, +:gradient+, +:flux+ and +:delta+, specifying the respective
+ # features. For +:marking+, an array of places (or Marking features) is
+ # expected. For +:firing+ and +:flux+, an array of transitions (or Firing
+ # / Flux features) is expected. For +:gradient+ and +:delta+, a hash value
+ # is expected, containing keys +:places+ and +:transitions+, specifying
+ # for which place set / transition set should gradient / delta features
+ # be constructed. More in detail, values supplied under keys +:marking+,
+ # +:firing+, +:gradient+, +:flux+ and +:delta+ are delegated to
+ # +Features.marking+, +Features.firing+, +Features.gradient+ and
+ # +Features.flux+ methods, and their results are joined into a single
+ # feature set.
+ #
+ def features arg
+ case arg
+ when Features(), Array then Features().new( arg )
+ else # the real job of the method
+ marking = arg[:marking] || []
+ firing = arg[:firing] || [] # array of tS transitions
+ gradient = arg[:gradient] || [ [], transitions: [] ]
+ flux = arg[:flux] || [] # array of TS transitions
+ delta = arg[:delta] || [ [], transitions: [] ]
+ [ Features().marking( marking ),
+ Features().firing( firing ),
+ Features().gradient( *gradient ),
+ Features().flux( flux ),
+ Features().delta( *delta ) ].reduce :+
end
+ end
- # If the argument is an array of features, or another Features instance,
- # a feature set based on this array is returned. But the real purpose of
- # this method is to allow hash-type argument, with keys +:marking+,
- # +:firing+, +:gradient+, +:flux+ and +:delta+, specifying the respective
- # features. For +:marking+, an array of places (or Marking features) is
- # expected. For +:firing+ and +:flux+, an array of transitions (or Firing
- # / Flux features) is expected. For +:gradient+ and +:delta+, a hash value
- # is expected, containing keys +:places+ and +:transitions+, specifying
- # for which place set / transition set should gradient / delta features
- # be constructed. More in detail, values supplied under keys +:marking+,
- # +:firing+, +:gradient+, +:flux+ and +:delta+ are delegated to
- # +Features.marking+, +Features.firing+, +Features.gradient+ and
- # +Features.flux+ methods, and their results are joined into a single
- # feature set.
- #
- def features arg
- case arg
- when Features(), Array then Features().new( arg )
- else # the real job of the method
- marking = arg[:marking] || []
- firing = arg[:firing] || [] # array of tS transitions
- gradient = arg[:gradient] || [ [], transitions: [] ]
- flux = arg[:flux] || [] # array of TS transitions
- delta = arg[:delta] || [ [], transitions: [] ]
- [ Features().marking( marking ),
- Features().firing( firing ),
- Features().gradient( *gradient ),
- Features().flux( flux ),
- Features().delta( *delta ) ].reduce :+
- end
- end
+ delegate :marking, :firing, :gradient, :flux, :delta, to: "Features()"
+ end
- delegate :marking, :firing, :gradient, :flux, :delta, to: "Features()"
- end
+ # For non-parametrized vesion of the class, the class instance variables
+ # hold the non-parametrized dependent classes.
+ #
+ @Feature, @Features = Feature, Features
- # For non-parametrized vesion of the class, the class instance variables
- # hold the non-parametrized dependent classes.
- #
- @Feature, @Features = Feature, Features
+ delegate :net,
+ :Feature,
+ :Features,
+ :features,
+ :marking, :firing, :gradient, :flux, :delta,
+ to: "self.class"
- delegate :net,
- :Feature,
- :Features,
- :features,
- :marking, :firing, :gradient, :flux, :delta,
- to: "self.class"
+ # Given a set of clamped places, this method outputs a Record instance
+ # containing the marking of the free places (complementary to the supplied
+ # set of clamped places). I no set of clamped places is supplied, it is
+ # considered empty.
+ #
+ def to_record clamped_places=[]
+ free_places = case clamped_places
+ when Hash then to_record( clamped_places.keys )
+ else
+ free_places = places - places( clamped_places )
+ end
+ features( marking: free_places ).Record.load markings( free_places )
+ end
- # Reconstructs a simulation from the current state instance, given marking
- # clamps and other simulation settings.
- #
- def reconstruct marking_clamps: {}, **settings
- net.simulation marking: to_hash,
- marking_clamps: marking_clamps,
- **settings
- end
- end # class State
-end # YPetri::Net
+ # Marking of a single given place in this state.
+ #
+ def marking place_id
+ self[ places.index place( place_id ) ]
+ end
+
+ # Returns an array of markings of particular places in this state..
+ #
+ def markings place_ids=nil
+ return markings( places ) if place_ids.nil?
+ place_ids.map &:marking
+ end
+end # YPetri::Net::State