nlopt 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
+ SHA256:
3
+ metadata.gz: 4700baee481eb9311b975cfd317df2f242f8e38f126b36fd44f14a34e9bee18b
4
+ data.tar.gz: 8b11358fa772f186d6b562e85b23e8c8c7879d3d208b0cd7f4b2cf007481f867
5
+ SHA512:
6
+ metadata.gz: 73da0c81d6aafc93c9e21c0ae9474f112bdb9923a9dd08d176ac24a3b191047ca902f011cc37015395d7b2159237c9895d6cea9a869bd00c092263e9259d3204
7
+ data.tar.gz: 57c37e5d06d5e5ebe37c70e70e8744225ecb2c5b7732c77a8c9b19c76d2f0d064f7bc4bd5146c4f1c46017f2f597583e1854a2695152cd9cc8bb1fdf630e7c44
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 (2024-12-14)
2
+
3
+ - First release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2007-2024 Massachusetts Institute of Technology
2
+ Copyright (c) 2024 Andrew Kane
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # NLopt Ruby
2
+
3
+ [NLopt](https://github.com/stevengj/nlopt) - nonlinear optimization - for Ruby
4
+
5
+ [![Build Status](https://github.com/ankane/nlopt-ruby/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/nlopt-ruby/actions)
6
+
7
+ ## Installation
8
+
9
+ First, install NLopt. For Homebrew, use:
10
+
11
+ ```sh
12
+ brew install nlopt
13
+ ```
14
+
15
+ And for Ubuntu, use:
16
+
17
+ ```sh
18
+ sudo apt-get install libnlopt0
19
+ ```
20
+
21
+ Then add this line to your application’s Gemfile:
22
+
23
+ ```ruby
24
+ gem "nlopt"
25
+ ```
26
+
27
+ ## Getting Started
28
+
29
+ Create an optimization
30
+
31
+ ```ruby
32
+ opt = NLopt::Opt.new("LN_COBYLA", 2)
33
+ ```
34
+
35
+ Set the objective function
36
+
37
+ ```ruby
38
+ f = lambda do |x, grad|
39
+ x[0] + x[1]
40
+ end
41
+ opt.set_min_objective(f)
42
+ ```
43
+
44
+ Set constraints
45
+
46
+ ```ruby
47
+ opt.set_lower_bounds([1, 2])
48
+ opt.set_upper_bounds([3, 4])
49
+ ```
50
+
51
+ Perform the optimization
52
+
53
+ ```ruby
54
+ xopt = opt.optimize([2, 3])
55
+ ```
56
+
57
+ ## History
58
+
59
+ View the [changelog](https://github.com/ankane/nlopt-ruby/blob/master/CHANGELOG.md)
60
+
61
+ ## Contributing
62
+
63
+ Everyone is encouraged to help improve this project. Here are a few ways you can help:
64
+
65
+ - [Report bugs](https://github.com/ankane/nlopt-ruby/issues)
66
+ - Fix bugs and [submit pull requests](https://github.com/ankane/nlopt-ruby/pulls)
67
+ - Write, clarify, or fix documentation
68
+ - Suggest or add new features
69
+
70
+ To get started with development:
71
+
72
+ ```sh
73
+ git clone https://github.com/ankane/nlopt-ruby.git
74
+ cd nlopt-ruby
75
+ bundle install
76
+ bundle exec rake test
77
+ ```
data/lib/nlopt/ffi.rb ADDED
@@ -0,0 +1,56 @@
1
+ module NLopt
2
+ module FFI
3
+ extend Fiddle::Importer
4
+
5
+ libs = Array(NLopt.ffi_lib).dup
6
+ begin
7
+ dlload Fiddle.dlopen(libs.shift)
8
+ rescue Fiddle::DLError => e
9
+ retry if libs.any?
10
+ raise e
11
+ end
12
+
13
+ # https://github.com/stevengj/nlopt/blob/master/src/api/nlopt.h
14
+
15
+ typealias "nlopt_algorithm", "int"
16
+ typealias "nlopt_func", "void *"
17
+ typealias "nlopt_opt", "void *"
18
+ typealias "nlopt_result", "int"
19
+ typealias "unsigned", "unsigned int"
20
+
21
+ extern "const char * nlopt_algorithm_name(nlopt_algorithm a)"
22
+ extern "const char * nlopt_algorithm_to_string(nlopt_algorithm algorithm)"
23
+ extern "nlopt_algorithm nlopt_algorithm_from_string(const char *name)"
24
+
25
+ extern "const char * nlopt_result_to_string(nlopt_result algorithm)"
26
+
27
+ extern "void nlopt_version(int *major, int *minor, int *bugfix)"
28
+
29
+ extern "nlopt_opt nlopt_create(nlopt_algorithm algorithm, unsigned n)"
30
+ extern "void nlopt_destroy(nlopt_opt opt)"
31
+ extern "nlopt_opt nlopt_copy(const nlopt_opt opt)"
32
+
33
+ extern "nlopt_result nlopt_optimize(nlopt_opt opt, double *x, double *opt_f)"
34
+
35
+ extern "nlopt_result nlopt_set_min_objective(nlopt_opt opt, nlopt_func f, void *f_data)"
36
+ extern "nlopt_result nlopt_set_max_objective(nlopt_opt opt, nlopt_func f, void *f_data)"
37
+
38
+ extern "nlopt_algorithm nlopt_get_algorithm(const nlopt_opt opt)"
39
+ extern "unsigned nlopt_get_dimension(const nlopt_opt opt)"
40
+ extern "const char * nlopt_get_errmsg(nlopt_opt opt)"
41
+
42
+ extern "nlopt_result nlopt_set_lower_bounds(nlopt_opt opt, const double *lb)"
43
+ extern "nlopt_result nlopt_set_lower_bounds1(nlopt_opt opt, double lb)"
44
+ extern "nlopt_result nlopt_set_lower_bound(nlopt_opt opt, int i, double lb)"
45
+ extern "nlopt_result nlopt_get_lower_bounds(const nlopt_opt opt, double *lb)"
46
+ extern "nlopt_result nlopt_set_upper_bounds(nlopt_opt opt, const double *ub)"
47
+ extern "nlopt_result nlopt_set_upper_bounds1(nlopt_opt opt, double ub)"
48
+ extern "nlopt_result nlopt_set_upper_bound(nlopt_opt opt, int i, double ub)"
49
+ extern "nlopt_result nlopt_get_upper_bounds(const nlopt_opt opt, double *ub)"
50
+
51
+ extern "nlopt_result nlopt_set_maxeval(nlopt_opt opt, int maxeval)"
52
+ extern "int nlopt_get_maxeval(const nlopt_opt opt)"
53
+
54
+ extern "int nlopt_get_numevals(const nlopt_opt opt)"
55
+ end
56
+ end
@@ -0,0 +1,35 @@
1
+ module NLopt
2
+ class Gradient
3
+ def initialize(ptr, n)
4
+ @ptr = ptr
5
+ @n = n
6
+ end
7
+
8
+ def [](index)
9
+ check_index(index)
10
+ @ptr[index * Fiddle::SIZEOF_DOUBLE, Fiddle::SIZEOF_DOUBLE].unpack1("d")
11
+ end
12
+
13
+ def []=(index, value)
14
+ check_index(index)
15
+ @ptr[index * Fiddle::SIZEOF_DOUBLE, Fiddle::SIZEOF_DOUBLE] = [value].pack("d")
16
+ end
17
+
18
+ def size
19
+ @n
20
+ end
21
+
22
+ def inspect
23
+ "#<#{self.class.name} #{@n.times.map { |i| self[i] }.inspect}>"
24
+ end
25
+ alias_method :to_s, :inspect
26
+
27
+ private
28
+
29
+ def check_index(index)
30
+ if index >= @n
31
+ raise IndexError, "index #{index} outside of array bounds"
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/nlopt/opt.rb ADDED
@@ -0,0 +1,99 @@
1
+ module NLopt
2
+ class Opt
3
+ def initialize(algorithm, n)
4
+ algorithm = FFI.nlopt_algorithm_from_string(algorithm)
5
+ if algorithm < 0
6
+ raise ArgumentError, "Invalid algorithm"
7
+ end
8
+
9
+ if n <= 0
10
+ raise ArgumentError, "Invalid dimension"
11
+ end
12
+
13
+ @opt = FFI.nlopt_create(algorithm, n)
14
+ @opt.free = FFI["nlopt_destroy"]
15
+ end
16
+
17
+ def set_min_objective(f)
18
+ # keep reference
19
+ @f = objective_callback(f)
20
+ check_res FFI.nlopt_set_min_objective(@opt, @f, nil)
21
+ end
22
+
23
+ def set_max_objective(f)
24
+ # keep reference
25
+ @f = objective_callback(f)
26
+ check_res FFI.nlopt_set_max_objective(@opt, @f, nil)
27
+ end
28
+
29
+ def set_lower_bounds(lb)
30
+ if lb.is_a?(Array)
31
+ check_res FFI.nlopt_set_lower_bounds(@opt, Fiddle::Pointer[lb.pack("d*")])
32
+ elsif lb.is_a?(Numeric)
33
+ check_res FFI.nlopt_set_lower_bounds1(@opt, lb)
34
+ else
35
+ raise TypeError, "expected array or numeric"
36
+ end
37
+ end
38
+
39
+ def set_upper_bounds(ub)
40
+ if ub.is_a?(Array)
41
+ check_res FFI.nlopt_set_upper_bounds(@opt, Fiddle::Pointer[ub.pack("d*")])
42
+ elsif ub.is_a?(Numeric)
43
+ check_res FFI.nlopt_set_upper_bounds1(@opt, ub)
44
+ else
45
+ raise TypeError, "expected array or numeric"
46
+ end
47
+ end
48
+
49
+ def set_maxeval(maxeval)
50
+ check_res FFI.nlopt_set_maxeval(@opt, maxeval)
51
+ end
52
+
53
+ def optimize(init)
54
+ if init.size != dimension
55
+ raise ArgumentError, "size does not match dimension"
56
+ end
57
+
58
+ x = Fiddle::Pointer[init.pack("d*")]
59
+ opt_f = Fiddle::Pointer.malloc(Fiddle::SIZEOF_DOUBLE)
60
+ res = FFI.nlopt_optimize(@opt, x, opt_f)
61
+
62
+ if res < 0 && res != -4
63
+ errmsg = FFI.nlopt_get_errmsg(@opt)
64
+ msg = !errmsg.null? ? errmsg.to_s : "Bad result: #{FFI.nlopt_result_to_string(res).to_s}"
65
+ raise Error, msg
66
+ end
67
+
68
+ x.to_s(x.size).unpack("d*")
69
+ end
70
+
71
+ def algorithm_name
72
+ FFI.nlopt_algorithm_name(FFI.nlopt_get_algorithm(@opt)).to_s
73
+ end
74
+
75
+ def dimension
76
+ FFI.nlopt_get_dimension(@opt)
77
+ end
78
+
79
+ def numevals
80
+ FFI.nlopt_get_numevals(@opt)
81
+ end
82
+
83
+ private
84
+
85
+ def check_res(res)
86
+ if res != 1
87
+ raise Error, "Bad result: #{FFI.nlopt_result_to_string(res).to_s}"
88
+ end
89
+ end
90
+
91
+ def objective_callback(f)
92
+ Fiddle::Closure::BlockCaller.new(Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_UINT, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP]) do |n, x, gradient, func_data|
93
+ x = x.to_s(n * Fiddle::SIZEOF_DOUBLE).unpack("d*")
94
+ grad = !gradient.null? ? Gradient.new(gradient, n) : nil
95
+ f.call(x, grad)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module NLopt
2
+ VERSION = "0.1.0"
3
+ end
data/lib/nlopt.rb ADDED
@@ -0,0 +1,40 @@
1
+ # stdlib
2
+ require "fiddle/import"
3
+
4
+ # modules
5
+ require_relative "nlopt/gradient"
6
+ require_relative "nlopt/opt"
7
+ require_relative "nlopt/version"
8
+
9
+ module NLopt
10
+ class Error < StandardError; end
11
+
12
+ class << self
13
+ attr_accessor :ffi_lib
14
+ end
15
+ lib_name =
16
+ if Gem.win_platform?
17
+ # TODO test
18
+ ["nlopt.dll"]
19
+ elsif RbConfig::CONFIG["host_os"] =~ /darwin/i
20
+ if RbConfig::CONFIG["host_cpu"] =~ /arm|aarch64/i
21
+ ["libnlopt.dylib", "/opt/homebrew/lib/libnlopt.dylib"]
22
+ else
23
+ ["libnlopt.dylib"]
24
+ end
25
+ else
26
+ # libnlopt-dev has libnlopt.so
27
+ # libnlopt0 has libnlopt.so.0
28
+ ["libnlopt.so", "libnlopt.so.0"]
29
+ end
30
+ self.ffi_lib = lib_name
31
+
32
+ # friendlier error message
33
+ autoload :FFI, "nlopt/ffi"
34
+
35
+ def self.lib_version
36
+ major, minor, bugfix = 3.times.map { Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT) }
37
+ FFI.nlopt_version(major, minor, bugfix)
38
+ [major, minor, bugfix].map { |v| v.to_s(v.size).unpack1("i") }.join(".")
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nlopt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Kane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fiddle
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email: andrew@ankane.org
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - CHANGELOG.md
34
+ - LICENSE.txt
35
+ - README.md
36
+ - lib/nlopt.rb
37
+ - lib/nlopt/ffi.rb
38
+ - lib/nlopt/gradient.rb
39
+ - lib/nlopt/opt.rb
40
+ - lib/nlopt/version.rb
41
+ homepage: https://github.com/ankane/nlopt-ruby
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '3.1'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubygems_version: 3.5.22
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Nonlinear optimization for Ruby
64
+ test_files: []