Skip to content

Commit

Permalink
Adding brute force optimization.
Browse files Browse the repository at this point in the history
Largely untested at this time.
  • Loading branch information
knaveofdiamonds committed Feb 20, 2012
1 parent 62fe2d6 commit 5197d08
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 1 deletion.
12 changes: 12 additions & 0 deletions lib/tealeaves.rb
@@ -1,5 +1,17 @@
require 'tealeaves/moving_average'
require 'tealeaves/seasonal_components'
require 'tealeaves/forecast'
require 'tealeaves/naive_forecast'
require 'tealeaves/single_exponential_smoothing_forecast'
require 'tealeaves/brute_force_optimization'
require 'tealeaves/exponential_smoothing_forecast'

module TeaLeaves
def self.optimal_model(time_series, period)
BruteForceOptimization.new(time_series, period).optimize
end

def self.forecast(time_series, period, periods_ahead=nil)
optimal_model(time_series, period).predict(periods_ahead)
end
end
84 changes: 84 additions & 0 deletions lib/tealeaves/brute_force_optimization.rb
@@ -0,0 +1,84 @@
module TeaLeaves
class BruteForceOptimization
INITIAL_PARAMETER_VALUES = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0].freeze

def initialize(time_series, period, opts={})
@time_series = time_series
@period = period
@opts = opts
end


def optimize
[0.1, 0.5, 0.25, 0.125, 0.0625, 0.03125, 0.015625].inject(optimum(initial_models)) do |model, change|
improve_model(model, change)
end
end

def initial_test_parameters(opts={})
parameters = []
INITIAL_PARAMETER_VALUES.each do |alpha|
parameters << {:alpha => alpha, :seasonality => :none, :trend => :none}

unless opts[:seasonality] == :none && opts[:trend] == :none
INITIAL_PARAMETER_VALUES.each do |b|
parameters << {:alpha => alpha, :beta => b, :seasonality => :none, :trend => :additive}
parameters << {:alpha => alpha, :beta => b, :seasonality => :none, :trend => :multiplicative}
parameters << {:alpha => alpha, :gamma => b, :trend => :none, :seasonality => :additive}
parameters << {:alpha => alpha, :gamma => b, :trend => :none, :seasonality => :multiplicative}

INITIAL_PARAMETER_VALUES.each do |gamma|
[:additive, :multiplicative].each do |trend|
[:additive, :multiplicative].each do |seasonality|
parameters << {
:alpha => alpha,
:beta => b,
:gamma => gamma,
:trend => trend,
:seasonality => seasonality
}
end
end
end
end
end
end

parameters
end

private

def improve_model(model, change)
trend_operations = model.trend == :none ? [nil] : [:+, :-, nil]
season_operations = model.seasonality == :none ? [nil] : [:+, :-, nil]
permutations = [:+, :-, nil].product(trend_operations, season_operations)
optimum(permutations.map do |(op_1,op_2,op_3)|
new_opts = {}
set_value(new_opts, :alpha, model, op_1, change)
set_value(new_opts, :beta, model, op_2, change)
set_value(new_opts, :gamma, model, op_3, change)
model.improve(new_opts)
end)
end

def set_value(hsh, key, model, op, change)
unless op.nil?
new_value = model.send(key).send(op, change)
if new_value >= 0.0 && new_value <= 1.0
hsh[key] = new_value
end
end
end

def optimum(models)
models.min_by(&:mean_squared_error)
end

def initial_models
initial_test_parameters.map do |parameters|
ExponentialSmoothingForecast.new(@time_series, @period, parameters)
end
end
end
end
9 changes: 8 additions & 1 deletion lib/tealeaves/exponential_smoothing_forecast.rb
Expand Up @@ -68,6 +68,8 @@ def apply(forecast, parameters, n)
end
end

attr_reader :alpha, :beta, :gamma, :trend, :seasonality

def initialize(time_series, period, opts={})
@time_series = time_series
@period = period
Expand All @@ -88,6 +90,11 @@ def initialize(time_series, period, opts={})
calculate_one_step_ahead_forecasts
end

def improve(opts)
new_opts = {:alpha => @alpha, :beta => @beta, :gamma => @gamma, :trend => @trend, :seasonality => @seasonality}.merge(opts)
self.class.new(@time_series, @period, new_opts)
end

attr_reader :model_parameters

def initial_level
Expand All @@ -114,7 +121,7 @@ def initial_parameters

def predict(n=nil)
if n.nil?
forecast(@model_parameters)
forecast(@model_parameters).first
else
(1..n).map {|i| forecast(@model_parameters, i).first }
end
Expand Down
11 changes: 11 additions & 0 deletions spec/brute_force_optimization_spec.rb
@@ -0,0 +1,11 @@
require 'spec_helper'

describe TeaLeaves::BruteForceOptimization do
it "should have 1014 initial test models" do
described_class.new([1,2,3,4], 1).initial_test_parameters.size.should == 1014
end

it "should produce an initial model" do

end
end

0 comments on commit 5197d08

Please sign in to comment.