ruby-gsl-ngx 0.2.6.1
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.
- data/.gitignore +11 -0
- data/History.txt +62 -0
- data/Manifest +33 -0
- data/README.rdoc +14 -0
- data/Rakefile +27 -0
- data/ext/extconf.rb +20 -0
- data/ext/gslng_extensions.cpp +307 -0
- data/ext/plotting.cpp +22 -0
- data/lib/core_extensions/array.rb +14 -0
- data/lib/gslng.rb +11 -0
- data/lib/gslng/backend.rb +23 -0
- data/lib/gslng/backend_components/error_handling.rb +12 -0
- data/lib/gslng/backend_components/matrix.rb +83 -0
- data/lib/gslng/backend_components/rng.rb +22 -0
- data/lib/gslng/backend_components/special.rb +7 -0
- data/lib/gslng/backend_components/stats.rb +36 -0
- data/lib/gslng/backend_components/vector.rb +64 -0
- data/lib/gslng/matrix.rb +528 -0
- data/lib/gslng/matrix_view.rb +47 -0
- data/lib/gslng/plotter.rb +88 -0
- data/lib/gslng/rng/gaussian.rb +34 -0
- data/lib/gslng/rng/rng.rb +28 -0
- data/lib/gslng/rng/uniform.rb +23 -0
- data/lib/gslng/special.rb +22 -0
- data/lib/gslng/vector.rb +553 -0
- data/lib/gslng/vector_view.rb +44 -0
- data/ruby-gsl-ngx.gemspec +22 -0
- data/test/benchmark.rb +113 -0
- data/test/benchmark_results +92 -0
- data/test/matrix_test.rb +145 -0
- data/test/rng_test.rb +27 -0
- data/test/test_gsl.rb +11 -0
- data/test/test_special.rb +21 -0
- data/test/vector_test.rb +145 -0
- metadata +134 -0
data/ext/plotting.cpp
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#include <gsl/gsl_matrix.h>
|
2
|
+
#include <unistd.h>
|
3
|
+
#include <iostream>
|
4
|
+
using namespace std;
|
5
|
+
|
6
|
+
extern "C" int gsl_matrix_putdata(gsl_matrix* m, int fd) {
|
7
|
+
size_t bytes = m->size1 * m->size2 * sizeof(double);
|
8
|
+
long ret = write(fd, m->data, bytes);
|
9
|
+
if (ret == -1 || (unsigned long)ret < bytes) {
|
10
|
+
if (errno == EINTR) {
|
11
|
+
cout << "retrying write" << endl;
|
12
|
+
long written;
|
13
|
+
if (ret == -1) written = 0;
|
14
|
+
else written = ret;
|
15
|
+
|
16
|
+
ret = write(fd, m->data + written, bytes - written);
|
17
|
+
if (ret == -1 || (unsigned long)ret < (bytes - written)) return errno;
|
18
|
+
else return 0;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
else return 0;
|
22
|
+
}
|
data/lib/gslng.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module GSLng
|
4
|
+
# Anonymous module: avoids exposing this internal module when doing "include GSLng" at the top-level.
|
5
|
+
# If ruby had "private" modules I wouldn't have to do this.
|
6
|
+
@backend = Module.new do
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib(FFI::CURRENT_PROCESS)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns the internal backend module
|
12
|
+
def GSLng.backend # @private
|
13
|
+
@backend
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'gslng_extensions'
|
18
|
+
require 'gslng/backend_components/vector'
|
19
|
+
require 'gslng/backend_components/matrix'
|
20
|
+
require 'gslng/backend_components/error_handling'
|
21
|
+
require 'gslng/backend_components/rng'
|
22
|
+
require 'gslng/backend_components/special'
|
23
|
+
require 'gslng/backend_components/stats'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module GSLng
|
2
|
+
backend.instance_eval do
|
3
|
+
# All of this raises an exception when GSL registers an error
|
4
|
+
callback :error_handler_callback, [ :string, :string, :int, :int ], :void
|
5
|
+
attach_function :gsl_set_error_handler, [ :error_handler_callback ], :error_handler_callback
|
6
|
+
|
7
|
+
ErrorHandlerCallback = Proc.new {|reason, file, line, errno|
|
8
|
+
raise RuntimeError, "#{reason} (errno: #{errno})", caller[2..-1]
|
9
|
+
}
|
10
|
+
gsl_set_error_handler(ErrorHandlerCallback)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module GSLng
|
2
|
+
|
3
|
+
class GSLmatrix < FFI::Struct
|
4
|
+
layout :size1, :size_t,
|
5
|
+
:size2, :size_t,
|
6
|
+
:tda, :size_t,
|
7
|
+
:data, :pointer,
|
8
|
+
:block, :pointer,
|
9
|
+
:owner, :int
|
10
|
+
end
|
11
|
+
|
12
|
+
backend.instance_eval do
|
13
|
+
|
14
|
+
# memory handling
|
15
|
+
attach_function :gsl_matrix_alloc, [ :size_t, :size_t ], :pointer
|
16
|
+
attach_function :gsl_matrix_calloc, [ :size_t, :size_t ], :pointer
|
17
|
+
attach_function :gsl_matrix_free, [ :pointer ], :void
|
18
|
+
|
19
|
+
# initializing
|
20
|
+
attach_function :gsl_matrix_set_all, [ :pointer, :double ], :void
|
21
|
+
attach_function :gsl_matrix_set_zero, [ :pointer ], :void
|
22
|
+
attach_function :gsl_matrix_set_identity, [ :pointer ], :void
|
23
|
+
|
24
|
+
# copying
|
25
|
+
attach_function :gsl_matrix_memcpy, [ :pointer, :pointer ], :int
|
26
|
+
|
27
|
+
# operations
|
28
|
+
attach_function :gsl_matrix_add, [ :pointer, :pointer ], :int
|
29
|
+
attach_function :gsl_matrix_sub, [ :pointer, :pointer ], :int
|
30
|
+
attach_function :gsl_matrix_mul_elements, [ :pointer, :pointer ], :int
|
31
|
+
attach_function :gsl_matrix_div_elements, [ :pointer, :pointer ], :int
|
32
|
+
attach_function :gsl_matrix_scale, [ :pointer, :double ], :int
|
33
|
+
attach_function :gsl_matrix_add_constant, [ :pointer, :double ], :int
|
34
|
+
|
35
|
+
# copy rows/columns into
|
36
|
+
attach_function :gsl_matrix_get_row, [ :pointer, :pointer, :size_t ], :int
|
37
|
+
attach_function :gsl_matrix_get_col, [ :pointer, :pointer, :size_t ], :int
|
38
|
+
attach_function :gsl_matrix_set_row, [ :pointer, :size_t, :pointer ], :int
|
39
|
+
attach_function :gsl_matrix_set_col, [ :pointer, :size_t, :pointer ], :int
|
40
|
+
|
41
|
+
# element access
|
42
|
+
attach_function :gsl_matrix_get, [ :pointer, :size_t, :size_t ], :double
|
43
|
+
attach_function :gsl_matrix_set, [ :pointer, :size_t, :size_t, :double ], :void
|
44
|
+
|
45
|
+
# properties
|
46
|
+
attach_function :gsl_matrix_isnull, [ :pointer ], :int
|
47
|
+
attach_function :gsl_matrix_ispos, [ :pointer ], :int
|
48
|
+
attach_function :gsl_matrix_isneg, [ :pointer ], :int
|
49
|
+
attach_function :gsl_matrix_isnonneg, [ :pointer ], :int
|
50
|
+
|
51
|
+
# max and min
|
52
|
+
attach_function :gsl_matrix_max, [ :pointer ], :double
|
53
|
+
attach_function :gsl_matrix_min, [ :pointer ], :double
|
54
|
+
attach_function :gsl_matrix_minmax, [ :pointer, :buffer_out, :buffer_out ], :void
|
55
|
+
attach_function :gsl_matrix_max_index, [ :pointer, :buffer_out, :buffer_out ], :void
|
56
|
+
attach_function :gsl_matrix_min_index, [ :pointer, :buffer_out, :buffer_out ], :void
|
57
|
+
attach_function :gsl_matrix_minmax_index, [ :pointer, :buffer_out, :buffer_out, :buffer_out, :buffer_out ], :void
|
58
|
+
|
59
|
+
# exchange elements
|
60
|
+
attach_function :gsl_matrix_swap_rows, [ :pointer, :size_t, :size_t ], :int
|
61
|
+
attach_function :gsl_matrix_swap_columns, [ :pointer, :size_t, :size_t ], :int
|
62
|
+
attach_function :gsl_matrix_swap_rowcol, [ :pointer, :size_t, :size_t ], :int
|
63
|
+
attach_function :gsl_matrix_transpose_memcpy, [ :pointer, :pointer ], :int
|
64
|
+
attach_function :gsl_matrix_transpose, [ :pointer ], :int
|
65
|
+
|
66
|
+
# From local extension
|
67
|
+
# views
|
68
|
+
attach_function :gsl_matrix_submatrix2, [ :pointer, :size_t, :size_t, :size_t, :size_t ], :pointer
|
69
|
+
attach_function :gsl_matrix_row_view, [ :pointer, :size_t, :size_t, :size_t ], :pointer
|
70
|
+
attach_function :gsl_matrix_column_view, [ :pointer, :size_t, :size_t, :size_t ], :pointer
|
71
|
+
|
72
|
+
# slide
|
73
|
+
attach_function :gsl_matrix_slide, [ :pointer, :ssize_t, :ssize_t ], :void
|
74
|
+
|
75
|
+
# BLAS interface
|
76
|
+
enum :cblas_transpose_t, [ :no_transpose, 111, :transpose, :conjugate_transpose ]
|
77
|
+
attach_function :gsl_blas_dgemv, [ :cblas_transpose_t, :double, :pointer, :pointer, :double, :pointer ], :int
|
78
|
+
attach_function :gsl_blas_dgemm, [ :cblas_transpose_t, :cblas_transpose_t, :double, :pointer, :pointer, :double, :pointer ], :int
|
79
|
+
|
80
|
+
# communication to gnuplot
|
81
|
+
attach_function :gsl_matrix_putdata, [ :pointer, :int ], :int
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module GSLng
|
2
|
+
backend.instance_eval do
|
3
|
+
# memory handling
|
4
|
+
attach_function :gsl_rng_alloc, [ :pointer ], :pointer
|
5
|
+
attach_function :gsl_rng_free, [ :pointer ], :void
|
6
|
+
|
7
|
+
# RNG types
|
8
|
+
algorithms = %w(mt19937 ranlxs0 ranlxs1 ranlxs2 ranlxd1 ranlxd2 ranlux ranlux389 cmrg mrg taus taus2 gfsr4)
|
9
|
+
algorithms.each do |alg|
|
10
|
+
attach_variable :"gsl_rng_#{alg}", :pointer
|
11
|
+
end
|
12
|
+
|
13
|
+
# Uniform
|
14
|
+
attach_function :gsl_ran_flat, [ :pointer, :double, :double ], :double
|
15
|
+
attach_function :gsl_ran_flat_pdf, [ :double, :double, :double ], :double
|
16
|
+
|
17
|
+
# Gaussian
|
18
|
+
attach_function :gsl_ran_gaussian, [ :pointer, :double ], :double
|
19
|
+
attach_function :gsl_ran_gaussian_ziggurat, [ :pointer, :double ], :double
|
20
|
+
attach_function :gsl_ran_gaussian_ratio_method, [ :pointer, :double ], :double
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module GSLng
|
2
|
+
backend.instance_eval do
|
3
|
+
# mean, sd and variance
|
4
|
+
attach_function :gsl_stats_mean, [ :pointer, :size_t, :size_t ], :double
|
5
|
+
attach_function :gsl_stats_median_from_sorted_data, [ :pointer, :size_t, :size_t ], :double
|
6
|
+
attach_function :gsl_stats_variance, [ :pointer, :size_t, :size_t ], :double
|
7
|
+
attach_function :gsl_stats_variance_m, [ :pointer, :size_t, :size_t, :double ], :double
|
8
|
+
attach_function :gsl_stats_sd, [ :pointer, :size_t, :size_t ], :double
|
9
|
+
attach_function :gsl_stats_sd_m, [ :pointer, :size_t, :size_t, :double ], :double
|
10
|
+
attach_function :gsl_stats_tss, [ :pointer, :size_t, :size_t ], :double
|
11
|
+
attach_function :gsl_stats_tss_m, [ :pointer, :size_t, :size_t, :double ], :double
|
12
|
+
attach_function :gsl_stats_variance_with_fixed_mean, [ :pointer, :size_t, :size_t, :double ], :double
|
13
|
+
attach_function :gsl_stats_sd_with_fixed_mean, [ :pointer, :size_t, :size_t, :double ], :double
|
14
|
+
|
15
|
+
# absolute deviation
|
16
|
+
attach_function :gsl_stats_absdev, [ :pointer, :size_t, :size_t ], :double
|
17
|
+
attach_function :gsl_stats_absdev_m, [ :pointer, :size_t, :size_t, :double ], :double
|
18
|
+
|
19
|
+
# skewness and kurtosis
|
20
|
+
attach_function :gsl_stats_skew, [ :pointer, :size_t, :size_t ], :double
|
21
|
+
attach_function :gsl_stats_skew_m_sd, [ :pointer, :size_t, :size_t, :double, :double ], :double
|
22
|
+
attach_function :gsl_stats_kurtosis, [ :pointer, :size_t, :size_t ], :double
|
23
|
+
attach_function :gsl_stats_kurtosis_m_sd, [ :pointer, :size_t, :size_t, :double, :double ], :double
|
24
|
+
|
25
|
+
# autocorrelation
|
26
|
+
attach_function :gsl_stats_lag1_autocorrelation, [ :pointer, :size_t, :size_t ], :double
|
27
|
+
attach_function :gsl_stats_lag1_autocorrelation_m, [ :pointer, :size_t, :size_t, :double ], :double
|
28
|
+
|
29
|
+
# covariance
|
30
|
+
attach_function :gsl_stats_covariance, [ :pointer, :size_t, :pointer, :size_t, :size_t ], :double
|
31
|
+
attach_function :gsl_stats_covariance_m, [ :pointer, :size_t, :pointer, :size_t, :size_t, :double, :double ], :double
|
32
|
+
|
33
|
+
# correlation
|
34
|
+
attach_function :gsl_stats_correlation, [ :pointer, :size_t, :pointer, :size_t, :size_t ], :double
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module GSLng
|
2
|
+
backend.instance_eval do
|
3
|
+
# memory handling
|
4
|
+
attach_function :gsl_vector_alloc, [ :size_t ], :pointer
|
5
|
+
attach_function :gsl_vector_calloc, [ :size_t ], :pointer
|
6
|
+
attach_function :gsl_vector_free, [ :pointer ], :void
|
7
|
+
|
8
|
+
# initializing
|
9
|
+
attach_function :gsl_vector_set_all, [ :pointer, :double ], :void
|
10
|
+
attach_function :gsl_vector_set_zero, [ :pointer ], :void
|
11
|
+
attach_function :gsl_vector_set_basis, [ :pointer, :size_t ], :int
|
12
|
+
|
13
|
+
# operations
|
14
|
+
attach_function :gsl_vector_add, [ :pointer, :pointer ], :int
|
15
|
+
attach_function :gsl_vector_sub, [ :pointer, :pointer ], :int
|
16
|
+
attach_function :gsl_vector_mul, [ :pointer, :pointer ], :int
|
17
|
+
attach_function :gsl_vector_div, [ :pointer, :pointer ], :int
|
18
|
+
attach_function :gsl_vector_scale, [ :pointer, :double ], :int
|
19
|
+
attach_function :gsl_vector_add_constant, [ :pointer, :double ], :int
|
20
|
+
|
21
|
+
# element access
|
22
|
+
attach_function :gsl_vector_get, [ :pointer, :size_t ], :double
|
23
|
+
attach_function :gsl_vector_set, [ :pointer, :size_t, :double ], :void
|
24
|
+
|
25
|
+
# properties
|
26
|
+
attach_function :gsl_vector_isnull, [ :pointer ], :int
|
27
|
+
attach_function :gsl_vector_ispos, [ :pointer ], :int
|
28
|
+
attach_function :gsl_vector_isneg, [ :pointer ], :int
|
29
|
+
attach_function :gsl_vector_isnonneg, [ :pointer ], :int
|
30
|
+
|
31
|
+
# max and min
|
32
|
+
attach_function :gsl_vector_max, [ :pointer ], :double
|
33
|
+
attach_function :gsl_vector_min, [ :pointer ], :double
|
34
|
+
attach_function :gsl_vector_minmax, [ :pointer, :buffer_out, :buffer_out ], :void
|
35
|
+
attach_function :gsl_vector_max_index, [ :pointer ], :size_t
|
36
|
+
attach_function :gsl_vector_min_index, [ :pointer ], :size_t
|
37
|
+
attach_function :gsl_vector_minmax_index, [ :pointer, :buffer_out, :buffer_out ], :void
|
38
|
+
|
39
|
+
# copying
|
40
|
+
attach_function :gsl_vector_memcpy, [ :pointer, :pointer ], :int
|
41
|
+
|
42
|
+
# exchanging elements
|
43
|
+
attach_function :gsl_vector_swap_elements, [ :pointer, :size_t, :size_t ], :int
|
44
|
+
attach_function :gsl_vector_reverse, [ :pointer ], :int
|
45
|
+
|
46
|
+
# BLAS functions
|
47
|
+
attach_function :gsl_blas_ddot, [ :pointer, :pointer, :buffer_out ], :int
|
48
|
+
attach_function :gsl_blas_dnrm2, [ :pointer ], :double
|
49
|
+
attach_function :gsl_blas_dasum, [ :pointer ], :double
|
50
|
+
#attach_function :gsl_blas_idamax, [ :pointer ], clbas_index??
|
51
|
+
#attach_function :gsl_blas_dcopy, use this instead of memcpy?
|
52
|
+
attach_function :gsl_blas_daxpy, [ :double, :pointer, :pointer ], :int
|
53
|
+
attach_function :gsl_blas_dscal, [ :double, :pointer ], :void
|
54
|
+
|
55
|
+
# Sorting
|
56
|
+
attach_function :gsl_sort_vector, [ :pointer ], :void
|
57
|
+
|
58
|
+
# From local extension
|
59
|
+
# views
|
60
|
+
attach_function :gsl_vector_subvector2, [ :pointer, :size_t, :size_t ], :pointer
|
61
|
+
attach_function :gsl_vector_subvector_with_stride2, [ :pointer, :size_t, :size_t, :size_t ], :pointer
|
62
|
+
attach_function :gsl_vector_as_array, [ :pointer ], :pointer
|
63
|
+
end
|
64
|
+
end
|
data/lib/gslng/matrix.rb
ADDED
@@ -0,0 +1,528 @@
|
|
1
|
+
module GSLng
|
2
|
+
# A fixed-size MxN matrix.
|
3
|
+
#
|
4
|
+
# =Notes
|
5
|
+
# See Vector notes. Everything applies with the following *differences/additions*:
|
6
|
+
# * The {#*} operator performs actual matrix-matrix and matrix-vector products. To perform element-by-element
|
7
|
+
# multiplication use the {#^} operator (or {#multiply} method) instead. The rest of the operators work element-by-element.
|
8
|
+
# * Operators can handle matrix-matrix, matrix-vector and matrix-scalar (also in reversed order). See {#coerce}.
|
9
|
+
# * The {#[]} and {#[]=} operators can handle a "wildcard" value for any dimension, just like MATLAB's colon (:).
|
10
|
+
class Matrix
|
11
|
+
attr_reader :m, :n
|
12
|
+
attr_reader :ptr # @private
|
13
|
+
attr_reader :ptr_value # @private
|
14
|
+
|
15
|
+
alias_method :height, :m
|
16
|
+
alias_method :width, :n
|
17
|
+
alias_method :rows, :m
|
18
|
+
alias_method :columns, :n
|
19
|
+
|
20
|
+
# Shorthand for [{#rows},{#columns}]
|
21
|
+
def size; [ @m, @n ] end
|
22
|
+
|
23
|
+
# @group Constructors
|
24
|
+
|
25
|
+
# Create a Matrix of m-by-n (rows and columns). If zero is true, the Matrix is initialized with zeros.
|
26
|
+
# Otherwise, the Matrix will contain garbage.
|
27
|
+
# You can optionally pass a block, in which case {#map_index!} will be called with it (i.e.: it works like Array.new).
|
28
|
+
def initialize(m, n, zero = false)
|
29
|
+
@backend = GSLng.backend
|
30
|
+
ptr = zero ? @backend.gsl_matrix_calloc(m, n) : @backend.gsl_matrix_alloc(m, n)
|
31
|
+
@ptr = FFI::AutoPointer.new(ptr, Matrix.method(:release))
|
32
|
+
@ptr_value = @ptr.to_i
|
33
|
+
@m,@n = m,n
|
34
|
+
if (block_given?) then self.map_index!(Proc.new) end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_copy(other) # @private
|
38
|
+
@backend = GSLng.backend
|
39
|
+
ptr = @backend.gsl_matrix_alloc(other.m, other.n)
|
40
|
+
@ptr = FFI::AutoPointer.new(ptr, Matrix.method(:release))
|
41
|
+
@ptr_value = @ptr.to_i
|
42
|
+
|
43
|
+
@m,@n = other.size
|
44
|
+
@backend.gsl_matrix_memcpy(@ptr, other.ptr)
|
45
|
+
end
|
46
|
+
|
47
|
+
def Matrix.release(ptr) # @private
|
48
|
+
GSLng.backend.gsl_matrix_free(ptr)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Same as Matrix.new(m, n, true)
|
52
|
+
def Matrix.zero(m, n); Matrix.new(m, n, true) end
|
53
|
+
|
54
|
+
# Create a matrix from an Array
|
55
|
+
# @see Matrix::[]
|
56
|
+
def Matrix.from_array(array)
|
57
|
+
if (array.empty?) then raise "Can't create empty matrix" end
|
58
|
+
|
59
|
+
if (Numeric === array[0])
|
60
|
+
m = Matrix.new(1, array.size)
|
61
|
+
GSLng.backend.gsl_matrix_from_array(m.ptr_value, [ array ])
|
62
|
+
return m
|
63
|
+
elsif (Array === array[0])
|
64
|
+
m = Matrix.new(array.size, array[0].size)
|
65
|
+
GSLng.backend.gsl_matrix_from_array(m.ptr_value, array)
|
66
|
+
return m
|
67
|
+
else
|
68
|
+
Matrix.new(array.size, array[0].to_a.size) {|i,j| array[i].to_a[j]}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Create a Matrix from an Array/Array of Arrays/Range
|
73
|
+
# @example
|
74
|
+
# Matrix[[1,2],[3,4]] => [1.0 2.0; 3.0 4.0]:Matrix
|
75
|
+
# Matrix[1,2,3] => [1.0 2.0 3.0]:Matrix
|
76
|
+
# Matrix[[1..3],[5..7]] => [1.0 2.0 3.0; 5.0 6.0 7.0]:Matrix
|
77
|
+
# @see Matrix::from_array
|
78
|
+
def Matrix.[](*args)
|
79
|
+
Matrix.from_array(args)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Generates a Matrix of m by n, of random numbers between 0 and 1.
|
83
|
+
# NOTE: This simply uses {Kernel::rand}
|
84
|
+
def Matrix.random(m, n)
|
85
|
+
Matrix.new(m, n).map!{|x| Kernel::rand}
|
86
|
+
end
|
87
|
+
class << self; alias_method :rand, :random end
|
88
|
+
|
89
|
+
# @group Setting/getting values
|
90
|
+
|
91
|
+
# Access the element (i,j), which means (row,column).
|
92
|
+
# Symbols :* or :all can be used as wildcards for both dimensions.
|
93
|
+
# @example If +m = Matrix[[1,2],[3,4]]+
|
94
|
+
# m[0,0] => 1.0
|
95
|
+
# m[0,:*] => [1.0, 2.0]:Matrix
|
96
|
+
# m[:*,0] => [1.0, 3.0]:Matrix
|
97
|
+
# m[:*,:*] => [1.0, 2.0; 3.0, 4.0]:Matrix
|
98
|
+
# @raise [RuntimeError] if out-of-bounds
|
99
|
+
# @return [Numeric,Matrix] the element/sub-matrix
|
100
|
+
def [](i, j = :*)
|
101
|
+
if (Integer === i && Integer === j)
|
102
|
+
@backend.gsl_matrix_get_operator(@ptr_value, i, j)
|
103
|
+
else
|
104
|
+
if (Symbol === i && Symbol === j) then return self
|
105
|
+
elsif (Symbol === i)
|
106
|
+
col = Vector.new(@m)
|
107
|
+
@backend.gsl_matrix_get_col(col.ptr, @ptr, j)
|
108
|
+
return col.to_matrix
|
109
|
+
elsif (Symbol === j)
|
110
|
+
row = Vector.new(@n)
|
111
|
+
@backend.gsl_matrix_get_row(row.ptr, @ptr, i)
|
112
|
+
return row.to_matrix
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Set the element (i,j), which means (row,column).
|
118
|
+
# @param [Numeric,Vector,Matrix] value depends on indexing
|
119
|
+
# @raise [RuntimeError] if out-of-bounds
|
120
|
+
# @see #[]
|
121
|
+
def []=(i, j, value)
|
122
|
+
if (Symbol === i && Symbol === j) then
|
123
|
+
if (Numeric === value) then self.fill!(value)
|
124
|
+
else
|
125
|
+
x,y = self.coerce(value)
|
126
|
+
@backend.gsl_matrix_memcpy(@ptr, x.ptr)
|
127
|
+
end
|
128
|
+
elsif (Symbol === i)
|
129
|
+
col = Vector.new(@m)
|
130
|
+
x,y = col.coerce(value)
|
131
|
+
@backend.gsl_matrix_set_col(@ptr, j, x.ptr)
|
132
|
+
return col
|
133
|
+
elsif (Symbol === j)
|
134
|
+
row = Vector.new(@n)
|
135
|
+
x,y = row.coerce(value)
|
136
|
+
@backend.gsl_matrix_set_row(@ptr, i, x.ptr)
|
137
|
+
return row
|
138
|
+
else
|
139
|
+
@backend.gsl_matrix_set_operator(@ptr_value, i, j, value)
|
140
|
+
end
|
141
|
+
|
142
|
+
return self
|
143
|
+
end
|
144
|
+
|
145
|
+
# Set all values to _v_
|
146
|
+
def all!(v); @backend.gsl_matrix_set_all(@ptr, v); return self end
|
147
|
+
alias_method :set!, :all!
|
148
|
+
alias_method :fill!, :all!
|
149
|
+
|
150
|
+
# Set all values to zero
|
151
|
+
def zero!; @backend.gsl_matrix_set_zero(@ptr); return self end
|
152
|
+
|
153
|
+
# Set the identity matrix values
|
154
|
+
def identity; @backend.gsl_matrix_set_identity(@ptr); return self end
|
155
|
+
|
156
|
+
# Copy matrix values from +other+ to +self+
|
157
|
+
def set(other); @backend.gsl_matrix_memcpy(@ptr, other.ptr); return self end
|
158
|
+
|
159
|
+
# @group Views
|
160
|
+
|
161
|
+
# Create a {Matrix::View} from this Matrix.
|
162
|
+
# If either _m_ or _n_ are nil, they're computed from _x_, _y_ and the Matrix's {#size}
|
163
|
+
# @return [Matrix::View]
|
164
|
+
def view(x = 0, y = 0, m = nil, n = nil)
|
165
|
+
View.new(self, x, y, (m or @m - x), (n or @n - y))
|
166
|
+
end
|
167
|
+
alias_method :submatrix_view, :view
|
168
|
+
|
169
|
+
# Shorthand for #submatrix_view(..).to_matrix.
|
170
|
+
# @return [Matrix]
|
171
|
+
def submatrix(*args); self.submatrix_view(*args).to_matrix end
|
172
|
+
|
173
|
+
# Creates a {Matrix::View} for the i-th column
|
174
|
+
# @return [Matrix::View]
|
175
|
+
def column_view(i, offset = 0, size = nil); self.view(offset, i, (size or (@m - offset)), 1) end
|
176
|
+
|
177
|
+
# Analogous to {#submatrix}
|
178
|
+
# @return [Matrix]
|
179
|
+
def column(*args); self.column_view(*args).to_matrix end
|
180
|
+
|
181
|
+
# Creates a {Matrix::View} for the i-th row
|
182
|
+
# @return [Matrix::View]
|
183
|
+
def row_view(i, offset = 0, size = nil); self.view(i, offset, 1, (size or (@n - offset))) end
|
184
|
+
|
185
|
+
# Analogous to {#submatrix}
|
186
|
+
# @return [Matrix]
|
187
|
+
def row(*args); self.row_view(*args).to_matrix end
|
188
|
+
|
189
|
+
# Same as {#row_view}, but returns a {Vector::View}
|
190
|
+
# @return [Vector::View]
|
191
|
+
def row_vecview(i, offset = 0, size = nil)
|
192
|
+
size = (@n - offset) if size.nil?
|
193
|
+
ptr = @backend.gsl_matrix_row_view(@ptr, i, offset, size)
|
194
|
+
Vector::View.new(ptr, self, size)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Same as {#column_view}, but returns a {Vector::View}
|
198
|
+
# @return [Vector::View]
|
199
|
+
def column_vecview(i, offset = 0, size = nil)
|
200
|
+
size = (@m - offset) if size.nil?
|
201
|
+
ptr = @backend.gsl_matrix_column_view(@ptr, i, offset, size)
|
202
|
+
Vector::View.new(ptr, self, size, self.columns)
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
# @group Operators
|
207
|
+
|
208
|
+
# Add other to self
|
209
|
+
# @return [Matrix] self
|
210
|
+
def add!(other)
|
211
|
+
case other
|
212
|
+
when Numeric; @backend.gsl_matrix_add_constant(@ptr, other.to_f)
|
213
|
+
when Matrix; @backend.gsl_matrix_add(@ptr, other.ptr)
|
214
|
+
else
|
215
|
+
x,y = other.coerce(self)
|
216
|
+
x.add!(y)
|
217
|
+
end
|
218
|
+
return self
|
219
|
+
end
|
220
|
+
|
221
|
+
# Substract other from self
|
222
|
+
# @return [Matrix] self
|
223
|
+
def substract!(other)
|
224
|
+
case other
|
225
|
+
when Numeric; @backend.gsl_matrix_add_constant(@ptr, -other.to_f)
|
226
|
+
when Matrix; @backend.gsl_matrix_sub(@ptr, other.ptr)
|
227
|
+
else
|
228
|
+
x,y = other.coerce(self)
|
229
|
+
x.substract!(y)
|
230
|
+
end
|
231
|
+
return self
|
232
|
+
end
|
233
|
+
alias_method :sub!, :substract!
|
234
|
+
|
235
|
+
# Multiply (element-by-element) other with self
|
236
|
+
# @return [Matrix] self
|
237
|
+
def multiply!(other)
|
238
|
+
case other
|
239
|
+
when Numeric; @backend.gsl_matrix_scale(@ptr, other.to_f)
|
240
|
+
when Matrix; @backend.gsl_matrix_mul_elements(@ptr, other.ptr)
|
241
|
+
else
|
242
|
+
x,y = other.coerce(self)
|
243
|
+
x.multiply!(y)
|
244
|
+
end
|
245
|
+
return self
|
246
|
+
end
|
247
|
+
alias_method :mul!, :multiply!
|
248
|
+
|
249
|
+
# Divide (element-by-element) self by other
|
250
|
+
# @return [Matrix] self
|
251
|
+
def divide!(other)
|
252
|
+
case other
|
253
|
+
when Numeric; @backend.gsl_matrix_scale(@ptr, 1.0 / other)
|
254
|
+
when Matrix; @backend.gsl_matrix_div_elements(@ptr, other.ptr)
|
255
|
+
else
|
256
|
+
x,y = other.coerce(self)
|
257
|
+
x.divide!(y)
|
258
|
+
end
|
259
|
+
return self
|
260
|
+
end
|
261
|
+
alias_method :div!, :divide!
|
262
|
+
|
263
|
+
# Element-by-element addition
|
264
|
+
def +(other); self.dup.add!(other) end
|
265
|
+
|
266
|
+
# Element-by-element substraction
|
267
|
+
def -(other); self.dup.substract!(other) end
|
268
|
+
|
269
|
+
# Element-by-element division
|
270
|
+
def /(other); self.dup.divide!(other) end
|
271
|
+
|
272
|
+
# Element-by-element product. Both matrices should have same dimensions.
|
273
|
+
def ^(other); self.dup.multiply!(other) end
|
274
|
+
alias_method :multiply, :^
|
275
|
+
alias_method :mul, :^
|
276
|
+
|
277
|
+
# Matrix Product. self.n should equal other.m (or other.size, if a Vector).
|
278
|
+
# @example
|
279
|
+
# Matrix[[1,2],[2,3]] * 2 => [2.0 4.0; 4.0 6.0]:Matrix
|
280
|
+
# @todo some cases could be optimized when doing Matrix-Matrix, by using dgemv
|
281
|
+
def *(other)
|
282
|
+
case other
|
283
|
+
when Numeric
|
284
|
+
self.multiply(other)
|
285
|
+
when Vector
|
286
|
+
matrix = Matrix.new(self.m, other.size)
|
287
|
+
@backend.gsl_blas_dgemm(:no_transpose, :no_transpose, 1, @ptr, other.to_matrix.ptr, 0, matrix.ptr)
|
288
|
+
return matrix
|
289
|
+
when Matrix
|
290
|
+
matrix = Matrix.new(self.m, other.n)
|
291
|
+
@backend.gsl_blas_dgemm(:no_transpose, :no_transpose, 1, @ptr, other.ptr, 0, matrix.ptr)
|
292
|
+
return matrix
|
293
|
+
else
|
294
|
+
x,y = other.coerce(self)
|
295
|
+
x * y
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# @group Row/column swapping
|
300
|
+
|
301
|
+
# Transposes in-place. Only for square matrices
|
302
|
+
def transpose!; @backend.gsl_matrix_transpose(@ptr); return self end
|
303
|
+
|
304
|
+
# Returns the transpose of self, in a new matrix
|
305
|
+
def transpose; matrix = Matrix.new(@n, @m); @backend.gsl_matrix_transpose_memcpy(matrix.ptr, @ptr); return matrix end
|
306
|
+
|
307
|
+
# Swap the i-th and j-th columnos
|
308
|
+
def swap_columns(i, j); @backend.gsl_matrix_swap_columns(@ptr, i, j); return self end
|
309
|
+
|
310
|
+
# Swap the i-th and j-th rows
|
311
|
+
def swap_rows(i, j); @backend.gsl_matrix_swap_rows(@ptr, i, j); return self end
|
312
|
+
|
313
|
+
# Swap the i-th row with the j-th column. The Matrix must be square.
|
314
|
+
def swap_rowcol(i, j); @backend.gsl_matrix_swap_rowcol(@ptr, i, j); return self end
|
315
|
+
|
316
|
+
# Discards rows and columns as necessary (fill them with zero), to "slide" the values of the matrix
|
317
|
+
# @param [Integer] i If > 0, slides all values to the bottom (adds +i+ rows of zeros at the top). If < 0,
|
318
|
+
# slides all values to the top and adds zeros in the bottom.
|
319
|
+
# @param [Integer] j Analogous to parameter +i+, in this case a value < 0 adds zeros to the right (slides to the left),
|
320
|
+
# and a value > 0 adds zeros to the left (slides to the right).
|
321
|
+
def slide(i, j); @backend.gsl_matrix_slide(@ptr, i, j); return self end
|
322
|
+
|
323
|
+
# @group Predicate methods
|
324
|
+
|
325
|
+
# if all elements are zero
|
326
|
+
def zero?; @backend.gsl_matrix_isnull(@ptr) == 1 ? true : false end
|
327
|
+
|
328
|
+
# if all elements are strictly positive (>0)
|
329
|
+
def positive?; @backend.gsl_matrix_ispos(@ptr) == 1 ? true : false end
|
330
|
+
|
331
|
+
#if all elements are strictly negative (<0)
|
332
|
+
def negative?; @backend.gsl_matrix_isneg(@ptr) == 1 ? true : false end
|
333
|
+
|
334
|
+
# if all elements are non-negative (>=0)
|
335
|
+
def nonnegative?; @backend.gsl_matrix_isnonneg(@ptr) == 1 ? true : false end
|
336
|
+
|
337
|
+
# If this is a column Matrix
|
338
|
+
def column?; self.columns == 1 end
|
339
|
+
|
340
|
+
# @group Minimum/maximum
|
341
|
+
|
342
|
+
# Maximum element of the Matrix
|
343
|
+
def max; @backend.gsl_matrix_max(@ptr) end
|
344
|
+
|
345
|
+
# Minimum element of the Matrix
|
346
|
+
def min; @backend.gsl_matrix_min(@ptr) end
|
347
|
+
|
348
|
+
# Same as {Array#minmax}
|
349
|
+
def minmax
|
350
|
+
min = FFI::Buffer.new(:double)
|
351
|
+
max = FFI::Buffer.new(:double)
|
352
|
+
@backend.gsl_matrix_minmax(@ptr, min, max)
|
353
|
+
return [min[0].get_float64(0),max[0].get_float64(0)]
|
354
|
+
end
|
355
|
+
|
356
|
+
# Same as {#minmax}, but returns the indices to the i-th and j-th min, and i-th and j-th max.
|
357
|
+
def minmax_index
|
358
|
+
i_min = FFI::Buffer.new(:size_t)
|
359
|
+
j_min = FFI::Buffer.new(:size_t)
|
360
|
+
i_max = FFI::Buffer.new(:size_t)
|
361
|
+
j_max = FFI::Buffer.new(:size_t)
|
362
|
+
@backend.gsl_matrix_minmax_index(@ptr, i_min, j_min, i_max, j_max)
|
363
|
+
#return [min[0].get_size_t(0),max[0].get_size_t(0)]
|
364
|
+
return [i_min[0].get_ulong(0),j_min[0].get_ulong(0),i_max[0].get_ulong(0),j_max[0].get_ulong(0)]
|
365
|
+
end
|
366
|
+
|
367
|
+
# Same as {#min}, but returns the indices to the i-th and j-th minimum elements
|
368
|
+
def min_index
|
369
|
+
i_min = FFI::Buffer.new(:size_t)
|
370
|
+
j_min = FFI::Buffer.new(:size_t)
|
371
|
+
@backend.gsl_matrix_min_index(@ptr, i_min, j_min)
|
372
|
+
return [i_min[0].get_ulong(0), j_min[0].get_ulong(0)]
|
373
|
+
end
|
374
|
+
|
375
|
+
# Same as {#max}, but returns the indices to the i-th and j-th maximum elements
|
376
|
+
def max_index
|
377
|
+
i_max = FFI::Buffer.new(:size_t)
|
378
|
+
j_max = FFI::Buffer.new(:size_t)
|
379
|
+
@backend.gsl_matrix_max_index(@ptr, i_max, j_max)
|
380
|
+
return [i_max[0].get_ulong(0), j_max[0].get_ulong(0)]
|
381
|
+
end
|
382
|
+
|
383
|
+
# @group High-order methods
|
384
|
+
|
385
|
+
# Yields the specified block for each element going row-by-row
|
386
|
+
# @yield [elem]
|
387
|
+
def each
|
388
|
+
@m.times {|i| @n.times {|j| yield(self[i,j]) } }
|
389
|
+
end
|
390
|
+
|
391
|
+
# Yields the specified block for each element going row-by-row
|
392
|
+
# @yield [elem, i, j]
|
393
|
+
def each_with_index
|
394
|
+
@m.times {|i| @n.times {|j| yield(self[i,j], i, j) } }
|
395
|
+
end
|
396
|
+
|
397
|
+
# Calls the block on each element of the matrix
|
398
|
+
# @yield [elem]
|
399
|
+
# @return [void]
|
400
|
+
def each(block = Proc.new)
|
401
|
+
@backend.gsl_matrix_each(@ptr_value, &block)
|
402
|
+
end
|
403
|
+
|
404
|
+
# @see #each
|
405
|
+
# @yield [elem,i,j]
|
406
|
+
def each_with_index(block = Proc.new)
|
407
|
+
@backend.gsl_matrix_each_with_index(@ptr_value, &block)
|
408
|
+
end
|
409
|
+
|
410
|
+
# Yields the block for each row *view* ({Matrix::View}).
|
411
|
+
# @yield [view]
|
412
|
+
def each_row; self.rows.times {|i| yield(row_view(i))} end
|
413
|
+
|
414
|
+
# Same as {#each_row}, but yields {Vector::View}'s
|
415
|
+
# @yield [vector_view]
|
416
|
+
def each_vec_row; self.rows.times {|i| yield(row_vecview(i))} end
|
417
|
+
|
418
|
+
# Same as #each_column, but yields {Vector::View}'s
|
419
|
+
# @yield [vector_view]
|
420
|
+
def each_vec_column; self.columns.times {|i| yield(column_vecview(i))} end
|
421
|
+
|
422
|
+
# Yields the block for each column *view* ({Matrix::View}).
|
423
|
+
# @yield [view]
|
424
|
+
def each_column; self.columns.times {|i| yield(column_view(i))} end
|
425
|
+
|
426
|
+
# Efficient {#map!} implementation
|
427
|
+
# @yield [elem]
|
428
|
+
def map!(block = Proc.new); @backend.gsl_matrix_map!(@ptr_value, &block); return self end
|
429
|
+
|
430
|
+
# Alternate version of {#map!}, in this case the block receives the index (row, column) as a parameter.
|
431
|
+
# @yield [i,j]
|
432
|
+
def map_index!(block = Proc.new); @backend.gsl_matrix_map_index!(@ptr_value, &block); return self end
|
433
|
+
|
434
|
+
# Similar to {#map_index!}, in this case it receives both the element and the index to it
|
435
|
+
# @yield [elem,i,j]
|
436
|
+
def map_with_index!(block = Proc.new); @backend.gsl_matrix_map_with_index!(@ptr_value, &block); return self end
|
437
|
+
|
438
|
+
# @see #map!
|
439
|
+
# @return [Matrix]
|
440
|
+
# @yield [elem]
|
441
|
+
def map(block = Proc.new); self.dup.map!(block) end
|
442
|
+
|
443
|
+
# @see #map
|
444
|
+
# @return [Array]
|
445
|
+
# @yield [elem]
|
446
|
+
def map_array(block = Proc.new); @backend.gsl_matrix_map_array(@ptr_value, &block) end
|
447
|
+
|
448
|
+
# @group Type conversions
|
449
|
+
|
450
|
+
# Same as {Array#join}
|
451
|
+
# @example
|
452
|
+
# Matrix[[1,2],[2,3]].join => "1.0 2.0 2.0 3.0"
|
453
|
+
def join(sep = $,)
|
454
|
+
s = ''
|
455
|
+
self.each do |e|
|
456
|
+
s += (s.empty?() ? e.to_s : "#{sep}#{e}")
|
457
|
+
end
|
458
|
+
return s
|
459
|
+
end
|
460
|
+
|
461
|
+
# Converts the matrix to a String, separating each element with a space and each row with a ';' and a newline.
|
462
|
+
# @example
|
463
|
+
# Matrix[[1,2],[2,3]] => "[1.0 2.0;\n 2.0 3.0]"
|
464
|
+
def to_s
|
465
|
+
s = '['
|
466
|
+
@m.times do |i|
|
467
|
+
s += ' ' unless i == 0
|
468
|
+
@n.times do |j|
|
469
|
+
s += (j == 0 ? self[i,j].to_s : ' ' + self[i,j].to_s)
|
470
|
+
end
|
471
|
+
s += (i == (@m-1) ? ']' : ";\n")
|
472
|
+
end
|
473
|
+
|
474
|
+
return s
|
475
|
+
end
|
476
|
+
|
477
|
+
# Converts the matrix to an Array (of Arrays).
|
478
|
+
# @example
|
479
|
+
# Matrix[[1,2],[2,3]] => [[1.0,2.0],[2.0,3.0]]
|
480
|
+
def to_a
|
481
|
+
@backend.gsl_matrix_to_a(@ptr_value)
|
482
|
+
end
|
483
|
+
|
484
|
+
def inspect # @private
|
485
|
+
"#{self}:Matrix"
|
486
|
+
end
|
487
|
+
|
488
|
+
# Coerces _other_ to be of Matrix class.
|
489
|
+
# If _other_ is a scalar (Numeric) a Matrix filled with _other_ values is created.
|
490
|
+
# Vectors are coerced using {Vector#to_matrix} (which results in a row matrix).
|
491
|
+
def coerce(other)
|
492
|
+
case other
|
493
|
+
when Matrix
|
494
|
+
[ other, self ]
|
495
|
+
when Numeric
|
496
|
+
[ Matrix.new(@m, @n).fill!(other), self ]
|
497
|
+
when Vector
|
498
|
+
[ other.to_matrix, self ]
|
499
|
+
else
|
500
|
+
raise TypeError, "Can't coerce #{other.class} into #{self.class}"
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
# @group Equality
|
505
|
+
|
506
|
+
# Element-by-element comparison.
|
507
|
+
def ==(other)
|
508
|
+
if (self.m != other.m || self.n != other.n) then return false end
|
509
|
+
|
510
|
+
@m.times do |i|
|
511
|
+
@n.times do |j|
|
512
|
+
if (self[i,j] != other[i,j]) then return false end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
return true
|
517
|
+
end
|
518
|
+
|
519
|
+
# @group FFI
|
520
|
+
|
521
|
+
# Returns the FFI::Pointer to the underlying C data memory; can be used to
|
522
|
+
# pass the data directly to/from other FFI libraries, without needing
|
523
|
+
# to go through Ruby conversion
|
524
|
+
def data_ptr
|
525
|
+
GSLmatrix.new(ptr)[:data]
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|