nlopt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +77 -0
- data/lib/nlopt/ffi.rb +56 -0
- data/lib/nlopt/gradient.rb +35 -0
- data/lib/nlopt/opt.rb +99 -0
- data/lib/nlopt/version.rb +3 -0
- data/lib/nlopt.rb +40 -0
- metadata +64 -0
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
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
|
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: []
|