ruby-gsl-ngx 0.2.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|