optimal-control 0.0.2
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.
- data/CHANGELOG +4 -0
- data/Manifest +11 -0
- data/README +17 -0
- data/Rakefile +36 -0
- data/bin/optimalcontrol +58 -0
- data/lib/cgl/evaluate.rb +26 -0
- data/lib/cgl/neighbors.rb +26 -0
- data/lib/cgl/runner.rb +90 -0
- data/optimal-control.gemspec +35 -0
- data/test/test_evaluate.rb +48 -0
- data/test/test_neighbors.rb +43 -0
- data/test/test_runner.rb +114 -0
- metadata +81 -0
data/CHANGELOG
ADDED
data/Manifest
ADDED
data/README
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Optimal Control for Conway's game of Life - 0.0.1
|
2
|
+
Franziska Hinkelmann
|
3
|
+
fhinkel@vt.edu
|
4
|
+
|
5
|
+
|
6
|
+
This gem will generate an optimal controller for Conway's game of Life on a n
|
7
|
+
x n grid with periodic boundary conditions. The control objective is to have
|
8
|
+
the left upper corner dead after m iterations, the control that can be applied
|
9
|
+
is to kill the cell one to the right of it.
|
10
|
+
|
11
|
+
Cost for control is 1 cost unit, independent of the iteration step it is
|
12
|
+
applied to. Every initial state to which control should be applied is
|
13
|
+
reported, including the iteration steps at which control should be applied.
|
14
|
+
|
15
|
+
Unit tests are in test/
|
16
|
+
|
17
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rake/gempackagetask'
|
2
|
+
require 'echoe'
|
3
|
+
|
4
|
+
#spec = Gem::Specification.new do |s|
|
5
|
+
# s.name = "optimal-control"
|
6
|
+
# s.summary = "Simulate Conway's game of Life on a small grid with periodic
|
7
|
+
# boundary conditions and find the optimal controller for the specified
|
8
|
+
# control objective and control options"
|
9
|
+
# s.description= File.read(File.join(File.dirname(__FILE__), 'README'))
|
10
|
+
# s.author = "Franziska Hinkelmann"
|
11
|
+
# s.email = "fhinkel@vt.edu"
|
12
|
+
# s.version = "0.0.2"
|
13
|
+
# s.platform = Gem::Platform::RUBY
|
14
|
+
# s.required_ruby_version = '>=1.8.6'
|
15
|
+
# s.files = Dir['**/**']
|
16
|
+
# s.executables = [ 'optimalcontrol' ]
|
17
|
+
# s.test_files = Dir["test/test*.rb"]
|
18
|
+
# s.has_rdoc = false
|
19
|
+
#end
|
20
|
+
#Rake::GemPackageTask.new(spec).define
|
21
|
+
|
22
|
+
spec = Echoe.new("optimal-control") do |s|
|
23
|
+
s.summary = "Simulate Conway's game of Life on a small grid with periodic
|
24
|
+
boundary conditions and find the optimal controller for the specified
|
25
|
+
control objective and control options"
|
26
|
+
#s.description= File.read(File.join(File.dirname(__FILE__), 'README'))
|
27
|
+
s.author = "Franziska Hinkelmann"
|
28
|
+
s.email = "fhinkel@vt.edu"
|
29
|
+
s.url = "http://rubyforge.org/projects/optimal-control/"
|
30
|
+
s.platform = Gem::Platform::RUBY
|
31
|
+
#s.required_ruby_version = '>=1.8.6'
|
32
|
+
#s.files = Dir['**/**']
|
33
|
+
#s.executables = [ 'optimalcontrol' ]
|
34
|
+
#s.test_files = Dir["test/test*.rb"]
|
35
|
+
#s.has_rdoc = false
|
36
|
+
end
|
data/bin/optimalcontrol
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.dirname(__FILE__) + '/../lib/cgl/runner'
|
3
|
+
|
4
|
+
# Optimal Control for a 4by4 Conways game of life, control objective is
|
5
|
+
# defined in the assign_final_cost function
|
6
|
+
# assume control is 1 unit cost per cell, the central 4 cells can be
|
7
|
+
# independently controlled
|
8
|
+
|
9
|
+
if /-h/ =~ ARGV.to_s
|
10
|
+
puts
|
11
|
+
puts "Optimal Control for Conway's Game of Life"
|
12
|
+
puts
|
13
|
+
puts "Usage: bin/optimalconrol Iterations Height Width"
|
14
|
+
puts "Default values are 5 for the number of steps to iterate, and 2 as\
|
15
|
+
grid height and width of the grid."
|
16
|
+
puts
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
Steps = ARGV[0].to_i - 1 || 4 # Steps + 1 iterations
|
21
|
+
|
22
|
+
unless 0 <= Steps
|
23
|
+
puts "The first argument, the number of iterations, must be an integer at least 1"
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
Max_cost = (Steps+1)*4 + 1 # make states that are not admissible more expensive than
|
28
|
+
# applying control before all #Steps iterations
|
29
|
+
|
30
|
+
Grid_height = ARGV[1].to_i || 2
|
31
|
+
unless 0 < Grid_height && Grid_height < 5
|
32
|
+
puts "The second argument, height of the grid, must be an integer between 1 and 5"
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
Grid_width = ARGV[2].to_i || Grid_height # if no hight is given, use a square grid
|
36
|
+
unless 0 < Grid_width && Grid_width < 5
|
37
|
+
puts "The third argument, width of the grid, must be an integer between 1 and 5"
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
runner = Cgl::Runner.new(Steps, Max_cost, Grid_height, Grid_width)
|
43
|
+
|
44
|
+
# result = [cost_vector[0], control[0]]
|
45
|
+
result = runner.run
|
46
|
+
|
47
|
+
n_agents = Grid_height * Grid_width
|
48
|
+
cost_vector = result[0]
|
49
|
+
control = result[1]
|
50
|
+
|
51
|
+
# print all states that can lead to a valid final state
|
52
|
+
# TODO print control input to reach final state
|
53
|
+
for i in 0..2**n_agents-1 do
|
54
|
+
if (cost_vector[i]>0)
|
55
|
+
puts "%0#{n_agents}d" % i.to_s(2) + ": cost = #{cost_vector[i]}, control applied at timeSteps \"#{control[i]}\""
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
data/lib/cgl/evaluate.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cgl
|
2
|
+
class Evaluate
|
3
|
+
# given an array of length 9, where the first entry is the agent to
|
4
|
+
# update, return the agents updated value
|
5
|
+
def self.evaluate( neighbors )
|
6
|
+
agent = neighbors.shift.to_i
|
7
|
+
number_of_alive_neighbors = neighbors.inject(0){|sum,item| sum.to_i + item.to_i }
|
8
|
+
# assume the very first column is the agent we're computing the next state
|
9
|
+
# for
|
10
|
+
if (number_of_alive_neighbors < 2)
|
11
|
+
f = 0
|
12
|
+
elsif (number_of_alive_neighbors == 2)
|
13
|
+
if (agent==1)
|
14
|
+
f = 1
|
15
|
+
else
|
16
|
+
f = 0
|
17
|
+
end
|
18
|
+
elsif (number_of_alive_neighbors == 3)
|
19
|
+
f = 1
|
20
|
+
else # number_of_alive_neighbors > 3
|
21
|
+
f = 0
|
22
|
+
end
|
23
|
+
f
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cgl
|
2
|
+
class Neighbors
|
3
|
+
# given the index of an agent and the full grid, return an array with the
|
4
|
+
# values of the 8 neighboring agents
|
5
|
+
# assume periodic boundary conditions (wrap)
|
6
|
+
# all indexing is 0 based, so a 3 x 4 grid has these indexes:
|
7
|
+
# 0 1 2 3
|
8
|
+
# 4 5 6 7
|
9
|
+
# 8 9 10 11
|
10
|
+
#
|
11
|
+
def self.neighbors(agent, all_agents, height, width = height)
|
12
|
+
neighbors = Array.new
|
13
|
+
row = agent/width
|
14
|
+
column = agent%width
|
15
|
+
neighbors.push all_agents[(row*width)+(column+1)%width]
|
16
|
+
neighbors.push all_agents[((row+1)%height)*width+(column+1)%width]
|
17
|
+
neighbors.push all_agents[((row+1)%height)*width+(column)]
|
18
|
+
neighbors.push all_agents[((row+1)%height)*width+(column-1)%width]
|
19
|
+
neighbors.push all_agents[row*width+(column-1)%width]
|
20
|
+
neighbors.push all_agents[(row-1)*width+(column-1)%width]
|
21
|
+
neighbors.push all_agents[(row-1)*width+(column)]
|
22
|
+
neighbors.push all_agents[(row-1)*width+(column+1)%width]
|
23
|
+
neighbors
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/cgl/runner.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/evaluate'
|
2
|
+
require File.dirname(__FILE__) + '/neighbors'
|
3
|
+
|
4
|
+
module Cgl
|
5
|
+
class Runner
|
6
|
+
attr_reader :steps
|
7
|
+
attr_reader :max_cost
|
8
|
+
attr_reader :grid_height
|
9
|
+
attr_reader :grid_width
|
10
|
+
attr_reader :n_agents
|
11
|
+
def initialize( steps, max_cost, grid_height, grid_width = grid_height )
|
12
|
+
@steps = steps
|
13
|
+
@max_cost = max_cost
|
14
|
+
@grid_height = grid_height
|
15
|
+
@grid_width = grid_width
|
16
|
+
@n_agents = grid_height * grid_width
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
cost_vector = Array.new(@steps+2) # cost_vector[i][state]cost of getting from state to admissible state in
|
21
|
+
# @steps+2-i time@steps
|
22
|
+
|
23
|
+
# control[j][i] has a sequence for applying control to state i
|
24
|
+
control = Array.new(@steps+2) # need an extra vector of empty strings, so we
|
25
|
+
#can later concatenate without worrying about being at the last entry
|
26
|
+
for i in 0..@steps+1 do
|
27
|
+
cost_vector[i] = Array.new(2**@n_agents)
|
28
|
+
control[i] = Array.new(2**@n_agents, "")
|
29
|
+
end
|
30
|
+
|
31
|
+
for i in 0..2**@n_agents-1 do
|
32
|
+
cost_vector[@steps+1][i] = assign_final_cost(i)
|
33
|
+
end
|
34
|
+
|
35
|
+
for j in 0..@steps do
|
36
|
+
puts @steps - j
|
37
|
+
for i in 0..2**@n_agents-1 do
|
38
|
+
i_as_array = ("%0#{@n_agents}d" % i.to_s(2)).split(//)
|
39
|
+
|
40
|
+
# calculate cost without control
|
41
|
+
output = iterate i_as_array
|
42
|
+
state = output.to_s.to_i(2)
|
43
|
+
cost_without_control = cost_vector[@steps-j+1][state]
|
44
|
+
|
45
|
+
# calculate cost with control and take min
|
46
|
+
# TODO pass agent to kill as arg
|
47
|
+
# control: kill agent 2
|
48
|
+
i_as_array[1] = "0"
|
49
|
+
output = iterate i_as_array
|
50
|
+
state_with_control = output.to_s.to_i(2)
|
51
|
+
cost_for_control = 1
|
52
|
+
cost_with_control = cost_vector[@steps-j+1][state_with_control] + cost_for_control
|
53
|
+
|
54
|
+
# take min and record if control was applied
|
55
|
+
if (cost_without_control < cost_with_control)
|
56
|
+
cost_vector[@steps-j][i] = cost_without_control
|
57
|
+
control[@steps-j][i] = control[@steps-j+1][state]
|
58
|
+
else
|
59
|
+
cost_vector[@steps-j][i] = cost_with_control
|
60
|
+
control[@steps-j][i] = "#{@steps-j+1}" + control[@steps-j+1][state_with_control]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
[cost_vector[0], control[0]]
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
def assign_final_cost( state )
|
70
|
+
# valid if agent 1 is dead
|
71
|
+
if ( state < 2**(@n_agents - 1) )
|
72
|
+
cost = 0
|
73
|
+
else
|
74
|
+
cost = @max_cost
|
75
|
+
end
|
76
|
+
cost
|
77
|
+
end
|
78
|
+
|
79
|
+
def iterate( i_as_array )
|
80
|
+
output = Array.new(@n_agents)
|
81
|
+
for k in 0..@n_agents-1 do # update all N_agents states
|
82
|
+
input_data = Cgl::Neighbors.neighbors(k, i_as_array, @grid_height,
|
83
|
+
@grid_width)
|
84
|
+
output[k] = Cgl::Evaluate.evaluate([i_as_array[k]] + input_data)
|
85
|
+
end
|
86
|
+
output
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{optimal-control}
|
5
|
+
s.version = "0.0.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Franziska Hinkelmann"]
|
9
|
+
s.date = %q{2010-01-01}
|
10
|
+
s.default_executable = %q{optimalcontrol}
|
11
|
+
s.description = %q{Simulate Conway's game of Life on a small grid with periodic
|
12
|
+
boundary conditions and find the optimal controller for the specified
|
13
|
+
control objective and control options}
|
14
|
+
s.email = %q{fhinkel@vt.edu}
|
15
|
+
s.executables = ["optimalcontrol"]
|
16
|
+
s.extra_rdoc_files = ["CHANGELOG", "README", "bin/optimalcontrol", "lib/cgl/evaluate.rb", "lib/cgl/neighbors.rb", "lib/cgl/runner.rb"]
|
17
|
+
s.files = ["CHANGELOG", "Manifest", "README", "Rakefile", "bin/optimalcontrol", "lib/cgl/evaluate.rb", "lib/cgl/neighbors.rb", "lib/cgl/runner.rb", "test/test_evaluate.rb", "test/test_neighbors.rb", "test/test_runner.rb", "optimal-control.gemspec"]
|
18
|
+
s.homepage = %q{http://rubyforge.org/projects/optimal-control/}
|
19
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Optimal-control", "--main", "README"]
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.rubyforge_project = %q{optimal-control}
|
22
|
+
s.rubygems_version = %q{1.3.5}
|
23
|
+
s.summary = %q{Simulate Conway's game of Life on a small grid with periodic boundary conditions and find the optimal controller for the specified control objective and control options}
|
24
|
+
s.test_files = ["test/test_evaluate.rb", "test/test_neighbors.rb", "test/test_runner.rb"]
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 3
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
31
|
+
else
|
32
|
+
end
|
33
|
+
else
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
require 'lib/cgl/evaluate'
|
6
|
+
|
7
|
+
class TestEvaluate < Test::Unit::TestCase
|
8
|
+
context "alive agent" do
|
9
|
+
should "die as if caused by underpopulation" do
|
10
|
+
agent = Cgl::Evaluate.evaluate( ["1", "1"] )
|
11
|
+
assert_equal agent, 0
|
12
|
+
agent = Cgl::Evaluate.evaluate( ["1"] )
|
13
|
+
assert_equal agent, 0
|
14
|
+
end
|
15
|
+
should "die as if by overcrowding " do
|
16
|
+
agent = Cgl::Evaluate.evaluate( ["1", "1", "1", "1", "1"] )
|
17
|
+
assert_equal agent, 0
|
18
|
+
end
|
19
|
+
should "life on with 2 or 3 neighbors" do
|
20
|
+
agent = Cgl::Evaluate.evaluate( ["1", "1", "1"] )
|
21
|
+
assert_equal agent, 1
|
22
|
+
agent = Cgl::Evaluate.evaluate( ["1", "0", "0", "0", "1", "1"] )
|
23
|
+
assert_equal agent, 1
|
24
|
+
agent = Cgl::Evaluate.evaluate( ["1", "1", "1", "1"] )
|
25
|
+
assert_equal agent, 1
|
26
|
+
agent = Cgl::Evaluate.evaluate( ["1", "1", "0", "1", "1", "0", "0", "0",
|
27
|
+
"0"] )
|
28
|
+
assert_equal agent, 1
|
29
|
+
agent = Cgl::Evaluate.evaluate( ["1", "0", "0", "1", "1", "0", "0", "0",
|
30
|
+
"0"] )
|
31
|
+
assert_equal agent, 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context "dead agent" do
|
35
|
+
should "become a live cell with 3 neighbors" do
|
36
|
+
agent = Cgl::Evaluate.evaluate( ["0", "1", "1", "1"] )
|
37
|
+
assert_equal agent, 1
|
38
|
+
agent = Cgl::Evaluate.evaluate( ["0", "0", "0", "0","1", "1", "1","0", "0"] )
|
39
|
+
assert_equal agent, 1
|
40
|
+
agent = Cgl::Evaluate.evaluate( ["0", "1", "0", "0","1", "0", "1"] )
|
41
|
+
assert_equal agent, 1
|
42
|
+
agent = Cgl::Evaluate.evaluate( ["1", "1", "0", "1", "1", "0","0", "0",
|
43
|
+
"0"] )
|
44
|
+
assert_equal agent, 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
require 'lib/cgl/neighbors'
|
6
|
+
|
7
|
+
class TestNeighbors < Test::Unit::TestCase
|
8
|
+
context "agent in a 1 by 1 grid " do
|
9
|
+
should "have all 8 neighbors the same" do
|
10
|
+
neighbors = Cgl::Neighbors.neighbors(0, ["0"], 1)
|
11
|
+
assert_equal ["0", "0", "0", "0", "0", "0", "0", "0"], neighbors
|
12
|
+
neighbors = Cgl::Neighbors.neighbors(0, ["aa"], 1)
|
13
|
+
assert_equal ["aa", "aa", "aa", "aa", "aa", "aa", "aa", "aa"], neighbors
|
14
|
+
end
|
15
|
+
end
|
16
|
+
context "agent a in a 2 by 2 grid " do
|
17
|
+
should "have all 8 neighbors bdcdbdcdb" do
|
18
|
+
neighbors = Cgl::Neighbors.neighbors(0, ["a", "b", "c", "d"], 2)
|
19
|
+
assert_equal ["b", "d", "c", "d", "b", "d", "c", "d"], neighbors
|
20
|
+
end
|
21
|
+
end
|
22
|
+
context "central agent a in a 3 by 3 grid " do
|
23
|
+
should "have all normal 8 neighbors" do
|
24
|
+
neighbors = Cgl::Neighbors.neighbors(4, ["a", "b", "c", "d", "e", "f", "g", "h", "i"], 3)
|
25
|
+
assert_equal ["f", "i", "h", "g", "d", "a", "b", "c"], neighbors
|
26
|
+
end
|
27
|
+
end
|
28
|
+
context "upper right agent a in a 2 by 3 grid " do
|
29
|
+
should "have all normal 8 neighbors" do
|
30
|
+
neighbors = Cgl::Neighbors.neighbors(0, ["a", "b", "c", "d", "e", "f"],
|
31
|
+
2, 3)
|
32
|
+
assert_equal ["b", "e", "d", "f", "c", "f", "d", "e"], neighbors
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context "lower right corner on 3 by 3 grid" do
|
36
|
+
should "have the following neighbors" do
|
37
|
+
neighbors = Cgl::Neighbors.neighbors(8, ["a", "b", "c", "d", "e", "f", "g", "h", "i"], 3)
|
38
|
+
assert_equal ["g", "a", "c", "b", "h", "e", "f", "d"], neighbors
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
data/test/test_runner.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
require 'lib/cgl/runner'
|
6
|
+
|
7
|
+
class TestRunner < Test::Unit::TestCase
|
8
|
+
context "2 by 2 grid, 1 timestep " do
|
9
|
+
should "have these intial states with max cost" do
|
10
|
+
steps = 0
|
11
|
+
max_cost = (steps+1)*1 + 1
|
12
|
+
grid_size = 2
|
13
|
+
n_agents = grid_size**2
|
14
|
+
runner = Cgl::Runner.new(steps, max_cost, grid_size)
|
15
|
+
result = runner.run
|
16
|
+
assert_equal 2, result.size
|
17
|
+
cost_vector = result[0]
|
18
|
+
control = result[1]
|
19
|
+
assert_equal 2**n_agents, cost_vector.size
|
20
|
+
assert_equal 2**n_agents, control.size
|
21
|
+
for i in 0..2**n_agents-1
|
22
|
+
unless cost_vector[i] == max_cost
|
23
|
+
assert cost_vector[i]==control[i].size
|
24
|
+
end
|
25
|
+
#puts "%0#{n_agents}d" %i.to_s(2) + ": #{cost_vector[i]}"
|
26
|
+
end
|
27
|
+
precomputed_cost = "0000000000201000".split(//).collect{|x| x.to_i}
|
28
|
+
assert precomputed_cost == cost_vector,
|
29
|
+
precomputed_cost + cost_vector
|
30
|
+
assert ["", "", "", "", "", "", "", "", "", "", "", "", "1", "", "",
|
31
|
+
""] == control
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context "2 by 3 grid, 1 timestep " do
|
35
|
+
should "have these intial states with max cost" do
|
36
|
+
steps = 0
|
37
|
+
max_cost = (steps+1)*1 + 1
|
38
|
+
grid_hight = 2
|
39
|
+
grid_width = 3
|
40
|
+
n_agents = grid_width * grid_hight
|
41
|
+
runner = Cgl::Runner.new(steps, max_cost, grid_hight, grid_width)
|
42
|
+
result = runner.run
|
43
|
+
cost_vector = result[0]
|
44
|
+
control = result[1]
|
45
|
+
for i in 0..2**n_agents-1
|
46
|
+
unless cost_vector[i] == max_cost
|
47
|
+
assert cost_vector[i]==control[i].size
|
48
|
+
end
|
49
|
+
#puts "%0#{n_agents}d" %i.to_s(2) + ": #{cost_vector[i]}"
|
50
|
+
end
|
51
|
+
|
52
|
+
precomputed_cost =
|
53
|
+
"0000000002202000011010000000000002202000022020000220200010000000".split(//).collect{|x| x.to_i}
|
54
|
+
assert precomputed_cost == cost_vector,
|
55
|
+
precomputed_cost.to_s + "\n" + cost_vector.to_s
|
56
|
+
precomputed_control = ["", "", "", "", "", "", "", "", "", "", "", "",
|
57
|
+
"", "", "", "", "", "1", "1", "", "1", "", "", "", "", "", "", "", "",
|
58
|
+
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
59
|
+
"", "", "", "", "", "", "", "", "", "1", "", "", "", "", "", "", ""]
|
60
|
+
assert precomputed_control == control, "control is wrong"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
context "2 by 2 grid, 3 timestep " do
|
64
|
+
should "have these intial states with max cost" do
|
65
|
+
steps = 2
|
66
|
+
max_cost = (steps+1)*1 + 1
|
67
|
+
grid_size = 2
|
68
|
+
n_agents = grid_size**2
|
69
|
+
runner = Cgl::Runner.new(steps, max_cost, grid_size)
|
70
|
+
result = runner.run
|
71
|
+
assert_equal 2, result.size
|
72
|
+
cost_vector = result[0]
|
73
|
+
control = result[1]
|
74
|
+
assert_equal 2**n_agents, cost_vector.size
|
75
|
+
assert_equal 2**n_agents, control.size
|
76
|
+
for i in 0..2**n_agents-1
|
77
|
+
unless cost_vector[i] == max_cost
|
78
|
+
assert cost_vector[i]==control[i].size
|
79
|
+
end
|
80
|
+
end
|
81
|
+
precomputed_cost = "0000000000401000".split(//).collect{|x| x.to_i}
|
82
|
+
assert precomputed_cost == cost_vector,
|
83
|
+
precomputed_cost + cost_vector
|
84
|
+
assert ["", "", "", "", "", "", "", "", "", "", "", "", "1", "", "",
|
85
|
+
""] == control
|
86
|
+
end
|
87
|
+
end
|
88
|
+
context "3 by 3 grid, 1 timestep " do
|
89
|
+
should "have these intial states with max cost" do
|
90
|
+
steps = 0
|
91
|
+
max_cost = (steps+1)*1 + 1
|
92
|
+
grid_size = 3
|
93
|
+
n_agents = grid_size**2
|
94
|
+
runner = Cgl::Runner.new(steps, max_cost, grid_size)
|
95
|
+
result = runner.run
|
96
|
+
assert_equal 2, result.size
|
97
|
+
cost_vector = result[0]
|
98
|
+
control = result[1]
|
99
|
+
assert_equal 2**n_agents, cost_vector.size
|
100
|
+
assert_equal 2**n_agents, control.size
|
101
|
+
for i in 0..2**n_agents-1
|
102
|
+
unless cost_vector[i] == max_cost
|
103
|
+
assert cost_vector[i]==control[i].size
|
104
|
+
end
|
105
|
+
#puts "%0#{n_agents}d" %i.to_s(2) + ": #{cost_vector[i]} control in step #{control[i]}"
|
106
|
+
end
|
107
|
+
# some hand crafted test cases
|
108
|
+
assert cost_vector[335] == 0
|
109
|
+
assert cost_vector[432] == 2
|
110
|
+
assert cost_vector[448] == 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: optimal-control
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Franziska Hinkelmann
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-01 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: |-
|
17
|
+
Simulate Conway's game of Life on a small grid with periodic
|
18
|
+
boundary conditions and find the optimal controller for the specified
|
19
|
+
control objective and control options
|
20
|
+
email: fhinkel@vt.edu
|
21
|
+
executables:
|
22
|
+
- optimalcontrol
|
23
|
+
extensions: []
|
24
|
+
|
25
|
+
extra_rdoc_files:
|
26
|
+
- CHANGELOG
|
27
|
+
- README
|
28
|
+
- bin/optimalcontrol
|
29
|
+
- lib/cgl/evaluate.rb
|
30
|
+
- lib/cgl/neighbors.rb
|
31
|
+
- lib/cgl/runner.rb
|
32
|
+
files:
|
33
|
+
- CHANGELOG
|
34
|
+
- Manifest
|
35
|
+
- README
|
36
|
+
- Rakefile
|
37
|
+
- bin/optimalcontrol
|
38
|
+
- lib/cgl/evaluate.rb
|
39
|
+
- lib/cgl/neighbors.rb
|
40
|
+
- lib/cgl/runner.rb
|
41
|
+
- test/test_evaluate.rb
|
42
|
+
- test/test_neighbors.rb
|
43
|
+
- test/test_runner.rb
|
44
|
+
- optimal-control.gemspec
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://rubyforge.org/projects/optimal-control/
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --line-numbers
|
52
|
+
- --inline-source
|
53
|
+
- --title
|
54
|
+
- Optimal-control
|
55
|
+
- --main
|
56
|
+
- README
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "1.2"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project: optimal-control
|
74
|
+
rubygems_version: 1.3.5
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Simulate Conway's game of Life on a small grid with periodic boundary conditions and find the optimal controller for the specified control objective and control options
|
78
|
+
test_files:
|
79
|
+
- test/test_evaluate.rb
|
80
|
+
- test/test_neighbors.rb
|
81
|
+
- test/test_runner.rb
|