class PetriNet::Net

Attributes

arcs[R]

List of arcs

description[RW]

Description

filename[RW]

Storage filename

markings[R]

List of markings !depricated!

name[RW]

Human readable name

places[R]

List of places

transitions[R]

List of transitions

Public Class Methods

new(options = {}) { |self| ... } click to toggle source

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

<<(object) click to toggle source

Adds an object to the Petri Net. You can add

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
Also aliased as: add_object
add_arc(arc) click to toggle source

Add an arc to the list of arcs.

see PetriNet::Net#add_place

# 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
add_object(object)
Alias for: <<
add_place(place) click to toggle source

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_transition(transition) click to toggle source

Add a transition to the list of transitions.

see PetriNet::Net#add_place

# 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
delta() click to toggle source
# File lib/petri_net/net.rb, line 365
def delta
  generate_delta if @delta.nil?
  @delta
end
fire(transition) click to toggle source
# File lib/petri_net/net.rb, line 361
def fire(transition)
  get_transition(transition).fire
end
generate_coverability_graph() click to toggle source
# 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
generate_gv() click to toggle source
# 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
generate_reachability_graph() click to toggle source
# 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
generate_weight_function() click to toggle source
# 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
get_arc(name) click to toggle source

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
get_marking(places) click to toggle source
# 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
get_markings() click to toggle source
# File lib/petri_net/net.rb, line 302
def get_markings
  @places.map { |_key, pid| @objects[pid].markings.size }
end
get_object(id) click to toggle source
# File lib/petri_net/net.rb, line 341
def get_object(id)
  @objects[id]
end
get_objects() click to toggle source
# File lib/petri_net/net.rb, line 345
def get_objects
  @objects.clone
end
get_place(name) click to toggle source

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
get_place_from_marking(marking) click to toggle source
# 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
get_place_list() click to toggle source
# File lib/petri_net/net.rb, line 323
def get_place_list
  @places.map { |_key, pid| @objects[pid] }
end
get_transition(name) click to toggle source

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
load(filename) click to toggle source
# File lib/petri_net/net.rb, line 357
def load(filename)
  @net = YAML.safe_load(File.read(filename))
end
merge(net) click to toggle source

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
objects_find_index(object) click to toggle source
# File lib/petri_net/net.rb, line 349
def objects_find_index(object)
  @objects.find_index object
end
objects_include?(object) click to toggle source
# File lib/petri_net/net.rb, line 337
def objects_include?(object)
  @objects.include?(object)
end
objects_size() click to toggle source
# File lib/petri_net/net.rb, line 333
def objects_size
  @objects.count { |o| !o.nil? }
end
ordinary?() click to toggle source

Is this Petri Net ordinary? A Petri Net is said to be ordinary if all of its arc weights are 1's.

# File lib/petri_net/net.rb, line 155
def ordinary?
  raise 'Not implemented yet'
end
pure?() click to toggle source

Is this Petri Net pure? A Petri Net is said to be pure if it has no self-loops.

# File lib/petri_net/net.rb, line 149
def pure?
  raise 'Not implemented yet'
end
reachability_graph() click to toggle source
# 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
s_invariant() click to toggle source
# File lib/petri_net/net.rb, line 383
def s_invariant
  raise 'Not jet implemented'
end
save(filename) click to toggle source
# File lib/petri_net/net.rb, line 353
def save(filename)
  File.open(filename, 'w') { |_f| @net.to_yaml }
end
set_markings(markings) click to toggle source
# 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
t_invariants() click to toggle source
# 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
to_gv(output = 'png', filename = '') click to toggle source
# 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
to_s() click to toggle source

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
up_to_date()
Alias for: update?
update() click to toggle source
# File lib/petri_net/net.rb, line 287
def update
  generate_weight_function
  @up_to_date = true
end
update?() click to toggle source

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
Also aliased as: up_to_date
w0(x, y) click to toggle source
# 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

changed_state() click to toggle source
# File lib/petri_net/net.rb, line 408
def changed_state
  @up_to_date = false
end
changed_structure() click to toggle source
# File lib/petri_net/net.rb, line 403
def changed_structure
  @w_up_to_date = false
  @up_to_date = false
end
coverability_helper(markings, source, added_omega = false) click to toggle source
# 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
generate_delta() click to toggle source
# 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
reachability_helper(markings, source) click to toggle source
# 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