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
@@ -0,0 +1,47 @@
|
|
1
|
+
module GSLng
|
2
|
+
class Matrix
|
3
|
+
# A View of a Matrix.
|
4
|
+
#
|
5
|
+
# Views reference an existing Matrix and can be used to access parts of it without having to copy
|
6
|
+
# it entirely. You can treat a View just like a Matrix.
|
7
|
+
# But note that modifying elements of a View will modify the elements of the original matrix.
|
8
|
+
#
|
9
|
+
class View < Matrix
|
10
|
+
attr_reader :owner # The Matrix owning the data this View uses
|
11
|
+
|
12
|
+
# Create a Matrix::View of the sub-matrix starting at (x,y), of size (m,n)
|
13
|
+
def initialize(owner, x, y, m, n) # @private
|
14
|
+
@owner = owner
|
15
|
+
@m,@n = m,n
|
16
|
+
|
17
|
+
@backend = GSLng.backend
|
18
|
+
ptr = GSLng.backend::gsl_matrix_submatrix2(owner.ptr, x, y, m, n)
|
19
|
+
@ptr = FFI::AutoPointer.new(ptr, View.method(:release))
|
20
|
+
@ptr_value = @ptr.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def View.release(ptr)
|
24
|
+
GSLng.backend.gsl_matrix_free(ptr)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a Matrix (*NOT* a View) copied from this view. In other words,
|
28
|
+
# you'll get a Matrix which you can modify without modifying #owner elements.
|
29
|
+
# @return [Matrix]
|
30
|
+
def dup
|
31
|
+
matrix = Matrix.new(@m, @n)
|
32
|
+
GSLng.backend::gsl_matrix_memcpy(matrix.ptr, @ptr)
|
33
|
+
return matrix
|
34
|
+
end
|
35
|
+
alias_method :clone, :dup
|
36
|
+
alias_method :to_matrix, :dup
|
37
|
+
|
38
|
+
def view # @private
|
39
|
+
raise "Can't create a View from a View"
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect # @private
|
43
|
+
"#{self}:MatrixView"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'ffi'
|
3
|
+
|
4
|
+
module GSLng
|
5
|
+
# This class encapsulates the communication with the plotting backend: gnuplot.
|
6
|
+
#
|
7
|
+
# Plot data is always represented as a {Matrix}, so you can use {Matrix#plot} to do a single plot.
|
8
|
+
# Otherwise, you can use {Matrix#define_plot} (taking the same parameters as the previous method) which returns a {Plotter::Plot}
|
9
|
+
# object. By defining multiple plot objects you can then use {Plotter#plot} passing all plot objects as parameters effectively
|
10
|
+
# creating a single output of all plots.
|
11
|
+
#
|
12
|
+
# This class works as {Singleton}, so when you instantiate it the gnuplot process is started. You can also send arbitrary commands
|
13
|
+
# to the gnuplot process by using the {Plotter#<<} operator. Read the gnuplot documentation on what are the possible commands you can send.
|
14
|
+
#
|
15
|
+
class Plotter
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
attr_reader :io
|
19
|
+
|
20
|
+
# Creates the singleton Plotter object
|
21
|
+
def initialize
|
22
|
+
@io = IO.popen('gnuplot', 'w')
|
23
|
+
@io.sync = true
|
24
|
+
self << "set datafile nofpe_trap"
|
25
|
+
end
|
26
|
+
|
27
|
+
# Close the pipe. It is desireable to call this in an "ensure" section, to avoid
|
28
|
+
# leaving the child gnuplot process there if the main ruby process dies
|
29
|
+
def close
|
30
|
+
pid = @io.pid
|
31
|
+
@io.flush; @io.close; @io = nil
|
32
|
+
begin
|
33
|
+
Process.kill 'TERM', pid
|
34
|
+
rescue Errno::ESRCH
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Send a command to the gnuplot process
|
39
|
+
# @example Setting the xrange
|
40
|
+
# Plotter.instance << "set xrange [0:1]"
|
41
|
+
def <<(cmd)
|
42
|
+
@io.puts(cmd)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Expects a variable number of {Plot} objects and creates a single plot out of them
|
46
|
+
def plot(*plots)
|
47
|
+
self << 'plot ' + plots.map(&:command).join(', ')
|
48
|
+
plots.each {|p| self.put_data(p.matrix)}
|
49
|
+
end
|
50
|
+
|
51
|
+
def put_data(matrix) # @private
|
52
|
+
ret = GSLng.backend.gsl_matrix_putdata(matrix.ptr, @io.to_i)
|
53
|
+
if (ret != 0) then raise SystemCallError.new("Problem sending data to gnuplot", ret) end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Yields the given block enabling and disabling multiplot mode, before and after the yield respectively
|
57
|
+
def multiplot
|
58
|
+
self << 'set multiplot'
|
59
|
+
yield(self)
|
60
|
+
ensure
|
61
|
+
self << 'unset multiplot'
|
62
|
+
end
|
63
|
+
|
64
|
+
# This class holds a "plot" command and the associated matrix
|
65
|
+
class Plot < Struct.new(:command, :matrix); end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Matrix
|
69
|
+
# Create a single plot out of the data contained in this Matrix
|
70
|
+
# @param [String] with The value of the 'with' option in gnuplot's plot command (i.e.: lines, linespoints, image, etc.)
|
71
|
+
# @param [String] extra_cmds Other variables/modifiers you can optionally pass to the "plot" command
|
72
|
+
def plot(with, extra_cmds = '')
|
73
|
+
Plotter.instance.plot(define_plot(with, extra_cmds))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Works the same as {#plot} but returns a {Plotter::Plot} object you can store and then pass (along with other plot objects)
|
77
|
+
# to {Plotter#plot} and create a single plot out of them
|
78
|
+
# @return [Plot] The plot object you can pass to {Plotter#plot}
|
79
|
+
def define_plot(with, extra_cmds = '')
|
80
|
+
if (with == 'image')
|
81
|
+
cmd = "'-' binary array=(#{self.m},#{self.n}) format='%double' #{extra_cmds} with #{with}"
|
82
|
+
else
|
83
|
+
cmd = "'-' binary record=(#{self.m}) format=\"#{Array.new(self.n,'%double').join('')}\" #{extra_cmds} with #{with}"
|
84
|
+
end
|
85
|
+
Plotter::Plot.new(cmd, self)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module GSLng
|
2
|
+
class RNG
|
3
|
+
class Gaussian < RNG
|
4
|
+
attr_reader :ptr # @private
|
5
|
+
|
6
|
+
attr_reader :mu, :sigma
|
7
|
+
|
8
|
+
# Creates a new Gaussian distribution
|
9
|
+
# @param [Float] mu mean
|
10
|
+
# @param [Float] sigma standard deviation
|
11
|
+
# @param [Symbol] sample_method The method to use to sample numbers. Can be either :boxmuller, :ziggurat or :ratio_method
|
12
|
+
# @param generator_type (see GSLng::RNG#initialize)
|
13
|
+
# @see http://www.gnu.org/software/gsl/manual/html_node/The-Gaussian-Distribution.html
|
14
|
+
def initialize(mu = 0, sigma = 1, sample_method = :boxmuller, generator_type = nil)
|
15
|
+
super(generator_type)
|
16
|
+
@mu,@sigma = mu,sigma
|
17
|
+
|
18
|
+
case sample_method
|
19
|
+
when :boxmuller; @function = :gsl_ran_gaussian
|
20
|
+
when :ziggurat; @function = :gsl_ran_gaussian_ziggurat
|
21
|
+
when :ratio_method; @function = :gsl_ran_ratio_method
|
22
|
+
else raise "Unsupported method"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Obtain a sample from this distribution
|
27
|
+
# @return [Float]
|
28
|
+
def sample
|
29
|
+
GSLng.backend.send(@function, self.ptr, @sigma) + @mu
|
30
|
+
end
|
31
|
+
alias_method :get, :sample
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module GSLng
|
2
|
+
# Random Number Generator class
|
3
|
+
# @abstract You should use any of the descendant classes which implement the {#sample} method.
|
4
|
+
class RNG
|
5
|
+
# Create a Generator of the given type
|
6
|
+
# @param [Symbol] generator_type the algorithm to use (without the +gsl_rng+ prefix). Default is :mt19937.
|
7
|
+
# @see http://www.gnu.org/software/gsl/manual/html_node/Random-number-generator-algorithms.html
|
8
|
+
def initialize(generator_type = nil)
|
9
|
+
@type = generator_type
|
10
|
+
if (@type.nil?) then @type = :mt19937 end # update comment above if changed
|
11
|
+
|
12
|
+
type = GSLng.backend.send(:"gsl_rng_#{@type}")
|
13
|
+
@ptr = FFI::AutoPointer.new(GSLng.backend.gsl_rng_alloc(type), RNG.method(:release))
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.release(ptr)
|
17
|
+
GSLng.backend.gsl_rng_free(ptr)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize_copy
|
21
|
+
type = GSLng.backend.send(:"gsl_rng_#{@type}")
|
22
|
+
@ptr = FFI::AutoPointer.new(GSLng.backend.gsl_rng_alloc(type), RNG.method(:release))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'gslng/rng/gaussian'
|
28
|
+
require 'gslng/rng/uniform'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module GSLng
|
2
|
+
class RNG
|
3
|
+
class Uniform < RNG
|
4
|
+
attr_reader :ptr # @private
|
5
|
+
|
6
|
+
# Creates a new Uniform distribution with values in [min,max)
|
7
|
+
# @param [Float] min
|
8
|
+
# @param [Float] max
|
9
|
+
# @param generator_type (see GSLng::RNG#initialize)
|
10
|
+
def initialize(min, max, generator_type = nil)
|
11
|
+
super(generator_type)
|
12
|
+
@min,@max = min,max
|
13
|
+
end
|
14
|
+
|
15
|
+
# Obtain a sample from this distribution
|
16
|
+
# @return [Float]
|
17
|
+
def sample
|
18
|
+
GSLng.backend.gsl_ran_flat(self.ptr, @min, @max)
|
19
|
+
end
|
20
|
+
alias_method :get, :sample
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module GSLng
|
2
|
+
# A group of several different special functions.
|
3
|
+
#
|
4
|
+
# =Notes
|
5
|
+
# You can use this module simply by acessing its functions. Optionally, you can also extend the +Math+ standard module
|
6
|
+
# with these methods by doing:
|
7
|
+
# Math.extend(GSLng::Special)
|
8
|
+
#
|
9
|
+
module Special
|
10
|
+
extend self # allow this module to be used as such, and as a mixin
|
11
|
+
|
12
|
+
# Restrict the given angle to the interval (-pi,pi]
|
13
|
+
def angle_restrict_symm(theta)
|
14
|
+
GSLng.backend.gsl_sf_angle_restrict_symm(theta)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Restrict the given angle to the interval (0,2pi]
|
18
|
+
def angle_restrict_pos(theta)
|
19
|
+
GSLng.backend.gsl_sf_angle_restrict_pos(theta)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/gslng/vector.rb
ADDED
@@ -0,0 +1,553 @@
|
|
1
|
+
module GSLng
|
2
|
+
# A fixed-size n-dimensional vector.
|
3
|
+
#
|
4
|
+
# =Notes
|
5
|
+
# * {#each}, {#map} and similar methods are implemented with C versions which should be fast.
|
6
|
+
# * {#map} returns a Vector, not an Array. Use {#map_array} for that.
|
7
|
+
# * While this class includes Enumerable, certain methods are redefined (like {#max} and {#min})
|
8
|
+
# so they use internal GSL methods.
|
9
|
+
# * Some functions (like {#sum}, {#dot}, and others) use BLAS functions (through GSLng's CBLAS interface).
|
10
|
+
# * In contrary to Array, operators {#[]} and {#[]=} will raise an exception when accessing out-of-bounds elements.
|
11
|
+
# * Operator {#*} multiplies two vectors element-by-element. To perform a dot product use the {#^} operator instead (or the {#dot} alias).
|
12
|
+
# * Operands are coerced to vectors so you can do vector + scalar, etc. (see {#coerce})
|
13
|
+
#
|
14
|
+
class Vector
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
attr_reader :size, :stride
|
18
|
+
attr_reader :ptr # @private
|
19
|
+
attr_reader :ptr_value # @private
|
20
|
+
|
21
|
+
# @group Constructors
|
22
|
+
|
23
|
+
# Create a Vector of size n. If zero is true, the vector is initialized with zeros.
|
24
|
+
# Otherwise, the vector will contain garbage.
|
25
|
+
# You can optionally pass a block, in which case {#map_index!} will be called with it (i.e.: it works like {Array.new}).
|
26
|
+
def initialize(n, zero = false)
|
27
|
+
@backend = GSLng.backend
|
28
|
+
ptr = (zero ? @backend.gsl_vector_calloc(n) : @backend.gsl_vector_alloc(n))
|
29
|
+
@ptr = FFI::AutoPointer.new(ptr, Vector.method(:release))
|
30
|
+
@ptr_value = @ptr.to_i
|
31
|
+
@size = n
|
32
|
+
@stride = 1
|
33
|
+
if (block_given?) then self.map_index!(Proc.new) end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize_copy(other) # @private
|
37
|
+
@backend = GSLng.backend
|
38
|
+
ptr = @backend.gsl_vector_alloc(other.size)
|
39
|
+
@ptr = FFI::AutoPointer.new(ptr, Vector.method(:release))
|
40
|
+
@ptr_value = @ptr.to_i
|
41
|
+
@size = other.size
|
42
|
+
@stride = 1
|
43
|
+
@backend.gsl_vector_memcpy(@ptr, other.ptr)
|
44
|
+
end
|
45
|
+
|
46
|
+
def Vector.release(ptr) # @private
|
47
|
+
GSLng.backend.gsl_vector_free(ptr)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Same as Vector.new(n, true)
|
51
|
+
def Vector.zero(n); Vector.new(n, true) end
|
52
|
+
|
53
|
+
# Create a vector from an Array.
|
54
|
+
def Vector.from_array(array)
|
55
|
+
if (array.empty?) then raise "Can't create empty vector" end
|
56
|
+
v = Vector.new(array.size)
|
57
|
+
GSLng.backend.gsl_vector_from_array(v.ptr_value, array)
|
58
|
+
return v
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates a Vector with linearly distributed values between +start+ and +stop+, separated by +delta+.
|
62
|
+
def Vector.linspace(start, stop, delta)
|
63
|
+
if (start > stop || delta <= 0) then raise 'Invalid values' end
|
64
|
+
Vector.new(((stop - start) / delta).floor.to_i + 1) {|i| start + delta * i}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Creates a Vector from an Array or a Range
|
68
|
+
# @see Vector::from_array
|
69
|
+
# @example
|
70
|
+
# Vector[1,2,3]
|
71
|
+
# Vector[1..3]
|
72
|
+
def Vector.[](*args)
|
73
|
+
array = (args.size == 1 && Range === args[0] ? args[0].to_a : args)
|
74
|
+
Vector.from_array(array)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Generates a Vector of n random numbers between 0 and 1.
|
78
|
+
# NOTE: This simply uses {Kernel::rand}
|
79
|
+
def Vector.random(n)
|
80
|
+
Vector.new(n).map!{|x| Kernel::rand}
|
81
|
+
end
|
82
|
+
class << self; alias_method :rand, :random end
|
83
|
+
|
84
|
+
# @group Operators
|
85
|
+
|
86
|
+
# Add (element-by-element) other to self
|
87
|
+
# @return [Vector] self
|
88
|
+
def add!(other)
|
89
|
+
case other
|
90
|
+
when Numeric; @backend.gsl_vector_add_constant(@ptr, other.to_f)
|
91
|
+
when Vector; @backend.gsl_vector_add(@ptr, other.ptr)
|
92
|
+
else
|
93
|
+
x,y = other.coerce(self)
|
94
|
+
x.add!(y)
|
95
|
+
end
|
96
|
+
return self
|
97
|
+
end
|
98
|
+
|
99
|
+
# Substract (element-by-element) other from self
|
100
|
+
# @return [Vector] self
|
101
|
+
def substract!(other)
|
102
|
+
case other
|
103
|
+
when Numeric; @backend.gsl_vector_add_constant(@ptr, -other.to_f)
|
104
|
+
when Vector; @backend.gsl_vector_sub(@ptr, other.ptr)
|
105
|
+
else
|
106
|
+
x,y = other.coerce(self)
|
107
|
+
x.sub!(y)
|
108
|
+
end
|
109
|
+
return self
|
110
|
+
end
|
111
|
+
alias_method :sub!, :substract!
|
112
|
+
|
113
|
+
# Multiply (element-by-element) other with self
|
114
|
+
# @return [Vector] self
|
115
|
+
def multiply!(other)
|
116
|
+
case other
|
117
|
+
when Numeric; @backend.gsl_blas_dscal(other.to_f, @ptr)
|
118
|
+
when Vector; @backend.gsl_vector_mul(@ptr, other.ptr)
|
119
|
+
else
|
120
|
+
x,y = other.coerce(self)
|
121
|
+
x.mul!(y)
|
122
|
+
end
|
123
|
+
return self
|
124
|
+
end
|
125
|
+
alias_method :mul!, :multiply!
|
126
|
+
|
127
|
+
# Divide (element-by-element) self by other
|
128
|
+
# @return [Vector] self
|
129
|
+
def divide!(other)
|
130
|
+
case other
|
131
|
+
when Numeric; @backend.gsl_blas_dscal(1.0 / other, @ptr)
|
132
|
+
when Vector; @backend.gsl_vector_div(@ptr, other.ptr)
|
133
|
+
else
|
134
|
+
x,y = other.coerce(self)
|
135
|
+
x.div!(y)
|
136
|
+
end
|
137
|
+
return self
|
138
|
+
end
|
139
|
+
alias_method :div!, :divide!
|
140
|
+
|
141
|
+
# Element-by-element addition
|
142
|
+
def +(other); self.dup.add!(other) end
|
143
|
+
|
144
|
+
# Element-by-element substraction
|
145
|
+
def -(other); self.dup.sub!(other) end
|
146
|
+
|
147
|
+
# Element-by-element product
|
148
|
+
# @example
|
149
|
+
# Vector[1,2,3] * 2 => [2.0, 4.0, 6.0]:Vector
|
150
|
+
# Vector[1,2,3] * Vector[0,1,2] => [0.0, 2.0, 6.0]:Vector
|
151
|
+
def *(other)
|
152
|
+
case other
|
153
|
+
when Numeric; self.dup.mul!(other)
|
154
|
+
when Vector; self.dup.mul!(other)
|
155
|
+
else
|
156
|
+
x,y = other.coerce(self)
|
157
|
+
x * y
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Element-by-element division
|
162
|
+
def /(other); self.dup.div!(other) end
|
163
|
+
|
164
|
+
# Invert sign on all elements
|
165
|
+
def -@; self.map!(&:-@) end
|
166
|
+
|
167
|
+
# @group Other mathematical operations
|
168
|
+
|
169
|
+
# Dot product between self and other (uses BLAS's ddot)
|
170
|
+
# @return [Float]
|
171
|
+
# @example
|
172
|
+
# Vector[1,2,3] ^ Vector[0,1,2] => 8.0
|
173
|
+
def dot(other)
|
174
|
+
out = FFI::Buffer.new(:double)
|
175
|
+
@backend.gsl_blas_ddot(@ptr, other.ptr, out)
|
176
|
+
return out[0].get_double(0)
|
177
|
+
end
|
178
|
+
alias_method :^, :dot
|
179
|
+
|
180
|
+
# Norm 2 of the vector (uses BLAS's dnrm2)
|
181
|
+
def norm; @backend.gsl_blas_dnrm2(@ptr) end
|
182
|
+
alias_method :length, :norm
|
183
|
+
|
184
|
+
# Returns the sum of all elements (uses BLAS's dasum)
|
185
|
+
def sum; @backend.gsl_blas_dasum(@ptr) end
|
186
|
+
|
187
|
+
# Optimized version of: self += other * alpha (where alpha is a Numeric). Uses BLAS's daxpy.
|
188
|
+
def mul_add(other, alpha); @backend.gsl_blas_daxpy(alpha, other.ptr, @ptr); return self end
|
189
|
+
|
190
|
+
# @group Miscelaneous methods
|
191
|
+
|
192
|
+
# Reverse the order of elements
|
193
|
+
def reverse!; @backend.gsl_vector_reverse(@ptr); return self end
|
194
|
+
|
195
|
+
# Swap the i-th element with the j-th element
|
196
|
+
def swap(i,j); @backend.gsl_vector_swap_elements(@ptr, i, j); return self end
|
197
|
+
|
198
|
+
def sort!; @backend.gsl_sort_vector(@ptr); return self end
|
199
|
+
def sort; self.dup.sort! end
|
200
|
+
|
201
|
+
# Copy other's values into self
|
202
|
+
def copy(other); @backend.gsl_vector_memcpy(@ptr, other.ptr); return self end
|
203
|
+
|
204
|
+
# Wraps self into the interval [0,up_to). NOTE: this value must be > 0
|
205
|
+
# @param [Vector,Numeric] up_to
|
206
|
+
# @return [Vector] a vector of values -1, 1 or 0, if (max-min) was substracted, added to the coordinate,
|
207
|
+
# or not modified, respectively.
|
208
|
+
# @example Assuming that +v = Vector[-8,2,8]+
|
209
|
+
# v.wrap(5) => [1.0 0.0 -1.0]:Vector
|
210
|
+
# v => [-3.0 2.0 3.0]:Vector
|
211
|
+
def wrap!(up_to)
|
212
|
+
delta = Vector.new(self.size)
|
213
|
+
self.map_index! do |i|
|
214
|
+
a,b = self[i].divmod(up_to)
|
215
|
+
delta[i] = -a
|
216
|
+
b
|
217
|
+
end
|
218
|
+
return delta
|
219
|
+
end
|
220
|
+
|
221
|
+
# Compute hash value for this Vector.
|
222
|
+
# Note: this may be a bit inefficient for now
|
223
|
+
def hash
|
224
|
+
self.to_a.hash
|
225
|
+
end
|
226
|
+
|
227
|
+
# @group Setting/getting values
|
228
|
+
|
229
|
+
# Access the i-th element.
|
230
|
+
# If _index_ is negative, it counts from the end (-1 is the last element).
|
231
|
+
# @raise [RuntimeError] if out-of-bounds
|
232
|
+
# @todo support ranges
|
233
|
+
def [](index)
|
234
|
+
@backend.gsl_vector_get_operator(@ptr_value, index)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Set the i-th element.
|
238
|
+
# If _index_ is negative, it counts from the end (-1 is the last element).
|
239
|
+
# @raise [RuntimeError] if out-of-bounds
|
240
|
+
# @todo support ranges
|
241
|
+
def []=(index, value)
|
242
|
+
@backend.gsl_vector_set_operator(@ptr_value, index, value.to_f)
|
243
|
+
#@backend.gsl_vector_set(@ptr, (index < 0 ? @size + index : index), value.to_f)
|
244
|
+
end
|
245
|
+
|
246
|
+
# @group Views
|
247
|
+
|
248
|
+
# Create a {Vector::View} from this Vector.
|
249
|
+
# If _size_ is nil, it is computed automatically from _offset_ and _stride_
|
250
|
+
def view(offset = 0, size = nil, stride = 1)
|
251
|
+
if (stride <= 0) then raise 'stride must be positive' end
|
252
|
+
|
253
|
+
if (size.nil?)
|
254
|
+
size = @size - offset
|
255
|
+
k,m = size.divmod(stride)
|
256
|
+
size = k + (m == 0 ? 0 : 1)
|
257
|
+
end
|
258
|
+
|
259
|
+
if (stride == 1) then ptr = @backend.gsl_vector_subvector2(@ptr, offset, size)
|
260
|
+
else ptr = @backend.gsl_vector_subvector_with_stride2(@ptr, offset, stride, size) end
|
261
|
+
View.new(ptr, self, size, stride)
|
262
|
+
end
|
263
|
+
alias_method :subvector_view, :view
|
264
|
+
|
265
|
+
# Shorthand for #subvector_view(..).to_vector.
|
266
|
+
def subvector(*args); subvector_view(*args).to_vector end
|
267
|
+
|
268
|
+
# Set all values to v
|
269
|
+
def all!(v); @backend.gsl_vector_set_all(@ptr, v); return self end
|
270
|
+
alias_method :set!, :all!
|
271
|
+
alias_method :fill!, :all!
|
272
|
+
|
273
|
+
# Set all values to zero
|
274
|
+
def zero!; @backend.gsl_vector_set_zero(@ptr); return self end
|
275
|
+
|
276
|
+
# Set all values to zero, except the i-th element, which is set to 1
|
277
|
+
def basis!(i); @backend.gsl_vector_set_basis(@ptr, i); return self end
|
278
|
+
|
279
|
+
# @group 2D/3D/4D utility vectors
|
280
|
+
|
281
|
+
# Same as Vector#[0]
|
282
|
+
def x; @backend.gsl_vector_get(@ptr, 0) end
|
283
|
+
# Same as Vector#[1]
|
284
|
+
def y; @backend.gsl_vector_get(@ptr, 1) end
|
285
|
+
# Same as Vector#[2]
|
286
|
+
def z; @backend.gsl_vector_get(@ptr, 2) end
|
287
|
+
# Same as Vector#[3]
|
288
|
+
def w; @backend.gsl_vector_get(@ptr, 3) end
|
289
|
+
|
290
|
+
# Same as Vector#[0]=
|
291
|
+
def x=(v); @backend.gsl_vector_set(@ptr, 0, v.to_f) end
|
292
|
+
# Same as Vector#[1]=
|
293
|
+
def y=(v); @backend.gsl_vector_set(@ptr, 1, v.to_f) end
|
294
|
+
# Same as Vector#[2]=
|
295
|
+
def z=(v); @backend.gsl_vector_set(@ptr, 2, v.to_f) end
|
296
|
+
# Same as Vector#[3]=
|
297
|
+
def w=(v); @backend.gsl_vector_set(@ptr, 3, v.to_f) end
|
298
|
+
|
299
|
+
# @group Predicate methods
|
300
|
+
|
301
|
+
# if all elements are zero
|
302
|
+
def zero?; @backend.gsl_vector_isnull(@ptr) == 1 ? true : false end
|
303
|
+
|
304
|
+
# if all elements are strictly positive (>0)
|
305
|
+
def positive?; @backend.gsl_vector_ispos(@ptr) == 1 ? true : false end
|
306
|
+
|
307
|
+
#if all elements are strictly negative (<0)
|
308
|
+
def negative?; @backend.gsl_vector_isneg(@ptr) == 1 ? true : false end
|
309
|
+
|
310
|
+
# if all elements are non-negative (>=0)
|
311
|
+
def nonnegative?; @backend.gsl_vector_isnonneg(@ptr) == 1 ? true : false end
|
312
|
+
|
313
|
+
# If each element of self is less than other's elements
|
314
|
+
def <(other); (other - self).positive? end
|
315
|
+
|
316
|
+
# If each element of self is greater than other's elements
|
317
|
+
def >(other); (other - self).negative? end
|
318
|
+
|
319
|
+
# If each element of self is less-or-equal than other's elements
|
320
|
+
def <=(other); (other - self).nonnegative? end
|
321
|
+
|
322
|
+
# If each element of self is less-or-equal than other's elements
|
323
|
+
def >=(other); (self - other).nonnegative? end
|
324
|
+
|
325
|
+
# @group Minimum/Maximum
|
326
|
+
|
327
|
+
# Return maximum element of vector
|
328
|
+
def max; @backend.gsl_vector_max(@ptr) end
|
329
|
+
|
330
|
+
# Return minimum element of vector
|
331
|
+
def min; @backend.gsl_vector_min(@ptr) end
|
332
|
+
|
333
|
+
# Same as {Array#minmax}
|
334
|
+
def minmax
|
335
|
+
min = FFI::Buffer.new(:double)
|
336
|
+
max = FFI::Buffer.new(:double)
|
337
|
+
@backend.gsl_vector_minmax(@ptr, min, max)
|
338
|
+
return [min[0].get_float64(0),max[0].get_float64(0)]
|
339
|
+
end
|
340
|
+
|
341
|
+
# Same as {#minmax}, but returns the indices to the elements
|
342
|
+
def minmax_index
|
343
|
+
min = FFI::Buffer.new(:size_t)
|
344
|
+
max = FFI::Buffer.new(:size_t)
|
345
|
+
@backend.gsl_vector_minmax_index(@ptr, min, max)
|
346
|
+
#return [min[0].get_size_t(0),max[0].get_size_t(0)]
|
347
|
+
return [min[0].get_ulong(0),max[0].get_ulong(0)]
|
348
|
+
end
|
349
|
+
|
350
|
+
# Same as {#min}, but returns the index to the element
|
351
|
+
def min_index; @backend.gsl_vector_min_index(@ptr) end
|
352
|
+
|
353
|
+
# Same as {#max}, but returns the index to the element
|
354
|
+
def max_index; @backend.gsl_vector_max_index(@ptr) end
|
355
|
+
|
356
|
+
# @group Statistics
|
357
|
+
|
358
|
+
# Compute the mean of the vector
|
359
|
+
def mean; @backend.gsl_stats_mean(self.as_array, self.stride, self.size) end
|
360
|
+
|
361
|
+
# Compute the median of the vector
|
362
|
+
# *Note* it assumes sorted data!
|
363
|
+
def median; @backend.gsl_stats_median_from_sorted_data(self.as_array, self.stride, self.size) end
|
364
|
+
|
365
|
+
# Compute the median of the vector
|
366
|
+
# *Note* it assumes sorted data!
|
367
|
+
# @param [Float] f A number between 0 and 1 indicating the percentile
|
368
|
+
def quantile(f); @backend.gsl_stats_quantile_from_sorted_data(self.as_array, self.stride, self.size, f) end
|
369
|
+
|
370
|
+
# Compute the variance of the vector
|
371
|
+
# @param [Float] mean Optionally supply the mean if you already computed it previously with {self#mean}
|
372
|
+
# @param [Boolean] fixed_mean If true, the passed mean is taken to be known a priori (see GSL documentation)
|
373
|
+
def variance(mean = nil, fixed_mean = false)
|
374
|
+
if (mean.nil?) then @backend.gsl_stats_variance(self.as_array, self.stride, self.size)
|
375
|
+
else
|
376
|
+
if (fixed_mean) then @backend.gsl_stats_variance_with_fixed_mean(self.as_array, self.stride, self.size, mean)
|
377
|
+
else @backend.gsl_stats_variance_m(self.as_array, self.stride, self.size, mean) end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# Compute the standard deviation of the vector
|
382
|
+
# @see #variance
|
383
|
+
def standard_deviation(mean = nil, fixed_mean = false)
|
384
|
+
if (mean.nil?) then @backend.gsl_stats_sd(self.as_array, self.stride, self.size)
|
385
|
+
else
|
386
|
+
if (fixed_mean) then @backend.gsl_stats_sd_with_fixed_mean(self.as_array, self.stride, self.size, mean)
|
387
|
+
else @backend.gsl_stats_sd_m(self.as_array, self.stride, self.size, mean) end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Compute the total sum of squares of the vector
|
392
|
+
# @see #variance
|
393
|
+
def total_sum_squares(mean = nil)
|
394
|
+
if (mean.nil?) then @backend.gsl_stats_tss(self.as_array, self.stride, self.size)
|
395
|
+
else @backend.gsl_stats_tss_m(self.as_array, self.stride, self.size, mean) end
|
396
|
+
end
|
397
|
+
|
398
|
+
# Compute the absolute deviation of the vector
|
399
|
+
# @see #variance
|
400
|
+
def absolute_deviation(mean = nil)
|
401
|
+
if (mean.nil?) then @backend.gsl_stats_absdev(self.as_array, self.stride, self.size)
|
402
|
+
else @backend.gsl_stats_absdev_m(self.as_array, self.stride, self.size, mean) end
|
403
|
+
end
|
404
|
+
|
405
|
+
# Compute the skewness of the vector. You can optionally provide the mean *and* the standard deviation if you already computed them
|
406
|
+
def skew(mean = nil, sd = nil)
|
407
|
+
if (mean.nil? || sd.nil?) then @backend.gsl_stats_skew(self.as_array, self.stride, self.size)
|
408
|
+
else @backend.gsl_stats_skew_sd_m(self.as_array, self.stride, self.size, mean, sd) end
|
409
|
+
end
|
410
|
+
|
411
|
+
# Compute the kurtosis of the vector
|
412
|
+
# @see #skew
|
413
|
+
def kurtosis(mean = nil, sd = nil)
|
414
|
+
if (mean.nil? || sd.nil?) then @backend.gsl_stats_kurtosis(self.as_array, self.stride, self.size)
|
415
|
+
else @backend.gsl_stats_kurtosis_sd_m(self.as_array, self.stride, self.size, mean, sd) end
|
416
|
+
end
|
417
|
+
|
418
|
+
# Compute the autocorrelation of the vector
|
419
|
+
# @see #variance
|
420
|
+
def autocorrelation(mean = nil)
|
421
|
+
if (mean.nil?) then @backend.gsl_stats_lag1_autocorrelation(self.as_array, self.stride, self.size)
|
422
|
+
else @backend.gsl_stats_lag1_autocorrelation(self.as_array, self.stride, self.size, mean) end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Compute the covariance between self and other. You can optionally pass the mean of both vectors if you already computed them
|
426
|
+
# @see #variance
|
427
|
+
def covariance(other, mean1 = nil, mean2 = nil)
|
428
|
+
if (mean1.nil? || mean2.nil?) then @backend.gsl_stats_covariance(self.as_array, self.stride, other.as_array, other.stride, self.size)
|
429
|
+
else @backend.gsl_stats_covariance(self.as_array, self.stride, other.as_array, other.stride, self.size, mean1, mean2) end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Compute the correlation between self and other
|
433
|
+
def correlation(other)
|
434
|
+
@backend.gsl_stats_correlation(self.as_array, self.stride, other.as_array, other.stride, self.size)
|
435
|
+
end
|
436
|
+
|
437
|
+
# @group High-order methods
|
438
|
+
|
439
|
+
# @yield [elem]
|
440
|
+
def each(block = Proc.new)
|
441
|
+
@backend.gsl_vector_each(@ptr_value, &block)
|
442
|
+
end
|
443
|
+
|
444
|
+
# @see #each
|
445
|
+
# @yield [elem,i]
|
446
|
+
def each_with_index(block = Proc.new)
|
447
|
+
@backend.gsl_vector_each_with_index(@ptr_value, &block)
|
448
|
+
end
|
449
|
+
|
450
|
+
# @see #map
|
451
|
+
def map!(block = Proc.new); @backend.gsl_vector_map!(@ptr_value, &block); return self end
|
452
|
+
|
453
|
+
# Similar to {#map!}, but passes the index to the element instead.
|
454
|
+
# @yield [i]
|
455
|
+
def map_index!(block = Proc.new); @backend.gsl_vector_map_index!(@ptr_value, &block); return self end
|
456
|
+
|
457
|
+
# @return [Vector]
|
458
|
+
# @see #map_index!
|
459
|
+
# @yield [i]
|
460
|
+
def map_index(block = Proc.new); self.dup.map_index!(block) end
|
461
|
+
|
462
|
+
alias_method :map_old, :map
|
463
|
+
|
464
|
+
# Acts like the normal 'map' method from Enumerator
|
465
|
+
# @return [Array]
|
466
|
+
# @see #map
|
467
|
+
# @yield [i]
|
468
|
+
def map_array(block = Proc.new); self.map_old(&block); end
|
469
|
+
|
470
|
+
# @return [Vector]
|
471
|
+
# @yield [elem]
|
472
|
+
def map(block = Proc.new); self.dup.map!(block) end
|
473
|
+
|
474
|
+
# @group Type conversions
|
475
|
+
|
476
|
+
# @see Array#join
|
477
|
+
# @return [String]
|
478
|
+
def join(sep = $,)
|
479
|
+
s = ''
|
480
|
+
self.each do |e|
|
481
|
+
s += (s.empty?() ? e.to_s : "#{sep}#{e}")
|
482
|
+
end
|
483
|
+
return s
|
484
|
+
end
|
485
|
+
|
486
|
+
# Coerces _other_ to be a Vector.
|
487
|
+
# @example
|
488
|
+
# Vector[1,2].coerce(5) => [[5.0, 5.0]:Vector, [1.0, 2.0]:Vector]
|
489
|
+
def coerce(other)
|
490
|
+
case other
|
491
|
+
when Vector
|
492
|
+
[ other, self ]
|
493
|
+
when Numeric
|
494
|
+
[ Vector.new(@size).set!(other), self ]
|
495
|
+
else
|
496
|
+
raise TypeError, "Can't coerce #{other.class} into #{self.class}"
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# @return [String] same format as {Array#to_s}
|
501
|
+
# @example
|
502
|
+
# Vector[1,2,3].to_s => "[1.0, 2.0, 3.0]"
|
503
|
+
def to_s
|
504
|
+
"[" + self.join(', ') + "]"
|
505
|
+
end
|
506
|
+
|
507
|
+
def inspect # @private
|
508
|
+
"#{self}:Vector"
|
509
|
+
end
|
510
|
+
|
511
|
+
# @return [Array]
|
512
|
+
def to_a
|
513
|
+
@backend.gsl_vector_to_a(@ptr_value)
|
514
|
+
end
|
515
|
+
|
516
|
+
def as_array # @private
|
517
|
+
@backend.gsl_vector_as_array(@ptr)
|
518
|
+
end
|
519
|
+
|
520
|
+
# Create a row matrix from this vector
|
521
|
+
# @return [Matrix]
|
522
|
+
def to_matrix
|
523
|
+
m = Matrix.new(1, @size)
|
524
|
+
@backend.gsl_matrix_set_row(m.ptr, 0, @ptr)
|
525
|
+
return m
|
526
|
+
end
|
527
|
+
alias_method :to_row, :to_matrix
|
528
|
+
|
529
|
+
# Create a column matrix from this vector
|
530
|
+
# @return [Matrix]
|
531
|
+
def transpose
|
532
|
+
m = Matrix.new(@size, 1)
|
533
|
+
@backend.gsl_matrix_set_col(m.ptr, 0, @ptr)
|
534
|
+
return m
|
535
|
+
end
|
536
|
+
alias_method :to_column, :transpose
|
537
|
+
|
538
|
+
# @group Equality test
|
539
|
+
|
540
|
+
# Element-by-element comparison. Admits comparing to Array.
|
541
|
+
def ==(other)
|
542
|
+
if (self.size != other.size) then return false end
|
543
|
+
self.each_with_index do |elem,i|
|
544
|
+
if (elem != other[i]) then return false end
|
545
|
+
end
|
546
|
+
return true
|
547
|
+
end
|
548
|
+
|
549
|
+
def eql?(other)
|
550
|
+
@backend.gsl_vector_eql?(@ptr_value, other.ptr_value)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|