petri_net 0.2.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3862dd137aeee506ef93dc6c0247d5f872bb5dba
4
+ data.tar.gz: 1159a8fa6abde650583c95cd80a9d54a749b57e3
5
+ SHA512:
6
+ metadata.gz: ad0e40f4bfb501824223f2f36fc0dc32aa42391e8903fd2a21f5805f88ec946b91f7cf238124d6f7fb06da055d65c50301f44f3ca2b9fd9279469a683e0b2885
7
+ data.tar.gz: c43775deaab72b0f5a8c56a7040665d2762d7ac4ecbd07230bb4382cab51998fc9fd81fd4c653f32153dded901baab56d5761b8f69a1a12d3f7bf785075b01aa
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0
data/CHANGELOG ADDED
@@ -0,0 +1,8 @@
1
+ 2009-08-14 Brian D. Nelson <bdnelson@wildcoder.com>
2
+ * Initial project creation.
3
+
4
+ 2010-08-26 Brian D. Nelson <bdnelson@wildcoder.com>
5
+ * Restart of project
6
+
7
+ 2013-13.12 Christian Clausen <cclausen@tzi.de>
8
+ * Fork project to create a petrinet library to use for my Bachelor-Thesis
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2013, Christian Clausen (cclausen@tzi.de)
2
+
3
+ Copyright (c) 2009, Brian D. Nelson (bdnelson@wildcoder.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,93 @@
1
+ = PTNet: Petri-net Simulation in Ruby
2
+ ===== based on the code by Brian D Nelson <bdnelson@wildcoder.com>
3
+ ===== Christian Clausen cclausen@tzi.de
4
+
5
+ == Version 0.5
6
+ I implemented most of the basics you need for playing with PetriNets but there are a lot things to add.
7
+ Next big step will be a stochastic analysis of the PetriNet.
8
+
9
+ == Overview
10
+ This package provides a ruby based simulation package for Petri nets.
11
+ You can build PetriNets, fire transitions and calculate statistica or solve problems in the net.
12
+ It also provides a visualization of the net (and even the Reachability Graph of the given net)
13
+
14
+
15
+ == Petri Net Resources
16
+ * http://en.wikipedia.org/wiki/Petri_net - Wikipedia
17
+
18
+ == Usage
19
+ === Creation
20
+ At first you need to create a blank petrinet:
21
+ net = PetriNet::Net.new
22
+ Then you can add places and transitions:
23
+ net << PetriNet::Place.new
24
+ net << PetriNet::Transition.new
25
+ It's a good idea to name the places and transitions, as you need to refer to them to add, so the better way will be:
26
+ net << PetriNet::Place.new(name: "A")
27
+ net << PetriNet::Transition.new(name: "a")
28
+ net << PetriNet::Arc.new(source: "A", destination: "a")
29
+ As you see, an arc needs to get a source and a destination. You can, but you don't need to name your arcs, as you don't need to refer to them later in most cases.
30
+ You can get a place/transition/arc by
31
+ net.get_place( name )
32
+ net.get_transition( name )
33
+ net.get_arc( name )
34
+ Or if you know the id (but not the name) you can use
35
+ net.get_object(id)
36
+ Of course you can save the Objects while creating them and add them afterwards:
37
+ place_B = PetriNet::Place.new(name: "B")
38
+ net << place_B
39
+ or
40
+ net.add_object place_B
41
+ or
42
+ net.add_place place_B
43
+ if you want to use this.
44
+ PetriNet#add_object even accepts arrays of accepted objects
45
+
46
+ To create an arc you do not need to specify source and destination, you can add them with "add_source" and "add_destination" later on, but an arc has to have both before being added to a net.
47
+
48
+ === Markings
49
+ !!! Attention, I'm not sure how I will implement markings in the end, this part might be wrong because I changed something. I'm not happy with the way I handle markings/token currently. Currently are PetriNet::Base-Objects which makes no sence in my opinion.
50
+
51
+ !!! Attention, Markings are inconsistens at the moment. In the original code markings are what I call "token" or "tokken" and a marking is the "state" of a petrinet, which means how many token are in which places. It's hard to understand which markings are meant as currently everything is called "marking" I will refactor this some time.
52
+
53
+ You can create markings like other PetriNet::Base objects with
54
+ marking = PetriNet::Marking.new
55
+ And add them to a place with
56
+ place_B.add_marking(marking)
57
+
58
+ But since I see no sence in creating the specific markings you can do
59
+ place_B.add_marking(3)
60
+ to add three markings to a place. Without any argument it will add just one marking.
61
+
62
+ You can get the current marking with net.get_markings. This will give you an Array of state-markings. [1,3,2,4] would mean, the first place (in order of id's, but be careful, every PetriNet::Base-object has a unique id) will hold one marking (or token) the second place will hold 3 and so on.
63
+ You can even set the markings with
64
+ net.set_markings([1,3,2,4]
65
+ but be carefull with this one, maybe this should be private in future but it can be very usefull to save the initial state or to set the net to a specific state for some algorithms (e.g. for generating the Reachability Graph)
66
+
67
+ === Visualization
68
+ You can just print yout PetriNet with
69
+ net.to_s
70
+ as you should know this means you can write
71
+ puts net
72
+
73
+ For a more fancy way to show off with your PetriNet you should try
74
+ net.to_gv
75
+ which generates the GraphViz-Code for the PetriNet.
76
+ It is planned to use the GraphViz-gem in the future to create png and stuff like that directly.
77
+
78
+ === Reachability Graph
79
+ The Reachablility Graph is a Graph with nodes representing reachable markings of the net in the current state and edges reresenting the transitions you need to get from one node to another.
80
+ You can generate the reachablility Graph with
81
+ net.generate_reachability_graph
82
+ As a PetriNet itself you can to_s and to_gv the reachablility Graph too.
83
+
84
+ === More documentation will come soon
85
+
86
+ == Contributing
87
+ Neither the library nor the documentation is finished by now, so please feel free to contribute
88
+
89
+ Just write an email and/or fork this project. Active contributors will get write-access to this repository too.
90
+
91
+
92
+ == Gem
93
+ This library is available on rubygems.org soon
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'rake/testtask'
3
+ require 'hanna/rdoctask'
4
+ require 'net/sftp'
5
+ require 'fileutils'
6
+
7
+ desc 'Default task'
8
+ task :default => [:test, :rdoc, :push, :clean]
9
+
10
+ task(:test) { puts "==> Running main test suite" }
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.test_files = FileList['test/tc_*.rb']
13
+ t.ruby_opts = ['-rubygems'] if defined? Gem
14
+ end
15
+
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_files.include('LICENSE', 'CHANGELOG', 'README', 'lib/')
18
+ rdoc.title = "PetriNet Documentation"
19
+ rdoc.options << '--webcvs=http://svn.wildcoder.com/svn/petri/trunk/'
20
+ rdoc.rdoc_dir = 'doc' # rdoc output folder
21
+ end
22
+
23
+ desc 'Clean up unused files.'
24
+ task :clean => :clobber_rdoc do
25
+ end
26
+
27
+ desc 'Run tests.'
28
+ task :test do
29
+ end
30
+
@@ -0,0 +1,133 @@
1
+ module PetriNet
2
+ # Arc
3
+ class Arc < PetriNet::Base
4
+ # Unique ID
5
+ attr_reader :id
6
+ # human readable name
7
+ attr_accessor :name
8
+ # Description
9
+ attr_accessor :description
10
+ # Arc weight
11
+ attr_accessor :weight
12
+ # Source-object
13
+ attr_reader :source
14
+ # Source-object
15
+ attr_reader :destination
16
+ # The net this arc belongs to
17
+ attr_writer :net
18
+
19
+ # Creates an arc.
20
+ # An arc is an directed edge between a place and a transition (or visa versa) and can have a weight which indicates how many token it comsumes or produces from/to the place
21
+ def initialize(options = {}, &block)
22
+ @id = next_object_id
23
+ @name = (options[:name] or "Arc#{@id}")
24
+ @description = (options[:description] or "Arc #{@id}")
25
+ @weight = (options[:weight] or 1)
26
+ self.add_source(options[:source]) unless options[:source].nil?
27
+ self.add_destination(options[:destination]) unless options[:destination].nil?
28
+
29
+ yield self unless block == nil
30
+ end
31
+
32
+ # Add a source object to this arc. Validation of the source object will be performed
33
+ # before the object is added to the arc and an exception will be raised.
34
+ def add_source(object)
35
+ if object.class.to_s == "String"
36
+ object = (net.get_place object or net.get_transition object)
37
+ end
38
+ if validate_source_destination(object)
39
+ @source = object
40
+ object.add_output(self)
41
+ else
42
+ raise "Invalid arc source object: #{object.class}"
43
+ end
44
+ end
45
+
46
+ # Add a destination object
47
+ def add_destination(object)
48
+ if object.class.to_s == "String"
49
+ object = (net.get_place object or net.get_transition object)
50
+ end
51
+ if validate_source_destination(object)
52
+ @destination = object
53
+ object.add_input(self)
54
+ else
55
+ raise "Invalid arc destination object: #{object.class}"
56
+ end
57
+ end
58
+
59
+ # A Petri Net is said to be ordinary if all of its arc weights are 1's.
60
+ # Is this arc ordinary?
61
+ def ordinary?
62
+ @weight == 1
63
+ end
64
+
65
+ # Validate this arc.
66
+ def validate(net)
67
+ return false if @id < 1
68
+ return false if @name.nil? or @name.length <= 0
69
+ return false if @weight < 1
70
+ return false if @source.nil? or @destination.nil?
71
+ return false if @source == @destination
72
+ return false if @source.class == @destination.class
73
+
74
+ if @source.class.to_s == "PetriNet::Place"
75
+ return net.objects_include? @source
76
+ elsif @source.class.to_s == "PetriNet::Transition"
77
+ return net.objects_include? @source
78
+ else
79
+ return false
80
+ end
81
+ if @destination.class.to_s == "PetriNet::Place"
82
+ return net.objects.include? @destination
83
+ elsif @destination.class.to_s == "PetriNet::Transition"
84
+ return net.objects.include? @destination
85
+ else
86
+ return false
87
+ end
88
+ return true
89
+ end
90
+
91
+ # Stringify this arc.
92
+ def to_s
93
+ "#{@id}: #{@name} (#{@weight}) #{@source.id} -> #{@destination.id}"
94
+ end
95
+
96
+ # Gives the GraphViz-representation of this arc as string of a GV-Edge
97
+ def to_gv
98
+ "\t#{@source.gv_id} -> #{@destination.gv_id} [ label = \"#{@name}\", headlabel = \"#{@weight}\" ];\n"
99
+ end
100
+
101
+ # Checks if the information in this arc are still correct.
102
+ # The information can get wrong if you merge two nets together.
103
+ def need_update? net
104
+ if net.get_object(@source.id).nil? || (@source.name != net.get_object(@source.id).name)
105
+ return true
106
+ end
107
+ if net.get_object(@destination.id).nil? || (@destination.name != net.get_object(@destination.id).name)
108
+ return true
109
+ end
110
+ end
111
+
112
+ # Updates the information in this arc
113
+ # Should only be necessary if PetriNet::Arc#need_update? is true
114
+ # affects source and destination
115
+ def update net
116
+ @source.id = net.objects_find_index @source
117
+ @destination.id = net.objects_find_index @destination
118
+ end
119
+
120
+ private
121
+
122
+ # Validate source or destination object
123
+ def validate_source_destination(object)
124
+ return false if object.nil?
125
+
126
+ return object.class.to_s == "PetriNet::Place" || object.class.to_s == "PetriNet::Transition"
127
+
128
+ #return if @source.nil? or @source.class.to_s == object.class.to_s
129
+ #return if @destination.nil? or @destination.class.to_s == object.class.to_s
130
+ return true
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,28 @@
1
+ module PetriNet
2
+ # Common structure
3
+ class Base
4
+ # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class.
5
+ attr_accessor :logger
6
+
7
+ # Global object count.
8
+ @@object_count = 0
9
+
10
+ # Initialize the base class.
11
+ def initialize(options = {})
12
+ @logger = Logger.new(STDOUT)
13
+ @logger.level = Logger::INFO
14
+ end
15
+
16
+ # Get the next object ID (object count).
17
+ def next_object_id
18
+ @@object_count += 1
19
+ end
20
+
21
+ # Resets the object-count
22
+ # This should not be used without extreme care
23
+ # It's made for testing-purposes only
24
+ def reset
25
+ @@object_count = 0
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ class PetriNet::ReachabilityGraph::Edge < PetriNet::Base
2
+ # Human readable name
3
+ attr_reader :name
4
+ # Unique ID
5
+ attr_reader :id
6
+ # Graph this edge belongs to
7
+ attr_accessor :graph
8
+
9
+ # Creates an edge for PetriNet::ReachabilityGraph
10
+ def initialize(options = {}, &block)
11
+ @id = next_object_id
12
+ @name = (options[:name] or "Edge#{@id}")
13
+ @description = (options[:description] or "Edge #{@id}")
14
+ @source = options[:source]
15
+ @destination = options[:destination]
16
+ @label = (options[:label] or @name)
17
+
18
+ yield self unless block.nil?
19
+ end
20
+
21
+ # Validates the data holded by this edge, this will be used while adding the edge to the graph
22
+ def validate
23
+ true
24
+ end
25
+
26
+ def to_gv
27
+ "\t#{@source.gv_id} -> #{@destination.gv_id};\n"
28
+ end
29
+
30
+ def ==(object)
31
+ return false unless object.class.to_s == "PetriNet::ReachabilityGraph::Edge"
32
+ (@source == object.yource && @destination == oject.destination)
33
+ end
34
+ def to_s
35
+ "#{@id}: #{@name} #{@source.id} -> #{@destination} )"
36
+ end
37
+
38
+ end
@@ -0,0 +1,28 @@
1
+ module PetriNet
2
+ # Marking
3
+ class Marking < PetriNet::Base
4
+ # depricated
5
+ attr_accessor :id # Unique ID
6
+ # depricated
7
+ attr_accessor :name # Human readable name
8
+ # depricated
9
+ attr_accessor :description # Description
10
+ # depricated
11
+ attr_accessor :timestep # Marking timestep
12
+
13
+ # Create a new marking.
14
+ # depricated
15
+ def initialize(options = {}, &block)
16
+
17
+ yield self unless block == nil
18
+ end
19
+
20
+ # Validate this marking.
21
+ def validate
22
+ end
23
+
24
+ # Stringify this marking.
25
+ def to_s
26
+ end
27
+ end
28
+ end