dynamical_system 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1f928508792386bcef2acfe99cb615e99b06fb97
4
+ data.tar.gz: 1403ada45408dea284ca54d7c3659a994b081e85
5
+ SHA512:
6
+ metadata.gz: 2144825c5731d2ab450dbba3aaadf58d3f96d3ed793d52c8c99c62fe4c18a6da3cdfaacce134268be22dcecc7389901f9db3526ff94d216b7bcc74eb8422f951
7
+ data.tar.gz: a2f77bde9fccb193cc5645753abe326f4c6b8ef12a3b8e220dde5389ddc6c292ef457bea2627e9e3b1de8a7b759b83d71b46a3c7d10444dd55df2a9169cb0afe
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ ruby '2.2.2'
3
+
4
+ group :development do
5
+ gem 'minitest'
6
+ gem 'jeweler'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.4.0)
5
+ builder (3.2.2)
6
+ descendants_tracker (0.0.4)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ faraday (0.9.2)
9
+ multipart-post (>= 1.2, < 3)
10
+ git (1.2.9.1)
11
+ github_api (0.13.1)
12
+ addressable (~> 2.4.0)
13
+ descendants_tracker (~> 0.0.4)
14
+ faraday (~> 0.8, < 0.10)
15
+ hashie (>= 3.4)
16
+ multi_json (>= 1.7.5, < 2.0)
17
+ oauth2
18
+ hashie (3.4.3)
19
+ highline (1.7.8)
20
+ jeweler (2.0.1)
21
+ builder
22
+ bundler (>= 1.0)
23
+ git (>= 1.2.5)
24
+ github_api
25
+ highline (>= 1.6.15)
26
+ nokogiri (>= 1.5.10)
27
+ rake
28
+ rdoc
29
+ json (1.8.3)
30
+ jwt (1.5.2)
31
+ mini_portile2 (2.0.0)
32
+ minitest (5.8.3)
33
+ multi_json (1.11.2)
34
+ multi_xml (0.5.5)
35
+ multipart-post (2.0.0)
36
+ nokogiri (1.6.7.1)
37
+ mini_portile2 (~> 2.0.0.rc2)
38
+ oauth2 (1.0.0)
39
+ faraday (>= 0.8, < 0.10)
40
+ jwt (~> 1.0)
41
+ multi_json (~> 1.3)
42
+ multi_xml (~> 0.5)
43
+ rack (~> 1.2)
44
+ rack (1.6.4)
45
+ rake (10.5.0)
46
+ rdoc (4.2.1)
47
+ json (~> 1.4)
48
+ thread_safe (0.3.5)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ jeweler
55
+ minitest
56
+
57
+ BUNDLED WITH
58
+ 1.11.2
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Luke Grecki
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,37 @@
1
+ A simple ruby library for discrete dynamical systems.
2
+ -----------------------------------------------------
3
+
4
+ The essence of each system is a rule (given as a hash) that determines state transitions. You can create, evolve, and toy with these systems in a variety of ways.
5
+
6
+ require_relative 'dynamical_system'
7
+
8
+ ### A cyclic system with 3 states
9
+
10
+ rule = { :s1 => :s2, :s2 => :s3, :s3 => :s1 }
11
+ sys = DynamicalSystem.new(rule, :s1)
12
+
13
+ sys.states # => [:s1, :s2, :s3]
14
+ sys.evolve!(6) # => :s1
15
+ sys.history # => [:s1, :s2, :s3, :s1, :s2, :s3, :s1]
16
+
17
+ ### A system with a cycle and a fixed point
18
+
19
+ rule = { :s1 => :s2, :s2 => :s1, :s3 => :s3 }
20
+ sys = DynamicalSystem.new(rule, :s1)
21
+
22
+ sys.fixed_points # => [:s3]
23
+ sys.is_invariant_set?([:s1, :s2]) # => true
24
+
25
+ ### A system with a single attractor
26
+
27
+ rule = { :s1 => :s3, :s2 => :s3, :s3 => :s3 }
28
+ sys = DynamicalSystem.new(rule, :s1)
29
+
30
+ sys.path!(3) # => [:s1, :s3, :s3, :s3]
31
+ sys.is_fixed_point?(:s3) # => true
32
+ sys.is_bijective? # => false
33
+
34
+ ### A random system with 5 states
35
+
36
+ sys = DynamicalSystem.random(5)
37
+ sys.states.size # => 5
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "dynamical_system"
17
+ gem.homepage = "http://github.com/lukegrecki/dynamical_system"
18
+ gem.license = "MIT"
19
+ gem.summary = "A simple ruby library for discrete dynamical systems"
20
+ gem.description = "Define simple dynamical systems, evolve them, and calculate properties."
21
+ gem.email = "lukegrecki@gmail.com"
22
+ gem.authors = ["Luke Grecki"]
23
+ # dependencies defined in Gemfile
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,136 @@
1
+ require 'set'
2
+
3
+ class DynamicalSystem
4
+ attr_reader :state, :states, :history
5
+
6
+ def self.is_valid_rule?(rule)
7
+ return (rule.is_a?(Hash) &&
8
+ rule.values.to_set.subset?(rule.keys.to_set)) ? true : false
9
+ end
10
+
11
+ def self.random(number_of_states)
12
+ rule = {}
13
+ (1..number_of_states).each do |s|
14
+ r = rand(number_of_states) + 1
15
+ rule.merge!({ "s#{s}".to_sym => "s#{r}".to_sym })
16
+ end
17
+ return self.new(rule, :s1)
18
+ end
19
+
20
+ def inspect
21
+ puts "state : #{@state}\n" + "history : #{history}\n" +
22
+ "rule : #{@rule}\n" + "states : #{@states}"
23
+ end
24
+
25
+ def initialize(rule, initial_state)
26
+ if DynamicalSystem.is_valid_rule?(rule)
27
+ @rule = rule
28
+ @states = rule.keys
29
+ @state = initial_state
30
+ @history = [initial_state]
31
+ else
32
+ raise RuleError
33
+ end
34
+ end
35
+
36
+ def state=(new_state)
37
+ @state = new_state
38
+ @history = [@state] #resets history
39
+ end
40
+
41
+ def evolve!(steps = 1, state = @state)
42
+ set_state(state) if state != @state
43
+ steps.times { @state = @rule[@state]; @history << @state }
44
+ return @state
45
+ end
46
+
47
+ def evolve(steps = 1, state = @state)
48
+ steps.times { state = @rule[state] }
49
+ return state
50
+ end
51
+
52
+ def path!(steps = 1, state = @state)
53
+ set_state(state) if state != @state
54
+ path = [@state]
55
+ steps.times { @state = @rule[@state]; @history << @state; path << @state }
56
+ return path
57
+ end
58
+
59
+ def path(steps = 1, state = @state)
60
+ path = [state]
61
+ steps.times { state = @rule[state]; path << state }
62
+ return path
63
+ end
64
+
65
+ def forward_orbit(state = @state)
66
+ forward_orbit = [state]
67
+ new_state = @rule[state]
68
+ until forward_orbit.include?(new_state) do
69
+ forward_orbit << new_state
70
+ new_state = @rule[new_state]
71
+ end
72
+ forward_orbit << new_state #needed to see the cycle
73
+ return forward_orbit
74
+ end
75
+
76
+ def backward_orbit(state = @state)
77
+ raise RuleError, "Is not bijective" unless is_bijective?
78
+ backward_orbit = [state]
79
+ old_state = @rule.key(state)
80
+ until backward_orbit.include?(old_state) do
81
+ backward_orbit << old_state
82
+ old_state = @rule.key(old_state)
83
+ end
84
+ backward_orbit << old_state #needed to see the cycle
85
+ return backward_orbit
86
+ end
87
+
88
+ def orbit(state = @state)
89
+ raise RuleError, "Is not bijective" unless is_bijective?
90
+ return backward_orbit(state)[0...-1] += (forward_orbit(state))
91
+ end
92
+
93
+ def cycle_from_forward_orbit(forward_orbit)
94
+ return forward_orbit[forward_orbit.index(forward_orbit.last)...-1]
95
+ end
96
+
97
+ def cycles
98
+ return @cycles if @cycles
99
+ @cycles = []
100
+ visited_states = []
101
+ states_array = Array(@states)
102
+ until visited_states.size == @states.size do
103
+ start_state = (states_array - visited_states).sample
104
+ forward_orbit = forward_orbit(start_state)
105
+ cycle = cycle_from_forward_orbit(forward_orbit)
106
+ cycle_set = cycle.to_set
107
+ cycle_sets = @cycles.map { |c| c.to_set }
108
+ @cycles << cycle unless cycle_sets.include?(cycle_set)
109
+ visited_states |= forward_orbit
110
+ end
111
+ return @cycles
112
+ end
113
+
114
+ def is_fixed_point?(state = @state)
115
+ return state == evolve(1, state) ? true : false
116
+ end
117
+
118
+ def fixed_points
119
+ @fixed_points ||= @states.select { |s| is_fixed_point?(s) }
120
+ end
121
+
122
+ def is_invariant_set?(state_array)
123
+ new_state_array = state_array.map { |s| evolve(1, s) }
124
+ return new_state_array.to_set == state_array.to_set ? true : false
125
+ end
126
+
127
+ def is_bijective?
128
+ return @states.size == @rule.values.uniq.size
129
+ end
130
+
131
+ alias :set_state :state=
132
+ end
133
+
134
+ class StateError < StandardError; end
135
+ class RuleError < StandardError; end
136
+
data/test/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+ require 'minitest/autorun'
10
+
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ require 'dynamical_system'
@@ -0,0 +1,104 @@
1
+ require 'helper'
2
+
3
+ class TestDynamicalSystem < Minitest::Test
4
+
5
+ def setup
6
+ @rule = { :s1 => :s2, :s2 => :s1, :s3 => :s3 }
7
+ @sys = DynamicalSystem.new(@rule, :s1)
8
+ end
9
+
10
+ def test_initialization
11
+ assert_instance_of(DynamicalSystem, DynamicalSystem.new(@rule, :s1))
12
+ end
13
+
14
+ def test_is_valid_rule?
15
+ @rule = 0 #not a Hash
16
+ assert_equal(DynamicalSystem.is_valid_rule?(@rule), false)
17
+
18
+ @rule = { :s1 => :s2, :s3 => :s3 } #undefined on :s2
19
+ assert_equal(DynamicalSystem.is_valid_rule?(@rule), false)
20
+ end
21
+
22
+ def test_random
23
+ random_sys = DynamicalSystem.random(5)
24
+ assert_instance_of(DynamicalSystem, random_sys)
25
+ assert_equal(5, random_sys.states.size)
26
+ end
27
+
28
+ def test_evolve!
29
+ assert_equal(@sys.evolve!(2, :s2), :s2)
30
+ assert_equal(@sys.state, :s2)
31
+ assert_equal(@sys.history, [:s2, :s1, :s2])
32
+ end
33
+
34
+ def test_evolve
35
+ assert_equal(@sys.evolve(2, :s2), :s2)
36
+ assert_equal(@sys.state, :s1)
37
+ end
38
+
39
+ def test_path!
40
+ assert_equal(@sys.path!(2, :s2), [:s2, :s1, :s2])
41
+ assert_equal(@sys.state, :s2)
42
+ assert_equal(@sys.history, [:s2, :s1, :s2])
43
+ end
44
+
45
+ def test_path
46
+ assert_equal(@sys.path(2, :s2), [:s2, :s1, :s2])
47
+ assert_equal(@sys.state, :s1)
48
+ end
49
+
50
+ def test_forward_orbit
51
+ @rule.merge!({ :s2 => :s4, :s4 => :s2 })
52
+ @sys = DynamicalSystem.new(@rule, :s1)
53
+ assert_equal(@sys.forward_orbit(:s1), [:s1, :s2, :s4, :s2])
54
+ end
55
+
56
+ def test_backward_orbit
57
+ assert_equal(@sys.backward_orbit, [:s1, :s2, :s1])
58
+ assert_equal(@sys.backward_orbit(:s3), [:s3, :s3])
59
+ end
60
+
61
+ def test_orbit
62
+ assert_equal(@sys.orbit, [:s1, :s2, :s1, :s2, :s1])
63
+ assert_equal(@sys.orbit(:s3), [:s3, :s3, :s3])
64
+ end
65
+
66
+ def test_cycle_from_forward_orbit
67
+ @rule.merge!({ :s2 => :s4, :s4 => :s2 })
68
+ @sys = DynamicalSystem.new(@rule, :s1)
69
+ forward_orbit = @sys.forward_orbit(:s1)
70
+ assert_equal([:s2, :s4], @sys.cycle_from_forward_orbit(forward_orbit))
71
+ end
72
+
73
+ def test_cycles
74
+ @rule.merge!({ :s2 => :s4, :s4 => :s2 })
75
+ @sys = DynamicalSystem.new(@rule, :s1)
76
+ assert_equal(true, [[:s2, :s4], [:s3]].to_set == @sys.cycles.to_set || \
77
+ [[:s4, :s2], [:s3]].to_set == @sys.cycles.to_set)
78
+ end
79
+
80
+ def test_is_fixed_point?
81
+ assert_equal(@sys.is_fixed_point?(:s1), false)
82
+ assert_equal(@sys.is_fixed_point?(:s3), true)
83
+ end
84
+
85
+ def test_fixed_points
86
+ @rule = { :s1 => :s2, :s2 => :s1, :s3 => :s3, :s4 => :s4 }
87
+ @sys = DynamicalSystem.new(@rule, :s1)
88
+ assert_equal(@sys.fixed_points.to_set, Set.new([:s3, :s4]))
89
+ end
90
+
91
+ def test_is_invariant_set?
92
+ assert_equal(@sys.is_invariant_set?([:s1, :s2]), true)
93
+ assert_equal(@sys.is_invariant_set?([:s1, :s3]), false)
94
+ end
95
+
96
+ def test_is_bijective?
97
+ assert_equal(@sys.is_bijective?, true)
98
+
99
+ @rule_2 = { :s1 => :s1, :s2 => :s1 }
100
+ @sys_2 = DynamicalSystem.new(@rule_2, :s1)
101
+ assert_equal(@sys_2.is_bijective?, false)
102
+ end
103
+ end
104
+
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dynamical_system
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Luke Grecki
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jeweler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Define simple dynamical systems, evolve them, and calculate properties.
42
+ email: lukegrecki@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files:
46
+ - LICENSE
47
+ - README.markdown
48
+ files:
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - LICENSE
52
+ - README.markdown
53
+ - Rakefile
54
+ - VERSION
55
+ - lib/dynamical_system.rb
56
+ - test/helper.rb
57
+ - test/test_dynamical_system.rb
58
+ homepage: http://github.com/lukegrecki/dynamical_system
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.4.5
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A simple ruby library for discrete dynamical systems
82
+ test_files: []