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
@@ -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
|