minimization-0.2.5/0000755000004100000410000000000013413717302014256 5ustar www-datawww-dataminimization-0.2.5/Gemfile.lock0000644000004100000410000000125013413717302016476 0ustar www-datawww-dataPATH remote: . specs: minimization (0.2.5) text-table (~> 1.2) GEM remote: https://rubygems.org/ specs: diff-lcs (1.3) rake (10.5.0) rspec (3.8.0) rspec-core (~> 3.8.0) rspec-expectations (~> 3.8.0) rspec-mocks (~> 3.8.0) rspec-core (3.8.0) rspec-support (~> 3.8.0) rspec-expectations (3.8.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) text-table (1.2.4) PLATFORMS ruby DEPENDENCIES bundler (~> 1.3) minimization! rake (~> 10) rspec (~> 3.2) BUNDLED WITH 1.17.2 minimization-0.2.5/.travis.yml0000644000004100000410000000030113413717302016361 0ustar www-datawww-datalanguage: ruby rvm: - 2.0 - 2.1 - 2.2 - 2.5 - 2.6 script: bundle install && bundle exec rake spec before_install: - sudo apt-get update -qq - sudo apt-get install -y libgsl0-dev minimization-0.2.5/README.md0000644000004100000410000000133613413717302015540 0ustar www-datawww-data= minimization * http://github.com/clbustos/minimization == DESCRIPTION: Minimization algorithms on pure Ruby. == FEATURES/PROBLEMS: Unidimensional: * Newton-Rahpson (requires first and second derivative) * Golden Section * Brent (Port of GSL code) Multidimensional: * Fletcher-Reeves (requires first derivative) * Polak Rebirer (requires first derivative) * Nelder-Mead * Powell's method If you needs speed, use gem *gsl* == SYNOPSIS: d=Minimization::Brent.new(-1000,20000 , proc {|x| x**2}) d.iterate puts d.x_minimum puts d.f_minimum == REQUIREMENTS: * Pure Ruby == INSTALL: sudo gem install minimization == API: http://ruby-statsample.rubyforge.org/minimization/ == LICENSE: BSD 2-clause (See LICENSE.txt) minimization-0.2.5/spec/0000755000004100000410000000000013413717302015210 5ustar www-datawww-dataminimization-0.2.5/spec/minimization_unidimensional_spec.rb0000644000004100000410000000341113413717302024353 0ustar www-datawww-datarequire File.expand_path(File.dirname(__FILE__) + '/spec_helper') describe Minimization::Unidimensional, "subclass" do before(:all) do @p1=rand(100) @p2=rand(100) @func=lambda {|x| (x-@p1)**2+@p2} @funcd=lambda {|x| 2*(x-@p1)} @funcdd=lambda {|x| 2} end describe Minimization::NewtonRaphson do before do @min = Minimization::NewtonRaphson.new(-1000,1000, @func,@funcd, @funcdd) @min.iterate end it "#x_minimum be close to expected" do @min.x_minimum.should be_within(@min.epsilon).of(@p1) end it "#f_minimum ( f(x)) be close to expected" do @min.f_minimum.should be_within(@min.epsilon).of(@p2) end context "#log" do subject {@min.log} it {should be_instance_of Array} it {should respond_to :to_table} end end describe Minimization::GoldenSection do before do @min = Minimization::GoldenSection.minimize(-1000,1000, &@func) end it "#x_minimum be close to expected" do @min.x_minimum.should be_within(@min.epsilon).of(@p1) end it "#f_minimum ( f(x)) be close to expected" do @min.f_minimum.should be_within(@min.epsilon).of(@p2) end context "#log" do subject {@min.log} it {should be_instance_of Array} it {should respond_to :to_table} end end describe Minimization::Brent do before do @min = Minimization::Brent.minimize(-1000,1000, &@func) end it "should x be correct" do @min.x_minimum.should be_within(@min.epsilon).of(@p1) end it "should f(x) be correct" do @min.f_minimum.should be_within(@min.epsilon).of(@p2) end context "#log" do subject {@min.log} it {should be_instance_of Array} it {should respond_to :to_table} end end end minimization-0.2.5/spec/minimization_conjugate_gradient_fletcher_reeves_spec.rb0000644000004100000410000000421013413717302030414 0ustar www-datawww-datarequire "#{File.expand_path(File.dirname(__FILE__))}/../lib/multidim/conjugate_gradient.rb" describe Minimization::FletcherReeves do before :all do @n = 3 @limit = 100 @epsilon = 1e-6 @p = Array.new(@n) @start_point = Array.new(@n) 0.upto(@n - 1) do |i| @p[i] = rand(@limit) end 0.upto(@n - 1) do |i| @start_point[i] = rand(@limit) end # fletcher_reeves example 1 puts @p.inspect f = proc{ |x| (x[0] - @p[0])**2 + (x[1] - @p[1])**2 + (x[2] - @p[2])**2 } fd = proc{ |x| [ 2 * (x[0] - @p[0]) , 2 * (x[1] - @p[1]) , 2 * (x[2] - @p[2]) ] } @min1 = Minimization::FletcherReeves.minimize(f, fd, @start_point) # fletcher_reeves example 2 @k = rand(@limit) f2 = proc{ |x| ( @p[0]*x[0] + @p[1]*x[1] + @p[2]*x[2] )**2 + @k} fd2 = proc{ |x| r0 = ( @p[0]*x[0] + @p[1]*x[1] + @p[2]*x[2] ) * 2 * @p[0] r1 = ( @p[0]*x[0] + @p[1]*x[1] + @p[2]*x[2] ) * 2 * @p[1] r2 = ( @p[0]*x[0] + @p[1]*x[1] + @p[2]*x[2] ) * 2 * @p[2] [r0, r1, r2] } @min2 = Minimization::FletcherReeves.minimize(f2, fd2, @start_point) # fletcher_reeves example 3 : unidimensional f3 = proc{ |x| ( (x[0] - @p[0])**2 + @k ) } fd3 = proc{ |x| [ (x[0] - @p[0]) * 2 ] } starting_point_3 = [rand(@limit)] @min3 = Minimization::FletcherReeves.minimize(f3, fd3, starting_point_3) end it "#x_minimum be close to expected in example 1 :fletcher_reeves" do 0.upto(@n - 1) do |i| expect(@min1.x_minimum[i]).to be_within(@epsilon).of(@p[i]) end end it "#f_minimum be close to expected in example 1 :fletcher_reeves" do expect(@min1.f_minimum).to be_within(@epsilon).of(0) end it "#f_minimum be close to expected in example 2 :fletcher_reeves" do expect(@min2.f_minimum).to be_within(@epsilon).of(@k) end it "#x_minimum be close to expected in example 3 :fletcher_reeves" do expect(@min3.x_minimum[0]).to be_within(@epsilon).of(@p[0]) end it "#f_minimum be close to expected in example 3 :fletcher_reeves" do expect(@min3.f_minimum).to be_within(@epsilon).of(@k) end end minimization-0.2.5/spec/spec.opts0000644000004100000410000000001413413717302017044 0ustar www-datawww-data--color -f sminimization-0.2.5/spec/minimization_conjugate_gradient_polak_ribiere_spec.rb0000644000004100000410000000300113413717302030053 0ustar www-datawww-datarequire "#{File.expand_path(File.dirname(__FILE__))}/../lib/multidim/conjugate_gradient.rb" describe Minimization::PolakRibiere do before :all do @n = 3 @limit = 100 @epsilon = 1e-6 @p = Array.new(@n) @start_point = Array.new(@n) 0.upto(@n - 1) do |i| @p[i] = rand(@limit) end 0.upto(@n - 1) do |i| @start_point[i] = rand(@limit) end # example 1 f = proc{ |x| (x[0] - @p[0])**2 + (x[1] - @p[1])**2 + (x[2] - @p[2])**2 } fd = proc{ |x| [ 2 * (x[0] - @p[0]) , 2 * (x[1] - @p[1]) , 2 * (x[2] - @p[2]) ] } @min1 = Minimization::PolakRibiere.minimize(f, fd, @start_point) # example 2 : unidimensional @k = rand(@limit) f2 = proc{ |x| ( (x[0] - @p[0])**2 + @k ) } fd2 = proc{ |x| [ (x[0] - @p[0]) * 2 ] } starting_point_2 = [rand(@limit)] @min2 = Minimization::PolakRibiere.minimize(f2, fd2, starting_point_2) end it "#x_minimum be close to expected in example 1 :polak_ribiere" do 0.upto(@n - 1) do |i| expect(@min1.x_minimum[i]).to be_within(@epsilon).of(@p[i]) end end it "#f_minimum be close to expected in example 1 :polak_ribiere" do expect(@min1.f_minimum).to be_within(@epsilon).of(0) end it "#x_minimum be close to expected in example 2 :polak_ribiere" do expect(@min2.x_minimum[0]).to be_within(@epsilon).of(@p[0]) end it "#f_minimum be close to expected in example 2 :polak_ribiere" do expect(@min2.f_minimum).to be_within(@epsilon).of(@k) end end minimization-0.2.5/spec/spec_helper.rb0000644000004100000410000000050713413717302020030 0ustar www-datawww-data$LOAD_PATH.unshift(File.dirname(__FILE__)) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) require 'minimization.rb' require 'rspec' RSpec.configure do |config| config.expect_with :rspec do |c| c.syntax = [:should, :expect] end end class String def deindent gsub /^[ \t]*/, '' end end minimization-0.2.5/spec/minimization_powell_spec_spec.rb0000644000004100000410000000323113413717302023651 0ustar www-datawww-datarequire "#{File.expand_path(File.dirname(__FILE__))}/../lib/multidim/powell.rb" describe Minimization::Powell do before :all do @n = 3 @limit = 100 @epsilon = 1e-5 @p = Array.new(@n) @start_point = Array.new(@n) 0.upto(@n - 1) do |i| @p[i] = rand(@limit) end 0.upto(@n - 1) do |i| @start_point[i] = rand(@limit) end # example 1 f = proc{ |x| (x[0] - @p[0])**2 + (x[1] - @p[1])**2 + (x[2] - @p[2])**2 } @min1 = Minimization::Powell.minimize(f, @start_point, [-@limit, -@limit, -@limit], [@limit, @limit, @limit]) # example 2 @k = rand(@limit) f2 = proc{ |x| ( @p[0]*x[0] + @p[1]*x[1] + @p[2]*x[2] )**2 + @k} @min2 = Minimization::Powell.minimize(f2, @start_point, [-@limit, -@limit, -@limit], [@limit, @limit, @limit]) # example 3 : unidimensional f3 = proc{ |x| (x[0] - @p[0])**2 + @k} @min3 = Minimization::Powell.minimize(f3, @start_point, [-@limit, -@limit, -@limit], [@limit, @limit, @limit]) end it "#x_minimum be close to expected in example 1" do 0.upto(@n - 1) do |i| expect(@min1.x_minimum[i]).to be_within(@epsilon).of(@p[i]) end end it "#f_minimum be close to expected in example 1" do expect(@min1.f_minimum).to be_within(@epsilon).of(0) end it "#f_minimum be close to expected in example 2" do expect(@min2.f_minimum).to be_within(@epsilon).of(@k) end it "#x_minimum be close to expected in example 3" do expect(@min3.x_minimum[0]).to be_within(@epsilon).of(@p[0]) end it "#f_minimum be close to expected in example 3" do expect(@min3.f_minimum).to be_within(@epsilon).of(@k) end end minimization-0.2.5/spec/minimization_nelder_mead_spec.rb0000644000004100000410000000300113413717302023567 0ustar www-datawww-datarequire "#{File.expand_path(File.dirname(__FILE__))}/../lib/multidim/nelder_mead.rb" describe Minimization::NelderMead do before :all do @n = 3 @limit = 100 @epsilon = 1e-6 @p = Array.new(@n) @start_point = Array.new(@n) 0.upto(@n - 1) do |i| @p[i] = rand(@limit) end 0.upto(@n - 1) do |i| @start_point[i] = rand(@limit) end # example 1 f = proc{ |x| (x[0] - @p[0])**2 + (x[1] - @p[1])**2 + (x[2] - @p[2])**2 } @min1 = Minimization::NelderMead.minimize(f, @start_point) # example 2 @k = rand(@limit) f2 = proc{ |x| ( @p[0]*x[0] + @p[1]*x[1] + @p[2]*x[2] )**2 + @k} @min2 = Minimization::NelderMead.minimize(f2, @start_point) # example 3 : unidimensional f3 = proc{ |x| (x[0] - @p[0])**2 + @k} @min3 = Minimization::NelderMead.minimize(f3, [@k]) end it "#x_minimum be close to expected in example 1" do 0.upto(@n - 1) do |i| expect(@min1.x_minimum[i]).to be_within(@epsilon).of(@p[i]) end end it "#f_minimum be close to expected in example 1" do expect(@min1.f_minimum).to be_within(@epsilon).of(0) end it "#f_minimum be close to expected in example 2" do expect(@min2.f_minimum).to be_within(@epsilon).of(@k) end it "#x_minimum be close to expected in example 3" do expect(@min3.x_minimum[0]).to be_within(@epsilon).of(@p[0]) end it "#f_minimum be close to expected in example 3" do expect(@min3.f_minimum).to be_within(@epsilon).of(@k) end end minimization-0.2.5/.gitignore0000644000004100000410000000003013413717302016237 0ustar www-datawww-dataGemfile.lock doc pkg *~ minimization-0.2.5/History.txt0000644000004100000410000000117713413717302016466 0ustar www-datawww-data=== 0.2.5 / 2018-12-29 * Fixed gem in RubyGems * Updated travis test === 0.2.4 / 2015-07-20 * Merge from clbustos repository === 0.2.3 / 2015-04-27 * Removed rb-gsl dependency. === 0.2.2 / 2015-04-03 * Added Travis-CI support. * Removed Hoe in favor of using a gemspec. === 0.2.1 / 2010-11-14 * Added iterations method. === 0.2.0 / 2010-04-15 * New Minimization::NewtonRahpson class, which implements a naive Newton-Rahpson minimization method `x = x_n - (f'(x) / f''(x))`. === 0.1.1 / 2010-03-19 * New Minimization#minimize convenience method. === 0.1.0 / 2010-02-24 * Golden Section and Brent Algorithm. minimization-0.2.5/Rakefile0000644000004100000410000000113413413717302015722 0ustar www-datawww-datarequire 'rake' require 'bundler' require 'bundler/gem_tasks' require "rspec/core/rake_task" require 'rdoc/task' # Setup the necessary gems, specified in the gemspec. begin Bundler.setup(:default, :development) rescue Bundler::BundlerError => e $stderr.puts e.message $stderr.puts "Run `bundle install` to install missing gems" exit e.status_code end RSpec::Core::RakeTask.new(:spec) do |spec| spec.pattern = FileList['spec/**/*_spec.rb'] end desc "Open an irb session preloaded with distribution" task :console do sh "irb -rubygems -I lib -r minimization.rb" end task :default => [:spec] minimization-0.2.5/lib/0000755000004100000410000000000013413717302015024 5ustar www-datawww-dataminimization-0.2.5/lib/minimization.rb0000644000004100000410000003054013413717302020062 0ustar www-datawww-data# = minimization.rb - # Minimization- Minimization algorithms on pure Ruby # Copyright (C) 2010-2018 Claudio Bustos # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # Algorithms for unidimensional minimization # importing the multidimensional minimization classes require_relative 'multidim/brent_root_finder' require_relative 'multidim/conjugate_gradient' require_relative 'multidim/nelder_mead' require_relative 'multidim/point_value_pair' require_relative 'multidim/powell' require 'text-table' module Minimization FailedIteration=Class.new(Exception) # Base class for unidimensional minimizers class Unidimensional # Default value for error on f(x) EPSILON=1e-6 # Default number of maximum iterations MAX_ITERATIONS=100 # Minimum value for x attr_reader :x_minimum # Minimum value for f(x) attr_reader :f_minimum # Log of iterations. Should be an array attr_reader :log # Name of fields of log attr_reader :log_header # Absolute error on x attr_accessor :epsilon # Expected value. Fast minimum finding if set attr_reader :expected # Numbers of iterations attr_reader :iterations # Create a new minimizer def initialize(lower, upper, proc) raise "first argument should be lower than second" if lower>=upper @lower=lower @upper=upper @proc=proc golden = 0.3819660; @expected = @lower + golden * (@upper - @lower); @max_iteration=MAX_ITERATIONS @epsilon=EPSILON @iterations=0 @log=[] @log_header=%w{I xl xh f(xl) f(xh) dx df(x)} end # Set expected value def expected=(v) @expected=v end def log_summary @log.join("\n") end # Convenience method to minimize # == Parameters: # * lower: Lower possible value # * upper: Higher possible value # * expected: Optional expected value. Faster the search is near correct value. # * &block: Block with function to minimize # == Usage: # minimizer=Minimization::GoldenSection.minimize(-1000, 1000) {|x| # x**2 } # def self.minimize(lower,upper,expected=nil,&block) minimizer=new(lower,upper,block) minimizer.expected=expected unless expected.nil? raise FailedIteration unless minimizer.iterate minimizer end # Iterate to find the minimum def iterate raise "You should implement this" end def f(x) @proc.call(x) end end # Classic Newton-Raphson minimization method. # Requires first and second derivative # == Usage # f = lambda {|x| x**2} # fd = lambda {|x| 2x} # fdd = lambda {|x| 2} # min = Minimization::NewtonRaphson.new(-1000,1000, f,fd,fdd) # min.iterate # min.x_minimum # min.f_minimum # class NewtonRaphson < Unidimensional # == Parameters: # * lower: Lower possible value # * upper: Higher possible value # * proc: Original function # * proc_1d: First derivative # * proc_2d: Second derivative # def initialize(lower, upper, proc, proc_1d, proc_2d) super(lower,upper,proc) @proc_1d=proc_1d @proc_2d=proc_2d end # Raises an error def self.minimize(*args) raise "You should use #new and #iterate" end def iterate # First x_prev=@lower x=@expected failed=true k=0 while (x-x_prev).abs > @epsilon and k<@max_iteration k+=1 x_prev=x x=x-(@proc_1d.call(x).quo(@proc_2d.call(x))) f_prev=f(x_prev) f=f(x) x_min,x_max=[x,x_prev].min, [x,x_prev].max f_min,f_max=[f,f_prev].min, [f,f_prev].max @log << [k, x_min, x_max, f_min, f_max, (x_prev-x).abs, (f-f_prev).abs] end raise FailedIteration, "Not converged" if k>=@max_iteration @x_minimum = x; @f_minimum = f(x); end end # = Golden Section Minimizer. # Basic minimization algorithm. Slow, but robust. # See Unidimensional for methods. # == Usage. # require 'minimization' # min=Minimization::GoldenSection.new(-1000,20000 , proc {|x| (x+1)**2} # min.expected=1.5 # Expected value # min.iterate # min.x_minimum # min.f_minimum # min.log class GoldenSection < Unidimensional # Start the iteration def iterate ax=@lower bx=@expected cx=@upper c = (3-Math::sqrt(5)).quo(2); r = 1-c; x0 = ax; x3 = cx; if ((cx-bx).abs > (bx-ax).abs) x1 = bx; x2 = bx + c*(cx-bx); else x2 = bx; x1 = bx - c*(bx-ax); end f1 = f(x1); f2 = f(x2); k = 1; while (x3-x0).abs > @epsilon and k<@max_iteration if f2 < f1 x0 = x1; x1 = x2; x2 = r*x1 + c*x3; # x2 = x1+c*(x3-x1) f1 = f2; f2 = f(x2); else x3 = x2; x2 = x1; x1 = r*x2 + c*x0; # x1 = x2+c*(x0-x2) f2 = f1; f1 = f(x1); end @log << [k, x3,x0, f1,f2,(x3-x0).abs, (f1-f2).abs] k +=1; end if f1 < f2 @x_minimum = x1; @f_minimum = f1; else @x_minimum = x2; @f_minimum = f2; end true end end # Direct port of Brent algorithm found on GSL. # See Unidimensional for methods. # == Usage # min=Minimization::Brent.new(-1000,20000 , proc {|x| (x+1)**2} # min.expected=1.5 # Expected value # min.iterate # min.x_minimum # min.f_minimum # min.log class Brent < Unidimensional GSL_SQRT_DBL_EPSILON=1.4901161193847656e-08 def initialize(lower,upper, proc) super @do_bracketing=true # Init golden = 0.3819660; #golden = (3 - sqrt(5))/2 v = @lower + golden * (@upper - @lower); w = v; @x_minimum = v ; @f_minimum = f(v) ; @x_lower=@lower @x_upper=@upper @f_lower = f(@lower) ; @f_upper = f(@lower) ; @v = v; @w = w; @d = 0; @e = 0; @f_v=f(v) @f_w=@f_v end def expected=(v) @x_minimum=v @f_minimum=f(v) @do_bracketing=false end def bracketing eval_max=10 f_left = @f_lower; f_right = @f_upper; x_left = @x_lower; x_right= @x_upper; golden = 0.3819660; # golden = (3 - sqrt(5))/2 */ nb_eval=0 if (f_right >= f_left) x_center = (x_right - x_left) * golden + x_left; nb_eval+=1; f_center=f(x_center) else x_center = x_right ; f_center = f_right ; x_right = (x_center - x_left).quo(golden) + x_left; nb_eval+=1; f_right=f(x_right); end begin @log << ["B#{nb_eval}", x_left, x_right, f_left, f_right, (x_left-x_right).abs, (f_left-f_right).abs] if (f_center < f_left ) if (f_center < f_right) @x_lower = x_left; @x_upper = x_right; @x_minimum = x_center; @f_lower = f_left; @f_upper = f_right; @f_minimum = f_center; return true; elsif (f_center > f_right) x_left = x_center; f_left = f_center; x_center = x_right; f_center = f_right; x_right = (x_center - x_left).quo(golden) + x_left; nb_eval+=1; f_right=f(x_right); else # f_center == f_right */ x_right = x_center; f_right = f_center; x_center = (x_right - x_left).quo(golden) + x_left; nb_eval+=1; f_center=f(x_center); end else # f_center >= f_left */ x_right = x_center; f_right = f_center; x_center = (x_right - x_left) * golden + x_left; nb_eval+=1; f_center=f(x_center); end end while ((nb_eval < eval_max) and ((x_right - x_left) > GSL_SQRT_DBL_EPSILON * ( (x_right + x_left) * 0.5 ) + GSL_SQRT_DBL_EPSILON)) @x_lower = x_left; @x_upper = x_right; @x_minimum = x_center; @f_lower = f_left; @f_upper = f_right; @f_minimum = f_center; return false; end # Start the minimization process # If you want to control manually the process, use brent_iterate def iterate k=0 bracketing if @do_bracketing while k<@max_iteration and (@x_lower-@x_upper).abs>@epsilon k+=1 result=brent_iterate raise FailedIteration,"Error on iteration" if !result begin @log << [k, @x_lower, @x_upper, @f_lower, @f_upper, (@x_lower-@x_upper).abs, (@f_lower-@f_upper).abs] rescue =>@e @log << [k, @e.to_s,nil,nil,nil,nil,nil] end end @iterations=k return true end # Generate one iteration. def brent_iterate x_left = @x_lower; x_right = @x_upper; z = @x_minimum; d = @e; e = @d; v = @v; w = @w; f_v = @f_v; f_w = @f_w; f_z = @f_minimum; golden = 0.3819660; # golden = (3 - sqrt(5))/2 */ w_lower = (z - x_left) w_upper = (x_right - z) tolerance = GSL_SQRT_DBL_EPSILON * z.abs midpoint = 0.5 * (x_left + x_right) _p,q,r=0,0,0 if (e.abs > tolerance) # fit parabola */ r = (z - w) * (f_z - f_v); q = (z - v) * (f_z - f_w); _p = (z - v) * q - (z - w) * r; q = 2 * (q - r); if (q > 0) _p = -_p else q = -q; end r = e; e = d; end if (_p.abs < (0.5 * q * r).abs and _p < q * w_lower and _p < q * w_upper) t2 = 2 * tolerance ; d = _p.quo(q); u = z + d; if ((u - x_left) < t2 or (x_right - u) < t2) d = (z < midpoint) ? tolerance : -tolerance ; end else e = (z < midpoint) ? x_right - z : -(z - x_left) ; d = golden * e; end if ( d.abs >= tolerance) u = z + d; else u = z + ((d > 0) ? tolerance : -tolerance) ; end @e = e; @d = d; f_u=f(u) if (f_u <= f_z) if (u < z) @x_upper = z; @f_upper = f_z; else @x_lower = z; @f_lower = f_z; end @v = w; @f_v = f_w; @w = z; @f_w = f_z; @x_minimum = u; @f_minimum = f_u; return true; else if (u < z) @x_lower = u; @f_lower = f_u; return true; else @x_upper = u; @f_upper = f_u; return true; end if (f_u <= f_w or w == z) @v = w; @f_v = f_w; @w = u; @f_w = f_u; return true; elsif f_u <= f_v or v == z or v == w @v = u; @f_v = f_u; return true; end end return false end end # = Bisection Method for Minimization. # See Unidimensional for methods. # == Usage. # require 'minimization' # min=Minimization::Bisection.new(1,2 , proc {|x| (x)**3-(x)-2} # min.iterate # min.x_minimum # min.f_minimum # min.log # Source: # * R.L. Burden, J. Faires: Numerical Analysis class Bisection < Unidimensional def iterate() ax = @lower cx = @upper k = 0; while (ax-cx).abs > @epsilon and k<@max_iteration bx = (ax + cx).quo(2); fa = f(ax); fb = f(bx); fc = f(cx); if (fa*fb <0) cx = bx; else (fb*fc <0) ax = bx; end k +=1; @log << [k, ax.to_f, cx.to_f, f(ax).to_f, f(cx).to_f, (ax-cx).abs.to_f, (f(ax)-f(cx)).abs.to_f] end if (falower_bound: Lower bound of the minimization search # * upper_bound: Upper bound of the minimization search # * f: Function to find roots # def find_root(lower_bound, upper_bound, f) @f = f lower = lower_bound f_upper = f(lower_bound) upper = upper_bound f_lower = f(upper_bound) c = lower fc = f_upper d = upper - lower e = d absolute_accuracy = EPSILON relative_accuracy = EPSILON loop do @iterations += 1 if (fc.abs < f_lower.abs) lower = upper upper = c c = lower f_upper = f_lower f_lower = fc fc = f_upper end tolerance = 2 * relative_accuracy * upper.abs + absolute_accuracy m = 0.5 * (c - upper) if (m.abs <= tolerance or f_lower.abs < EPSILON or @iterations > @max_iterations) return upper end if (e.abs < tolerance or f_upper.abs <= f_lower.abs) # use bisection d = m e = d else # use inverse cubic interpolation s = f_lower / f_upper if (lower == c) p = 2 * m * s q = 1 - s else q = f_upper / fc r = f_lower / fc p = s * (2 * m * q * (q - r) - (upper - lower) * (r - 1)) q = (q - 1) * (r - 1) * (s - 1) end if (p > 0) q = -q else p = -p end s = e e = d if (p >= 1.5 * m * q - (tolerance * q).abs or p >= (0.5 * s * q).abs) # interpolation failed, fall back to bisection d = m e = d else d = p / q end end # Update the best estimate of the root and bounds on each iteration lower = upper f_upper = f_lower if (d.abs > tolerance) upper += d elsif (m > 0) upper += tolerance else upper -= tolerance end f_lower = f(upper) if ((f_lower > 0 and fc > 0) or (f_lower <= 0 and fc <= 0)) c = lower fc = f_upper d = upper - lower e = d end end end end end minimization-0.2.5/lib/multidim/nelder_mead.rb0000644000004100000410000002413213413717302021436 0ustar www-datawww-data# = nelder_mead.rb - # Minimization- Minimization algorithms on pure Ruby # Copyright (C) 2010 Claudio Bustos # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # This algorith was adopted and ported into Ruby from Apache-commons # Math library's NelderMead.java file. Therefore this file is under # Apache License Version 2. # # Nelder Mead Algorithm for Multidimensional minimization require "#{File.expand_path(File.dirname(__FILE__))}/point_value_pair.rb" module Minimization class DirectSearchMinimizer EPSILON_DEFAULT = 1e-6 MAX_ITERATIONS_DEFAULT = 1000000 attr_reader :x_minimum attr_reader :f_minimum attr_reader :epsilon def initialize(f, start_point, iterate_simplex_ref) @epsilon = EPSILON_DEFAULT # Default number of maximum iterations @max_iterations = MAX_ITERATIONS_DEFAULT # proc which iterates the simplex @iterate_simplex_ref = iterate_simplex_ref @relative_threshold = 100 * @epsilon @absolute_threshold = @epsilon @x_minimum = nil @f_minimum = nil @f = f # create and initializ start configurations if @start_configuration == nil # sets the start configuration point as unit self.start_configuration = Array.new(start_point.length) { 1.0 } end @iterations = 0 @evaluations = 0 # create the simplex for the first time build_simplex(start_point) evaluate_simplex end def f(x) return @f.call(x) end def iterate_simplex return iterate_simplex_ref.call end # increment iteration counter by 1 def increment_iterations_counter @iterations += 1 raise "iteration limit reached" if @iterations > @max_iterations end # compares 2 PointValuePair points def compare(v1, v2) if v1.value == v2.value return 0 elsif v1.value > v2.value return 1 else return -1 end end # checks whether the function is converging def converging? # check the convergence in a given direction comparing the previous and current values def point_converged?(previous, current) pre = previous.value curr = current.value diff = (pre - curr).abs size = [pre.abs, curr.abs].max return !((diff <= (size * @relative_threshold)) and (diff <= @absolute_threshold)) end # returns true if converging is possible atleast in one direction if @iterations > 0 # given direction is converged converged = true 0.upto(@simplex.length - 1) do |i| converged &= !point_converged?(@previous[i], @simplex[i]) end return !converged end # if no iterations were done, convergence undefined return true end # only the relative position of the n vertices with respect # to the first one are stored def start_configuration=(steps) n = steps.length @start_configuration = Array.new(n) { Array.new(n, 0) } 0.upto(n - 1) do |i| vertex_i = @start_configuration[i] 0.upto(i) do |j| raise "equals vertices #{j} and #{j+1} in simplex configuration" if steps[j] == 0.0 0.upto(j) do |k| vertex_i[k] = steps[k] end end end end # Build an initial simplex # == Parameters: # * start_point: starting point of the minimization search # def build_simplex(start_point) n = start_point.length raise "dimension mismatch" if n != @start_configuration.length # set first vertex @simplex = Array.new(n+1) @simplex[0] = PointValuePair.new(start_point, Float::NAN) # set remaining vertices 0.upto(n - 1) do |i| conf_i = @start_configuration[i] vertex_i = Array.new(n) 0.upto(n - 1) do |k| vertex_i[k] = start_point[k] + conf_i[k] end @simplex[i + 1] = PointValuePair.new(vertex_i, Float::NAN) end end # Evaluate all the non-evaluated points of the simplex def evaluate_simplex # evaluate the objective function at all non-evaluated simplex points 0.upto(@simplex.length - 1) do |i| vertex = @simplex[i] point = vertex.point if vertex.value.nan? @simplex[i] = PointValuePair.new(point, f(point)) end end # sort the simplex from best to worst @simplex.sort!{ |x1, x2| x1.value <=> x2.value } end # Replace the worst point of the simplex by a new point # == Parameters: # * point_value_pair: point to insert # def replace_worst_point(point_value_pair) n = @simplex.length - 1 0.upto(n - 1) do |i| if (compare(@simplex[i], point_value_pair) > 0) point_value_pair, @simplex[i] = @simplex[i], point_value_pair end end @simplex[n] = point_value_pair end # Convenience method to minimize # == Parameters: # * start_point: Starting points # * f: Function to minimize # == Usage: # minimizer=Minimization::NelderMead.minimize(proc{|x| (x[0] - 1) ** 2 + (x[1] - 5) ** 2}, [0, 0]) # def self.minimize(f, start_point) min=Minimization::NelderMead.new(f, start_point) while min.converging? min.iterate end return min end # Iterate the simplex one step. Use this when iteration needs to be done manually # == Usage: # minimizer=Minimization::NelderMead.new(proc{|x| (x[0] - 1) ** 2 + (x[1] - 5) ** 2}, [0, 0]) # while minimizer.converging? # minimizer.Iterate # end # minimizer.x_minimum # minimizer.f_minimum # def iterate # set previous simplex as the current simplex @previous = Array.new(@simplex.length) 0.upto(@simplex.length - 1) do |i| point = @simplex[i].point # clone require? @previous[i] = PointValuePair.new(point, f(point)) end # iterate simplex iterate_simplex # set results @x_minimum = @simplex[0].point @f_minimum = @simplex[0].value end end # = Nelder Mead Minimizer. # A multidimensional minimization methods. # == Usage. # require 'minimization' # min=Minimization::NelderMead.new(proc {|x| (x[0] - 2)**2 + (x[1] - 5)**2}, [1, 2]) # while min.converging? # min.iterate # end # min.x_minimum # min.f_minimum # class NelderMead < DirectSearchMinimizer def initialize(f, start_point) # Reflection coefficient @rho = 1.0 # Expansion coefficient @khi = 2.0 # Contraction coefficient @gamma = 0.5 # Shrinkage coefficient @sigma = 0.5 super(f, start_point, proc{iterate_simplex}) end def iterate_simplex increment_iterations_counter n = @simplex.length - 1 # the simplex has n+1 point if dimension is n best = @simplex[0] secondBest = @simplex[n - 1] worst = @simplex[n] x_worst = worst.point centroid = Array.new(n, 0) # compute the centroid of the best vertices # (dismissing the worst point at index n) 0.upto(n - 1) do |i| x = @simplex[i].point 0.upto(n - 1) do |j| centroid[j] += x[j] end end scaling = 1.0 / n 0.upto(n - 1) do |j| centroid[j] *= scaling end xr = Array.new(n) # compute the reflection point 0.upto(n - 1) do |j| xr[j] = centroid[j] + @rho * (centroid[j] - x_worst[j]) end reflected = PointValuePair.new(xr, f(xr)) if ((compare(best, reflected) <= 0) && (compare(reflected, secondBest) < 0)) # accept the reflected point replace_worst_point(reflected) elsif (compare(reflected, best) < 0) xe = Array.new(n) # compute the expansion point 0.upto(n - 1) do |j| xe[j] = centroid[j] + @khi * (xr[j] - centroid[j]) end expanded = PointValuePair.new(xe, f(xe)) if (compare(expanded, reflected) < 0) # accept the expansion point replace_worst_point(expanded) else # accept the reflected point replace_worst_point(reflected) end else if (compare(reflected, worst) < 0) xc = Array.new(n) # perform an outside contraction 0.upto(n - 1) do |j| xc[j] = centroid[j] + @gamma * (xr[j] - centroid[j]) end out_contracted = PointValuePair.new(xc, f(xc)) if (compare(out_contracted, reflected) <= 0) # accept the contraction point replace_worst_point(out_contracted) return end else xc = Array.new(n) # perform an inside contraction 0.upto(n - 1) do |j| xc[j] = centroid[j] - @gamma * (centroid[j] - x_worst[j]) end in_contracted = PointValuePair.new(xc, f(xc)) if (compare(in_contracted, worst) < 0) # accept the contraction point replace_worst_point(in_contracted) return end end # perform a shrink x_smallest = @simplex[0].point 0.upto(@simplex.length - 1) do |i| x = @simplex[i].get_point_clone 0.upto(n - 1) do |j| x[j] = x_smallest[j] + @sigma * (x[j] - x_smallest[j]) end @simplex[i] = PointValuePair.new(x, Float::NAN) end evaluate_simplex end end end end minimization-0.2.5/lib/multidim/powell.rb0000644000004100000410000002432313413717302020503 0ustar www-datawww-data# = powell.rb - # Minimization- Minimization algorithms on pure Ruby # Copyright (C) 2010 Claudio Bustos # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # This algorith was adopted and ported into Ruby from Apache-commons # Math library's PowellOptimizer.java and # BaseAbstractMultivariateVectorOptimizer.java # files. Therefore this file is under Apache License Version 2. # # Powell's Algorithm for Multidimensional minimization require "#{File.expand_path(File.dirname(__FILE__))}/point_value_pair.rb" require "#{File.expand_path(File.dirname(__FILE__))}/../minimization.rb" module Minimization class ConjugateDirectionMinimizer attr_accessor :max_iterations attr_accessor :max_brent_iterations attr_accessor :x_minimum attr_accessor :f_minimum # default maximum Powell's iteration value Max_Iterations_Default = 100 # default Brent iteration value MAX_BRENT_ITERATION_DEFAULT = 10 # give a suitable value def initialize(f, initial_guess, lower_bound, upper_bound) @iterations = 0 @max_iterations = Max_Iterations_Default @evaluations = 0 @max_brent_iterations = MAX_BRENT_ITERATION_DEFAULT @converging = true # set minimizing function @f = f @start = initial_guess @lower_bound = lower_bound @upper_bound = upper_bound # set maximum and minimum coordinate value a point can have # while minimization process @min_coordinate_val = lower_bound.min @max_coordinate_val = upper_bound.max # validate input parameters check_parameters end # return the convergence of the search def converging? return @converging end # set minimization function def f(x) @f.call(x) end # validate input parameters def check_parameters if (!@start.nil?) dim = @start.length if (!@lower_bound.nil?) # check for dimension mismatches raise "dimension mismatching #{@lower_bound.length} and #{dim}" if @lower_bound.length != dim # check whether start point exeeds the lower bound 0.upto(dim - 1) do |i| v = @start[i] lo = @lower_bound[i] raise "start point is lower than lower bound" if v < lo end end if (!@upper_bound.nil?) # check for dimension mismatches raise "dimension mismatching #{@upper_bound.length} and #{dim}" if @upper_bound.length != dim # check whether strating point exceeds the upper bound 0.upto(dim - 1) do |i| v = @start[i] hi = @upper_bound[i] raise "start point is higher than the upper bound" if v > hi end end if (@lower_bound.nil?) @lower_bound = Array.new(dim) 0.upto(dim - 1) do |i| @lower_bound[i] = Float::INFINITY # eventually this will occur an error end end if (@upper_bound.nil?) @upper_bound = Array.new(dim) 0.upto(dim - 1) do |i| @upper_bound[i] = -Float::INFINITY # eventually this will occur an error end end end end # line minimization using Brent's minimization # == Parameters: # * point: Starting point # * direction: Search direction # def brent_search(point, direction) n = point.length # Create a proc to minimize using brent search # Function value varies with alpha value and represent a point # of the minimizing function which is on the given plane func = proc{ |alpha| x = Array.new(n) 0.upto(n - 1) do |i| # create a point according to the given alpha value x[i] = point[i] + alpha * direction[i] end # return the function value of the obtained point f(x) } # create Brent minimizer line_minimizer = Minimization::Brent.new(@min_coordinate_val, @max_coordinate_val, func) # iterate Brent minimizer for given number of iteration value 0.upto(@max_brent_iterations) do line_minimizer.iterate end # return the minimum point return {:alpha_min => line_minimizer.x_minimum, :f_val => line_minimizer.f_minimum} end end # = Powell's Minimizer. # A multidimensional minimization methods # == Usage. # require 'minimization' # f = proc{ |x| (x[0] - 1)**2 + (2*x[1] - 5)**2 + (x[2]-3.3)**2} # min = Minimization::Powell.minimize(f, [1, 2, 3], [0, 0, 0], [5, 5, 5]) # min.f_minimum # min.x_minimum # class Powell < ConjugateDirectionMinimizer attr_accessor :relative_threshold attr_accessor :absolute_threshold # default of relative threshold RELATIVE_THRESHOLD_DEFAULT = 0.1 # default of absolute threshold ABSOLUTE_THRESHOLD_DEFAULT =0.1 # == Parameters: # * f: Minimization function # * initial_guess: Initial position of Minimization # * lower_bound: Lower bound of the minimization # * upper_bound: Upper bound of the minimization # def initialize(f, initial_guess, lower_bound, upper_bound) super(f, initial_guess.clone, lower_bound, upper_bound) @relative_threshold = RELATIVE_THRESHOLD_DEFAULT @absolute_threshold = ABSOLUTE_THRESHOLD_DEFAULT end # Obtain new point and direction from the previous point, # previous direction and a parameter value # == Parameters: # * point: Previous point # * direction: Previous direction # * minimum: parameter value # def new_point_and_direction(point, direction, minimum) n = point.length new_point = Array.new(n) new_dir = Array.new(n) 0.upto(n - 1) do |i| new_dir[i] = direction[i] * minimum new_point[i] = point[i] + new_dir[i] end return {:point => new_point, :dir => new_dir} end # Iterate Powell's minimizer one step # == Parameters: # * f: Function to minimize # * starting_point: starting point # * lower_bound: Lowest possible values of each direction # * upper_bound: Highest possible values of each direction # == Usage: # minimizer = Minimization::Powell.new(proc{|x| (x[0] - 1)**2 + (x[1] -1)**2}, # [0, 0, 0], [-5, -5, -5], [5, 5, 5]) # while minimizer.converging? # minimizer.iterate # end # minimizer.x_minimum # minimizer.f_minimum # def iterate @iterations += 1 # set initial configurations if(@iterations <= 1) guess = @start @n = guess.length # initialize all to 0 @direc = Array.new(@n) { Array.new(@n) {0} } 0.upto(@n - 1) do |i| # set diagonal values to 1 @direc[i][i] = 1 end @x = guess @f_val = f(@x) @x1 = @x.clone end fx = @f_val fx2 = 0 delta = 0 big_ind = 0 alpha_min = 0 0.upto(@n - 1) do |i| direction = @direc[i].clone fx2 = @f_val # Find line minimum minimum = brent_search(@x, direction) @f_val = minimum[:f_val] alpha_min = minimum[:alpha_min] # Obtain new point and direction new_pnd = new_point_and_direction(@x, direction, alpha_min) new_point = new_pnd[:point] new_dir = new_pnd[:dir] @x = new_point if ((fx2 - @f_val) > delta) delta = fx2 - @f_val big_ind = i end end # convergence check @converging = !(2 * (fx - @f_val) <= (@relative_threshold * (fx.abs + @f_val.abs) + @absolute_threshold)) # storing results if((@f_val < fx)) @x_minimum = @x @f_minimum = @f_val else @x_minimum = @x1 @f_minimum = fx end direction = Array.new(@n) x2 = Array.new(@n) 0.upto(@n -1) do |i| direction[i] = @x[i] - @x1[i] x2[i] = 2 * @x[i] - @x1[i] end @x1 = @x.clone fx2 = f(x2) if (fx > fx2) t = 2 * (fx + fx2 - 2 * @f_val) temp = fx - @f_val - delta t *= temp * temp temp = fx - fx2 t -= delta * temp * temp if (t < 0.0) minimum = brent_search(@x, direction) @f_val = minimum[:f_val] alpha_min = minimum[:alpha_min] # Obtain new point and direction new_pnd = new_point_and_direction(@x, direction, alpha_min) new_point = new_pnd[:point] new_dir = new_pnd[:dir] @x = new_point last_ind = @n - 1 @direc[big_ind] = @direc[last_ind] @direc[last_ind] = new_dir end end end # Convenience method to minimize # == Parameters: # * f: Function to minimize # * starting_point: starting point # * lower_bound: Lowest possible values of each direction # * upper_bound: Highest possible values of each direction # == Usage: # minimizer = Minimization::Powell.minimize(proc{|x| (x[0] - 1)**2 + (x[1] -1)**2}, # [0, 0, 0], [-5, -5, -5], [5, 5, 5]) # minimizer.x_minimum # minimizer.f_minimum # def self.minimize(f, starting_point, lower_bound, upper_bound) min = Minimization::Powell.new(f, starting_point, lower_bound, upper_bound) while min.converging? min.iterate end return min end end end minimization-0.2.5/lib/multidim/conjugate_gradient.rb0000644000004100000410000002176713413717302023046 0ustar www-datawww-data# = conjugate_gradient.rb - # Minimization- Minimization algorithms on pure Ruby # Copyright (C) 2010 Claudio Bustos # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # This algorith was adopted and ported into Ruby from Apache-commons # Math library's NonLinearConjugateGradientOptimizer.java file. Therefore this file is under # Apache License Version 2. # # Conjugate Gradient Algorithms for Multidimensional minimization require "#{File.expand_path(File.dirname(__FILE__))}/point_value_pair.rb" require "#{File.expand_path(File.dirname(__FILE__))}/../minimization.rb" require "#{File.expand_path(File.dirname(__FILE__))}/brent_root_finder.rb" module Minimization # Conjugate Gradient minimizer class # The beta function may be :fletcher_reeves or :polak_ribiere class NonLinearConjugateGradientMinimizer attr_reader :x_minimum attr_reader :f_minimum attr_reader :converging attr_accessor :initial_step MAX_ITERATIONS_DEFAULT = 100000 EPSILON_DEFAULT = 1e-6 alias :converging? :converging def initialize(f, fd, start_point, beta_formula) @epsilon = EPSILON_DEFAULT @safe_min = 4.503599e15 @f = f @fd = fd @start_point = start_point @max_iterations = MAX_ITERATIONS_DEFAULT @iterations = 0 @update_formula = beta_formula @relative_threshold = 100 * @epsilon @absolute_threshold = 100 * @safe_min @initial_step = 1.0 # initial step default @converging = true # do initial steps @point = @start_point.clone @n = @point.length @r = gradient(@point) 0.upto(@n - 1) do |i| @r[i] = -@r[i] end # Initial search direction. @steepest_descent = precondition(@point, @r) @search_direction = @steepest_descent.clone @delta = 0 0.upto(@n - 1) do |i| @delta += @r[i] * @search_direction[i] end @current = nil end def f(x) return @f.call(x) end def gradient(x) return @fd.call(x) end def find_upper_bound(a, h, search_direction) ya = line_search_func(a, search_direction).to_f yb = ya step = h # check step value for float max value exceeds while step < Float::MAX b = a + step yb = line_search_func(b, search_direction).to_f if (ya * yb <= 0) return b end step *= [2, ya / yb].max end # raise error if bracketing failed raise "Unable to bracket minimum in line search." end def precondition(point, r) return r.clone # case: identity preconditioner has been used as the default end def converged(previous, current) p = f(previous) c = f(current) difference = (p - c).abs size = [p.abs, c.abs].max return ((difference <= size * @relative_threshold) or (difference <= @absolute_threshold)) end # solver to use during line search def solve(min, max, start_value, search_direction) # check start_value to eliminate unnessasary calculations ... func = proc{|x| line_search_func(x, search_direction)} root_finder = Minimization::BrentRootFinder.new(func) root = root_finder.find_root(min, max, func) return root end def line_search_func(x, search_direction) # current point in the search direction shifted_point = @point.clone 0.upto(shifted_point.length - 1) do |i| shifted_point[i] += x * search_direction[i] end # gradient of the objective function gradient = gradient(shifted_point) # dot product with the search direction dot_product = 0 0.upto(gradient.length - 1) do |i| dot_product += gradient[i] * search_direction[i] end return dot_product end # iterate one step of conjugate gradient minimizer # == Usage: # f = proc{ |x| (x[0] - 2)**2 + (x[1] - 5)**2 + (x[2] - 100)**2 } # fd = proc{ |x| [ 2 * (x[0] - 2) , 2 * (x[1] - 5) , 2 * (x[2] - 100) ] } # min = Minimization::FletcherReeves.new(f, fd, [0, 0, 0]) # while(min.converging?) # min.iterate # end # min.x_minimum # min.f_minimum # def iterate @iterations += 1 @previous = @current @current = Minimization::PointValuePair.new(@point, f(@point)) # set converging parameter @converging = !(@previous != nil and converged(@previous.point, @current.point)) # set results @x_minimum = @current.point @f_minimum = @current.value # set search_direction to be used in solve and find_upper_bound methods ub = find_upper_bound(0, @initial_step, @search_direction) step = solve(0, ub, 1e-15, @search_direction) # Validate new point 0.upto(@point.length - 1) do |i| @point[i] += step * @search_direction[i] end @r = gradient(@point) 0.upto(@n - 1) do |i| @r[i] = -@r[i] end # Compute beta delta_old = @delta new_steepest_descent = precondition(@point, @r) @delta = 0 0.upto(@n - 1) do |i| @delta += @r[i] * new_steepest_descent[i] end if (@update_formula == :fletcher_reeves) beta = @delta.to_f / delta_old elsif(@update_formula == :polak_ribiere) deltaMid = 0 0.upto(@r.length - 1) do |i| deltaMid += @r[i] * @steepest_descent[i] end beta = (@delta - deltaMid).to_f / delta_old else raise "Unknown beta formula type" end @steepest_descent = new_steepest_descent # Compute conjugate search direction if ((@iterations % @n == 0) or (beta < 0)) # Break conjugation: reset search direction @search_direction = @steepest_descent.clone else # Compute new conjugate search direction 0.upto(@n - 1) do |i| @search_direction[i] = @steepest_descent[i] + beta * @search_direction[i] end end end end # = Conjugate Gradient Fletcher Reeves minimizer. # A multidimensional minimization methods. # == Usage. # require 'minimization' # f = proc{ |x| (x[0] - 2)**2 + (x[1] - 5)**2 + (x[2] - 100)**2 } # fd = proc{ |x| [ 2 * (x[0] - 2) , 2 * (x[1] - 5) , 2 * (x[2] - 100) ] } # min = Minimization::FletcherReeves.minimize(f, fd, [0, 0, 0]) # min.x_minimum # min.f_minimum # class FletcherReeves < NonLinearConjugateGradientMinimizer def initialize(f, fd, start_point) super(f, fd, start_point, :fletcher_reeves) end # Convenience method to minimize using Fletcher Reeves method # == Parameters: # * f: Function to minimize # * fd: First derivative of f # * start_point: Starting point # == Usage: # f = proc{ |x| (x[0] - 2)**2 + (x[1] - 5)**2 + (x[2] - 100)**2 } # fd = proc{ |x| [ 2 * (x[0] - 2) , 2 * (x[1] - 5) , 2 * (x[2] - 100) ] } # min = Minimization::FletcherReeves.minimize(f, fd, [0, 0, 0]) # def self.minimize(f, fd, start_point) min = Minimization::FletcherReeves.new(f, fd, start_point) while(min.converging?) min.iterate end return min end end # = Conjugate Gradient Polak Ribbiere minimizer. # A multidimensional minimization methods. # == Usage. # require 'minimization' # f = proc{ |x| (x[0] - 2)**2 + (x[1] - 5)**2 + (x[2] - 100)**2 } # fd = proc{ |x| [ 2 * (x[0] - 2) , 2 * (x[1] - 5) , 2 * (x[2] - 100) ] } # min = Minimization::PolakRibiere.minimize(f, fd, [0, 0, 0]) # min.x_minimum # min.f_minimum # class PolakRibiere < NonLinearConjugateGradientMinimizer def initialize(f, fd, start_point) super(f, fd, start_point, :polak_ribiere) end # Convenience method to minimize using Polak Ribiere method # == Parameters: # * f: Function to minimize # * fd: First derivative of f # * start_point: Starting point # == Usage: # f = proc{ |x| (x[0] - 2)**2 + (x[1] - 5)**2 + (x[2] - 100)**2 } # fd = proc{ |x| [ 2 * (x[0] - 2) , 2 * (x[1] - 5) , 2 * (x[2] - 100) ] } # min = Minimization::PolakRibiere.minimize(f, fd, [0, 0, 0]) # def self.minimize(f, fd, start_point) min = Minimization::PolakRibiere.new(f, fd, start_point) while(min.converging?) min.iterate end return min end end end minimization-0.2.5/lib/multidim/point_value_pair.rb0000644000004100000410000000074313413717302022541 0ustar www-datawww-datamodule Minimization # class which holds the point,value pair class PointValuePair attr_reader :value attr_accessor :value attr_reader :point # == Parameters: # * point: Coordinates of the point # * value: Function value at the point # def initialize(point, value) @point = point.clone @value = value end # returns a copy of the point def get_point_clone return @point.clone end end end minimization-0.2.5/Gemfile0000644000004100000410000000004713413717302015552 0ustar www-datawww-datasource "https://rubygems.org" gemspec minimization-0.2.5/Manifest.txt0000644000004100000410000000071613413717302016571 0ustar www-datawww-dataHistory.txt LICENSE.txt Manifest.txt README.txt Rakefile lib/minimization.rb spec/minimization_unidimensional_spec.rb spec/spec.opts spec/spec_helper.rb spec/minimization_conjugate_gradient_fletcher_reeves.rb spec/minimization_conjugate_gradient_polak_ribiere.rb spec/minimization_nelder_mead.rb spec/minimization_powell_spec.rb multidim/brent_root_finder.rb multidim/conjugate_gradient.rb multidim/nelder_mead.rb multidim/point_value_pair.rb multidim/powell.rb minimization-0.2.5/LICENSE.txt0000644000004100000410000000242213413717302016101 0ustar www-datawww-dataCopyright (c) 2010-2018, Claudio Bustos All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. minimization-0.2.5/minimization.gemspec0000644000004100000410000000164713413717302020342 0ustar www-datawww-data$LOAD_PATH.unshift File.expand_path('../lib', __FILE__) require 'date' require 'minimization/version' Gem::Specification.new do |s| s.name = "minimization" s.version = Minimization::VERSION s.date = Date.today.to_s s.authors = ["Claudio Bustos", "Rajat Kapoor"] s.email = ["clbustos@gmail.com", "rajat100493@gmail.com"] s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.homepage = "https://github.com/sciruby/minimization" s.description = "Minimization algorithms on pure Ruby" s.summary = "A suite for minimization in Ruby" s.add_runtime_dependency 'text-table', '~> 1.2' s.add_development_dependency 'rake', '~> 10' s.add_development_dependency 'bundler', '~> 1.3' s.add_development_dependency 'rspec', '~> 3.2' end