class PetriNet::Net
Attributes
List of arcs
Description
Storage filename
List of markings !depricated!
Human readable name
List of places
List of transitions
Public Class Methods
Create new Petri Net
definition.
options may be
-
name used as a human usable identifier (defaults to 'petri_net')
-
filename (defaults to the name)
-
description (defaults to 'Petri Net')
Accepts a block and yields itself
# File lib/petri_net/net.rb, line 37 def initialize(options = {}, &block) @name = (options[:name] || 'petri_net') @filename = (options[:filename] || @name) @description = (options[:description] || 'Petri Net') @places = {} @arcs = {} @transitions = {} @markings = {} @objects = [] @up_to_date = false @w_up_to_date = false yield self unless block.nil? end
Public Instance Methods
Adds an object to the Petri Net
. You can add
-
Array of these
The Objects are added by PetriNet::Net#add_place
, PetriNet::Net#add_arc
and PetriNet::Net#add_transition
, refer to these to get more information on how they are added raises an RuntimeError if a wring Type is given
returns itself
# File lib/petri_net/net.rb, line 63 def <<(object) return if object.nil? # TODO: WORKAROUND There should never be a nil here, even while merging. case object.class.to_s when 'Array' object.each { |o| self << o } when 'PetriNet::Place' add_place(object) when 'PetriNet::Arc' add_arc(object) when 'PetriNet::Transition' add_transition(object) else raise "(PetriNet) Unknown object #{object.class}." end self end
Add an arc to the list of arcs.
# File lib/petri_net/net.rb, line 100 def add_arc(arc) if (arc.validate self) && !@arcs.include?(arc.name) arc.update self if arc.need_update? self @arcs[arc.name] = arc.id @objects[arc.id] = arc arc.net = self return arc.id end changed_structure false end
Adds a place to the list of places. Adds the place only if the place is valid and unique in the objects-list of the net
This Method changes the structure of the PetriNet
, you will have to recalculate all cached functions
# File lib/petri_net/net.rb, line 86 def add_place(place) if place.validate && !@places.include?(place.name) @places[place.name] = place.id @objects[place.id] = place place.net = self return place.id end changed_structure false end
Add a transition to the list of transitions.
# File lib/petri_net/net.rb, line 115 def add_transition(transition) if transition.validate && !@transitions.include?(transition.name) @transitions[transition.name] = transition.id @objects[transition.id] = transition transition.net = self return transition.id end changed_structure false end
# File lib/petri_net/net.rb, line 365 def delta generate_delta if @delta.nil? @delta end
# File lib/petri_net/net.rb, line 361 def fire(transition) get_transition(transition).fire end
# File lib/petri_net/net.rb, line 250 def generate_coverability_graph startmarkings = get_markings @graph = PetriNet::CoverabilityGraph.new(self) @graph.add_node current_node = PetriNet::CoverabilityGraph::Node.new(@graph, markings: get_markings, start: true) coverability_helper startmarkings, current_node set_markings startmarkings @graph end
# File lib/petri_net/net.rb, line 187 def generate_gv g = GraphViz.new(:G, type: :digraph) @places.each_value do |place| gv_node = g.add_nodes(@objects[place].name) end @transitions.each_value do |transition| gv_node = g.add_nodes(@objects[transition].name) gv_node.shape = :box gv_node.fillcolor = :grey90 end @arcs.each_value do |arc| gv_edge = g.add_edges(@objects[arc].source.name, @objects[arc].destination.name) end g end
# File lib/petri_net/net.rb, line 261 def generate_reachability_graph startmarkings = get_markings @graph = PetriNet::ReachabilityGraph.new(self) @graph.add_node current_node = PetriNet::ReachabilityGraph::Node.new(@graph, markings: get_markings, start: true) reachability_helper startmarkings, current_node set_markings startmarkings @graph end
# File lib/petri_net/net.rb, line 272 def generate_weight_function @weight = {} @arcs.each_value do |id| arc = @objects[id] @weight[[arc.source.id, arc.destination.id]] = arc.weight end @w_up_to_date = true @weight end
returns the arc refered by the given name or false if there is no arc with this name
# File lib/petri_net/net.rb, line 142 def get_arc(name) arc = @objects[@arcs[name]] arc.nil? ? false : arc end
# File lib/petri_net/net.rb, line 306 def get_marking(places) places = [places] unless places.class.to_s == 'Array' places.map! { |p| get_place p } if places.first.class.to_s == 'Fixnum' res = [] get_place_list.map { |place| res << ((places.include? place.name) ? 1 : 0) } res end
# File lib/petri_net/net.rb, line 302 def get_markings @places.map { |_key, pid| @objects[pid].markings.size } end
# File lib/petri_net/net.rb, line 341 def get_object(id) @objects[id] end
# File lib/petri_net/net.rb, line 345 def get_objects @objects.clone end
Returns the place refered by the given name or false if there is no place with this name
# File lib/petri_net/net.rb, line 128 def get_place(name) place = @objects[@places[name]] place.nil? ? false : place end
# File lib/petri_net/net.rb, line 327 def get_place_from_marking(marking) return marking if marking.count(1) != 1 get_place_list[marking.index(1)].name end
# File lib/petri_net/net.rb, line 323 def get_place_list @places.map { |_key, pid| @objects[pid] } end
Returns the transition refered by the given name or false if there is no transition with this name
# File lib/petri_net/net.rb, line 135 def get_transition(name) trans = @objects[@transitions[name]] trans.nil? ? false : trans end
# File lib/petri_net/net.rb, line 357 def load(filename) @net = YAML.safe_load(File.read(filename)) end
Merges two PetriNets Places, transitions and arcs are equal if they have the same name and description, arcs need to have the same source and destination too). With this definition of equality the resultung net will have unique ojects. ATTENTION conflicting capabilities and weights will be lost and the properies of the net you merge to will be used in future #TODO add a parameter to affect this!
# File lib/petri_net/net.rb, line 236 def merge(net) return self if equal? net return false if net.class.to_s != 'PetriNet::Net' self << net.get_objects self end
# File lib/petri_net/net.rb, line 349 def objects_find_index(object) @objects.find_index object end
# File lib/petri_net/net.rb, line 337 def objects_include?(object) @objects.include?(object) end
# File lib/petri_net/net.rb, line 333 def objects_size @objects.count { |o| !o.nil? } end
# File lib/petri_net/net.rb, line 244 def reachability_graph update unless @up_to_date generate_reachability_graph unless @graph && @up_to_date @graph end
# File lib/petri_net/net.rb, line 383 def s_invariant raise 'Not jet implemented' end
# File lib/petri_net/net.rb, line 353 def save(filename) File.open(filename, 'w') { |_f| @net.to_yaml } end
# File lib/petri_net/net.rb, line 314 def set_markings(markings) i = 0 @places.each_value do |pid| @objects[pid].set_marking markings[i] i += 1 end changed_state end
# File lib/petri_net/net.rb, line 370 def t_invariants delta = self.delta zero_vector = [] delta.row_count.times { zero_vector << 0 } zero = BigDecimal('0.0') one = BigDecimal('1.0') ps = ludecomp(delta.t.to_a.flatten.map { |i| BigDecimal(i, 16) }, delta.row_count, zero, one) x = lusolve(delta.t.to_a.flatten.map { |i| BigDecimal(i, 16) }, zero_vector.map { |i| BigDecimal(i, 16) }, ps, zero) x end
# File lib/petri_net/net.rb, line 180 def to_gv(output = 'png', filename = '') g = generate_gv filename = "#{@name}_net.png" if filename.empty? g.output(png: filename) if output == 'png' g.output end
Stringify this Petri Net
.
# File lib/petri_net/net.rb, line 160 def to_s str = %(Petri Net [#{@name}] ---------------------------- Description: #{@description} Filename: #{@filename} Places ---------------------------- #{str = ''; @places.each_value { |p| str += @objects[p].to_s + "\n" }; str} Transitions ---------------------------- #{str = ''; @transitions.each_value { |t| str += @objects[t].to_s + "\n" }; str} Arcs ---------------------------- #{str = ''; @arcs.each_value { |a| str += @objects[a].to_s + "\n" }; str} ) str end
# File lib/petri_net/net.rb, line 287 def update generate_weight_function @up_to_date = true end
is true if, and only if, the cached elements are calculated AND the net hasn't changed
# File lib/petri_net/net.rb, line 293 def update? if @w_up_to_date && true # all up_to_date-caches!!! @up_to_date = true return @up_to_date end false end
# File lib/petri_net/net.rb, line 282 def w0(x, y) generate_weight_function unless @w_up_to_date @weight[[x, y]].nil? ? 0 : @weight[[x, y]] end
Private Instance Methods
# File lib/petri_net/net.rb, line 408 def changed_state @up_to_date = false end
# File lib/petri_net/net.rb, line 403 def changed_structure @w_up_to_date = false @up_to_date = false end
# File lib/petri_net/net.rb, line 437 def coverability_helper(markings, source, added_omega = false) @transitions.each_value do |tid| if @objects[tid].fire current_node = PetriNet::ReachabilityGraph::Node.new(@graph, markings: get_markings) current_node_id = @graph.add_node current_node @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: source, destination: current_node, probability: @objects[tid].probability, transition: @objects[tid].name) if current_node_id >= 0 omega = false if current_node_id != -Float::INFINITY && current_node_id < 0 && @graph.get_node(current_node_id * -1) != current_node omega = true added_omega_old = added_omega added_omega = @graph.get_node(current_node_id * -1).add_omega current_node break if added_omega_old == added_omega @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: source, destination: @graph.get_node(current_node_id * -1), probability: @objects[tid].probability, transition: @objects[tid].name) end coverability_helper get_markings, @graph.get_node(current_node_id.abs), added_omega if (current_node_id >= 0 || !omega) && current_node_id != -Float::INFINITY end set_markings markings end end
# File lib/petri_net/net.rb, line 389 def generate_delta d = Array.new(@places.size) { Array.new(@transitions.size) } i = 0 @places.each do |_p_key, p_value| j = 0 @transitions.each do |_t_key, t_value| d[i][j] = w0(t_value, p_value) - w0(p_value, t_value) j += 1 end i += 1 end @delta = Matrix[d] end
# File lib/petri_net/net.rb, line 412 def reachability_helper(markings, source) @transitions.each_value do |tid| raise PetriNet::ReachabilityGraph::InfinityGraphError if @objects[tid].inputs.empty? && !@objects[tid].outputs.empty? next if @objects[tid].inputs.empty? if @objects[tid].fire current_node = PetriNet::ReachabilityGraph::Node.new(@graph, markings: get_markings) begin node_id = @graph.add_node current_node rescue StandardError @graph.add_node! current_node @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: source, destination: current_node) infinity_node = PetriNet::ReachabilityGraph::InfinityNode.new(@graph) @graph.add_node infinity_node @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: current_node, destination: infinity_node) next end current_node = @graph.get_node node_id.abs if node_id < 0 @graph.add_edge PetriNet::ReachabilityGraph::Edge.new(@graph, source: source, destination: current_node, probability: @objects[tid].probability) # if node_id reachability_helper get_markings, current_node if node_id >= 0 end set_markings markings end end