# -*- coding: utf-8 -*- module YPetri::Workspace::InstanceMethods # Readers for @Place, @Transition, @Net instance variables, which should # contain said classes, or their instance-specific subclasses. # Place class or parametrized subclass. # attr_reader :Place # Transition class or parametrized subclass. # attr_reader :Transition # Net class or parametrized subclass. # attr_reader :Net # Collections of clamps, initial marking vectors, and simulation settings. # attr_reader :clamp_collections, :initial_marking_collections, :simulation_settings_collections # Instance initialization. # def initialize set_up_Top_net # Sets up :Top net encompassing all places and transitions. @simulations = {} # { simulation => its settings } @clamp_collections = { Base: {} } # { collection name => clamp hash } @initial_marking_collections = { Base: {} } # { collection name => im hash } @simulation_settings_collections = # { collection name => ss hash } { Base: YPetri::DEFAULT_SIMULATION_SETTINGS.call } end # Returns a place instance identified by the argument. # def place which; Place().instance which end # Returns a transition instance identified by the argument. # def transition which; Transition().instance which end # Returns a net instance identified by the argument. # def net which; Net().instance which end # Returns the name of a place identified by the argument. # def p which; place( which ).name end # Returns the name of a transition identified by the argument. # def t which; transition( which ).name end # Returns the name of a net identified by the argument. # def n which; net( which ).name end # Place instances. # def places; Place().instances end # Transition instances. # def transitions; Transition().instances end # Net instances. # def nets; Net().instances end # Hash of simulation instances and their settings. # def simulations; @simulations end # Place names. # def pp; places.map &:name end # Transition names. # def tt; transitions.map &:name end # Net names. # def nn; nets.map &:name end # Clamp collection names. # def clamp_collection_names; @clamp_collections.keys end alias cc_names clamp_collection_names # Initial marking collection names. # def initial_marking_collection_names; @initial_marking_collections.keys end alias imc_names initial_marking_collection_names # Simulation settings collection names. # def simulation_settings_collection_names @simulation_settings_collections.keys end alias ssc_names simulation_settings_collection_names # Clamp collection identified by the argument. # def clamp_collection name=:Base @clamp_collections[name] end alias cc clamp_collection # Marking collection identified by the argument. # def initial_marking_collection name=:Base @initial_marking_collections[name] end alias imc initial_marking_collection # Simulation settings collection specified by the argument. # def simulation_settings_collection name=:Base @simulation_settings_collections[name] end alias ssc simulation_settings_collection # Creates a new clamp collection. If collection identifier is not given, # resets :Base clamp collection to new values. # def set_clamp_collection( name=:Base, clamp_hash ) @clamp_collections[name] = clamp_hash end alias set_cc set_clamp_collection # Creates a new initial marking collection. If collection identifier is not # given, resets :Base initial marking collection to new values. # def set_initial_marking_collection( name=:Base, initial_marking_hash ) @initial_marking_collections[name] = initial_marking_hash end alias set_imc set_initial_marking_collection # Creates a new simulation settings collection. If collection identifier is # not given, resets :Base simulation settings collection to new values. # def set_simulation_settings_collection( name=:Base, sim_set_hash ) @simulation_settings_collections[name] = sim_set_hash end alias set_ssc set_simulation_settings_collection # Presents a simulation specified by the argument, which must be a hash with # four items: :net, :clamp_collection, :inital_marking_collection and # :simulation_settings_collection. # def simulation settings={} key = case settings when ~:may_have then # it is a hash or equivalent settings.may_have :net settings.may_have :cc, syn!: :clamp_collection settings.may_have :imc, syn!: :initial_marking_collection settings.may_have :ssc, syn!: :simulation_settings_collection { net: net( settings[:net] || self.Net::Top ), # the key cc: settings[:cc] || :Base, imc: settings[:imc] || :Base, ssc: settings[:ssc] || :Base } else # use the unprocessed argument itself as the key settings end @simulations[ key ] end # Makes a new timed simulation. Named arguments for this method are the same # as for TimedSimulation#new, but in addition, :name can be supplied. # # To create a simulation, simulation settings collection, initial marking # collection, and clamp collection have to be specified. A place clamp, # is a fixed value, at which the marking is held. Similarly, initial # marking is the marking, which a free place receives at the beginning. # Free places are those, that are not clamped. After initialization, marking # of free places is allowed to change as the transition fire. # # For example, having places :P1..:P5, clamped :P1, :P2 can be written as eg.: # # * clamps = { P1: 4, P2: 5 } # # Places :P3, :P4, :P5 are free. Their initial marking has to be # specified, which can be written as eg.: # # * initial_markings = { P3: 1, P4: 2, P5: 3 } # # As for simulation settings, their exact nature depends on the simulation # method. For default Euler method, there are 3 important parameters: # - step_size, # - sampling_period, # - target_time # # For example, default simulation settings are: # # * default_ss = { step_size: 0.1, sampling_period: 5, target_time: 60 } # def new_timed_simulation( settings={} ); st = settings net_ɪ = net( st[:net] || self.Net::Top ) cc_id = st.may_have( :cc, syn!: :clamp_collection ) || :Base imc_id = st.may_have( :imc, syn!: :initial_marking_collection ) || :Base ssc_id = st.may_have( :ssc, syn!: :simulation_settings_collection ) || :Base # simulation key key = settings.may_have( :ɴ, syn!: :name ) || # either explicit { net: net_ɪ, cc: cc_id, imc: imc_id, ssc: ssc_id } # or constructed # Let's clarify what we got so far. simulation_settings = self.ssc( ssc_id ) clamp_hash = self.cc( cc_id ) im_hash = self.imc( imc_id ) # Use places' :default_marking in absence of explicit initial marking. untreated = net_ɪ.places.select do |p| ! clamp_hash.map { |k, _| place k }.include? p and ! im_hash.map { |k, _| place k }.include? p end im_complement = Hash[ untreated.zip( untreated.map &:default_marking ) ] # If marking can't be figured, raise nice errors. missing = im_complement.select { |_, v| v.nil? } err = lambda { |array, txt=''| raise TypeError, "Missing clamp and/or initial marking for %s#{txt}!" % Array( array ).map { |i| missing.keys[i] }.join( ', ' ) } case missing.size when 0 then im_hash = im_hash.merge im_complement # everything's OK when 1 then err.( 0 ) when 2 then err.( [0, 1] ) when 3 then err.( [0, 1, 2] ) else err.( [0, 1], " and #{missing.size-2} more places" ) end # Finally, create and return the simulation @simulations[ key ] = net_ɪ.new_timed_simulation( simulation_settings .merge( initial_marking: im_hash, place_clamps: clamp_hash ) ) end # def new_timed_simulation private # Creates all-encompassing Net instance named :Top. # def set_up_Top_net Net().new name: :Top # all-encompassing :Top net # Hook new places to add themselves magically to the :Top net. Place().new_instance_closure { |new_inst| net( :Top ) << new_inst } # Hook new transitions to add themselves magically to the :Top net. Transition().new_instance_closure { |new_inst| net( :Top ) << new_inst } end end # module YPetri::Workspace::InstanceMethods