nlopt 0.1.0

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.
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: []