simulator 0.1.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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'guard-cucumber'
7
+ gem 'guard-rspec'
8
+ gem 'ruby_gntp'
9
+ gem 'pry'
10
+ end
11
+ group :test do
12
+ gem "rspec"
13
+ gem "rspec-expectations", "~>2.12.1"
14
+ gem "cucumber"
15
+ end
16
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jamie Ly
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Simulator
2
+
3
+ Use to easily create models and view results across periods.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'simulator'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install simulator
18
+
19
+ ## Usage
20
+
21
+ See the features for some examples.
22
+
23
+ bundle exec cucumber
24
+
25
+ See the specs for some simpler examples.
26
+
27
+ bundle exec rspec
28
+
29
+ See the `examples` subdirectory for examples as well. Including the one
30
+ excerpted below:
31
+
32
+ ```ruby
33
+ # We create a model that simulates a ball drop
34
+ model = Simulator::Model.new do
35
+ name = "Ball drop model"
36
+ # create a couple static variables to represent acceleration with
37
+ # default values
38
+ var :ax, 0
39
+ var :ay, - 9.8
40
+
41
+ # create dynamic variables bound to some computation, with default
42
+ # values.
43
+
44
+ # velocity is affected by acceleration
45
+ eqtn(:vx, 20) { vx + ax }
46
+ eqtn(:vy, 50) { vy + ay }
47
+
48
+ # position is affected by velocity
49
+ eqtn(:x, 10) { x + vx }
50
+ eqtn(:y, 100) { y + vy }
51
+ end
52
+
53
+ # Run the model 10 periods
54
+ model_run = model.new_run
55
+ 10.times do
56
+ model_run.step
57
+ end
58
+
59
+ # retrieve the data
60
+ series = model_run.data.series :x, :y
61
+ puts series
62
+ # > [[30, 50, 70, 90, 110, 130, 150, 170, 190, 210, 210],
63
+ # > [140.2, 170.6, 191.2, 202.0, 203.0, 194.2, 175.6, 147.2, 108.99999999999999, 60.999999999999986, 60.999999999999986]]
64
+
65
+ # Then plot it or do whatever you like
66
+ ```
67
+
data/RELEASE.md ADDED
@@ -0,0 +1,31 @@
1
+ v0.1.2
2
+ ======
3
+ * Added detail to gemspec
4
+
5
+ v0.1.1
6
+ ======
7
+ * Added mortgage example
8
+
9
+ v0.1.0
10
+ ======
11
+ * First minor release
12
+ * Removes `puts` in `Model.rb`
13
+ * Alters beer_spec to use new var default syntax
14
+
15
+ v0.0.5
16
+ ======
17
+ * Added "bounce" example
18
+ * Added arg to pass default value of variable with eqtn call
19
+
20
+ v0.0.4
21
+ ======
22
+
23
+ v0.0.3
24
+ ======
25
+ * Added block parameter to Model instantiation to more easily create
26
+ variables and equations.
27
+ * Added "interest" spec to test a simple interest model
28
+ * Added "beer" spec for a more complex model example
29
+ * Added `delay` function which allows equations to interact with past periods
30
+
31
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gem 'simulator', path: '../..'
4
+ gem 'chunky_png'
5
+
Binary file
@@ -0,0 +1,70 @@
1
+ require 'json'
2
+ require 'chunky_png'
3
+ require 'simulator'
4
+
5
+ module Drop
6
+ class Program
7
+ attr_accessor :model, :steps_per_second
8
+
9
+ def initialize
10
+ steps_per_second = 3.0
11
+ @steps_per_second = steps_per_second
12
+ @model = ::Simulator::Model.new do
13
+ name = "Drop model"
14
+ var :ax, 0
15
+ var :ay, - 9.8/steps_per_second
16
+ eqtn(:vx, 20) { vx + ax }
17
+ eqtn(:vy, 50) { vy + ay }
18
+ eqtn(:x, 10) { x + vx }
19
+ eqtn(:y, 100) { y + vy }
20
+ end
21
+ @height = 500
22
+ @width = 700
23
+ end
24
+
25
+ def model_data
26
+ # Run the model 30 steps
27
+ model_run = @model.new_run
28
+ 30.times do
29
+ model_run.step
30
+ end
31
+
32
+ # get the data
33
+ model_run.data
34
+ end
35
+
36
+ def norm_data(data)
37
+ xs, ys = data.series :x, :y
38
+
39
+ # normalize
40
+ xs = xs.collect(&:round)
41
+ # Flip the y coordinates so we can draw
42
+ ys = ys.collect {|y| @height - y.round}
43
+
44
+ xs.zip(ys)
45
+ end
46
+
47
+ def plot_data(filename, pts)
48
+ # display the data in an image
49
+ image = ChunkyPNG::Image.new @width, @height,
50
+ ChunkyPNG::Color::BLACK
51
+ pts.each do |pt|
52
+ x, y = pt
53
+ image.circle x, y, 3, ChunkyPNG::Color('red')
54
+ end
55
+ image.save filename
56
+ end
57
+
58
+
59
+ def run
60
+ data = model_data
61
+ pts = norm_data data
62
+ plot_data 'drop.png', pts
63
+ end
64
+ end
65
+ end
66
+
67
+
68
+ # Run the program
69
+ Drop::Program.new.run
70
+
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gem 'simulator', path: '../..'
4
+ gem 'chunky_png'
5
+
Binary file
@@ -0,0 +1,91 @@
1
+ require 'simulator'
2
+ require 'chunky_png'
3
+
4
+ module Mortgage
5
+ class Program
6
+ include Simulator
7
+ def model
8
+ @model = Model.new do
9
+ name = "Mortgage model"
10
+
11
+ # monthly steps
12
+ var :base_rate, 0.08
13
+ eqtn(:annual_rate) { base_rate + rand(10).to_f/1000 }
14
+ eqtn(:monthly_rate) { annual_rate / 12.0 }
15
+ var :payment, 2000
16
+ eqtn :balance, 250000 do
17
+ balance * (1 + monthly_rate) - payment
18
+ end
19
+ end
20
+ end
21
+
22
+ def model_data
23
+ # fixed rate mortgage
24
+ fixed = @model.new_run
25
+ fixed.set payment: 2100
26
+ (30 * 12).times { fixed.step }
27
+
28
+ # balloon
29
+ balloon = @model.new_run
30
+ balloon.set payment: 1850
31
+ (30 * 12).times { balloon.step }
32
+
33
+ variable = @model.new_run
34
+ # first 10 years, stick with low payment
35
+ variable.set payment: 1800
36
+ (10 * 12).times do
37
+ variable.step
38
+ end
39
+
40
+ # subsequent years, balloon to higher payment until its paid
41
+ variable.set payment: 2100
42
+ (20 * 12).times {variable.step}
43
+ @balances = {
44
+ variable: variable.data.series(:balance),
45
+ fixed: fixed.data.series(:balance),
46
+ balloon: balloon.data.series(:balance)
47
+ }
48
+
49
+ @balances
50
+ end
51
+ def plot
52
+ @height = 500
53
+ @width = 800
54
+
55
+ # display the data in an image
56
+ image = ChunkyPNG::Image.new @width, @height,
57
+ ChunkyPNG::Color::BLACK
58
+
59
+ peg = @balances[:variable]
60
+ count = peg.length
61
+ max = peg.max.to_f
62
+ colors = {
63
+ variable: ChunkyPNG::Color('blue'),
64
+ fixed: ChunkyPNG::Color('yellow'),
65
+ balloon: ChunkyPNG::Color('red')
66
+ }
67
+ @balances.keys.each do |type|
68
+ balances = @balances[type]
69
+ color = colors[type]
70
+
71
+ balances.each_index do |i|
72
+ x = i/count.to_f * @width
73
+ y = @height - (balances[i]/max * @height).to_i
74
+ image.circle x, y, 3, color
75
+ end
76
+ end
77
+
78
+ image.save @filename
79
+ end
80
+ def run
81
+ model
82
+ model_data
83
+ @filename = "mortgage.png"
84
+ plot
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ Mortgage::Program.new.run
91
+
@@ -0,0 +1,17 @@
1
+ Feature: equations
2
+ In order to create simulations
3
+ As a simulation author
4
+ I want to be able to create equations which model the behavior of a system
5
+
6
+ Scenario: adding equations to scenarios
7
+ Given a model
8
+ When I add a new equation
9
+ Then it should be accessible in the list of equations
10
+
11
+ Scenario: creating an equation based on the pythagorean equation
12
+ Given a variable context
13
+ And a value 3 bound to x
14
+ And a value 4 bound to z
15
+ When I create a new equation "Math.sqrt(x^2 + z^2)"
16
+ Then I get a value result 5
17
+
@@ -0,0 +1,22 @@
1
+ Feature: Kinematics estimation
2
+ In order to determine the path of a ball
3
+ As someone studying physics
4
+ I want to be able model kinematics equations
5
+
6
+ Scenario: dropping a ball from rest
7
+ Given a model
8
+ And variable "velocity_y"
9
+ And variable "acceleration_y"
10
+ And variable "displacement_y"
11
+ And variable "t"
12
+ And equation "acceleration_y * t" bound to velocity_y
13
+ And equation "0.5 * acceleration_y * t**2" bound to displacement_y
14
+ And equation "t + 1" bound to t
15
+ And a new run of the model
16
+ And the value 0 bound to displacement_y
17
+ And the value 1 bound to t
18
+ And the value -9.8 bound to acceleration_y
19
+ When I step the run for 10 periods
20
+ Then the value -490 should be bound to displacement_y
21
+ And the value -98 should be bound to velocity_y
22
+
@@ -0,0 +1,17 @@
1
+ Feature: savings simulation
2
+ In order to simulate the outcome of savings rates across a number of periods
3
+ As a simulation runner
4
+ I want the simulation to output the outcome of the decisions made
5
+
6
+ Scenario: constant savings rate
7
+ Given a savings model
8
+ And variable "savings_rate"
9
+ And variable "savings"
10
+ And equation "savings * (1+savings_rate)" bound to savings
11
+ And a new run of the model
12
+ And the value 100 bound to savings
13
+ And the value 0.05 bound to savings_rate
14
+ When I step the run for 10 periods
15
+ Then the value 162.89 should be bound to savings
16
+
17
+
@@ -0,0 +1,52 @@
1
+ require 'simulator'
2
+ include Simulator
3
+
4
+ Given /^a variable context$/ do
5
+ @context = VariableContext.new
6
+ end
7
+
8
+ Given /^a value (\d+) bound to (\w+)$/ do |value, name|
9
+ @var = Variable.new name.to_sym
10
+ @context.add_variables @var
11
+ @context.set name.to_sym => value.to_i
12
+ end
13
+
14
+ When /^I create a new equation "(.*?)"$/ do |arg1|
15
+ @pythagorean_eqtn = Equation.new @var do
16
+ Math.sqrt( x*x + z*z )
17
+ end
18
+ end
19
+
20
+ Given /^variables with values x=(\d+), and y=(\d+)$/ do |x_value, y_value|
21
+ @context = VariableContext.new
22
+ @context.add x: 3, z: 4
23
+
24
+ # set the values of the variables in this context
25
+ @context.set x: x_value.to_i, z: y_value.to_i
26
+ end
27
+
28
+ Then /^I get a value result (\d+)$/ do |equation_result|
29
+ # the context evaluates an equation in its own context
30
+ @pythagorean_eqtn.evaluate_in(@context).should eq equation_result.to_i
31
+ end
32
+
33
+ Then /^a bound variable "(.*?)" with value (\d+)$/ do |var_name, value|
34
+ @context.get(var_name.to_sym).value.should eq value.to_i
35
+ end
36
+
37
+ Given /^a model$/ do
38
+ @model = Model.new
39
+ end
40
+
41
+ When /^I add a new equation$/ do
42
+ @new_equation = Equation.new nil do
43
+ 1 + 1
44
+ end
45
+ @model.add_equation @new_equation
46
+ end
47
+
48
+ Then /^it should be accessible in the list of equations$/ do
49
+ @model.equations.member?(@new_equation).should be true
50
+ end
51
+
52
+