fast_matrix 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,9 +3,8 @@
3
3
 
4
4
  #include "ruby.h"
5
5
 
6
- static VALUE matrix_eTypeError;
7
- static VALUE matrix_eIndexError;
8
- static VALUE cMatrix;
6
+ extern VALUE cMatrix;
7
+ extern const rb_data_type_t matrix_type;
9
8
 
10
9
  // matrix
11
10
  // m --->
@@ -22,22 +21,8 @@ struct matrix
22
21
  double* data;
23
22
  };
24
23
 
25
- void matrix_free(void* data);
26
- size_t matrix_size(const void* data);
27
-
28
- static const rb_data_type_t matrix_type =
29
- {
30
- .wrap_struct_name = "matrix",
31
- .function =
32
- {
33
- .dmark = NULL,
34
- .dfree = matrix_free,
35
- .dsize = matrix_size,
36
- },
37
- .data = NULL,
38
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
39
- };
24
+ void c_matrix_init(struct matrix* mtr, int m, int n);
40
25
 
41
26
  void init_fm_matrix();
42
27
 
43
- #endif /* FAST_MATRIX_MATRIX_H */
28
+ #endif /* FAST_MATRIX_MATRIX_H */
@@ -0,0 +1,273 @@
1
+ #include "vector.h"
2
+ #include "c_array_operations.h"
3
+ #include "errors.h"
4
+ #include "matrix.h"
5
+
6
+ VALUE cVector;
7
+
8
+ void vector_free(void* data);
9
+ size_t vector_size(const void* data);
10
+
11
+ const rb_data_type_t vector_type =
12
+ {
13
+ .wrap_struct_name = "vector",
14
+ .function =
15
+ {
16
+ .dmark = NULL,
17
+ .dfree = vector_free,
18
+ .dsize = vector_size,
19
+ },
20
+ .data = NULL,
21
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
22
+ };
23
+
24
+ void vector_free(void* data)
25
+ {
26
+ free(((*(struct vector*)data)).data);
27
+ free(data);
28
+ }
29
+
30
+ size_t vector_size(const void* data)
31
+ {
32
+ return sizeof(struct vector);
33
+ }
34
+
35
+ VALUE vector_alloc(VALUE self)
36
+ {
37
+ struct vector* vct = malloc(sizeof(struct vector));
38
+ vct->data = NULL;
39
+ return TypedData_Wrap_Struct(self, &vector_type, vct);
40
+ }
41
+
42
+ void c_vector_init(struct vector* vect, int n)
43
+ {
44
+ vect->n = n;
45
+ vect->data = malloc(n * sizeof(double));
46
+ }
47
+
48
+ VALUE vector_initialize(VALUE self, VALUE size)
49
+ {
50
+ struct vector* data;
51
+ int n = raise_rb_value_to_int(size);
52
+
53
+ if(n <= 0)
54
+ rb_raise(fm_eIndexError, "Size cannot be negative or zero");
55
+
56
+ TypedData_Get_Struct(self, struct vector, &vector_type, data);
57
+
58
+ c_vector_init(data, n);
59
+
60
+ return self;
61
+ }
62
+
63
+ // []=
64
+ VALUE vector_set(VALUE self, VALUE idx, VALUE v)
65
+ {
66
+ int i = raise_rb_value_to_int(idx);
67
+ double x = raise_rb_value_to_double(v);
68
+
69
+ struct vector* data;
70
+ TypedData_Get_Struct(self, struct vector, &vector_type, data);
71
+
72
+ raise_check_range(i, 0, data->n);
73
+
74
+ data->data[i] = x;
75
+ return v;
76
+ }
77
+
78
+ // []
79
+ VALUE vector_get(VALUE self, VALUE idx)
80
+ {
81
+ int i = raise_rb_value_to_int(idx);
82
+
83
+ struct vector* data;
84
+ TypedData_Get_Struct(self, struct vector, &vector_type, data);
85
+
86
+ raise_check_range(i, 0, data->n);
87
+
88
+ return DBL2NUM(data->data[i]);
89
+ }
90
+
91
+ VALUE c_vector_size(VALUE self)
92
+ {
93
+ struct vector* data;
94
+ TypedData_Get_Struct(self, struct vector, &vector_type, data);
95
+ return INT2NUM(data->n);
96
+ }
97
+
98
+
99
+ VALUE vector_add_with(VALUE self, VALUE value)
100
+ {
101
+ struct vector* A;
102
+ struct vector* B;
103
+ TypedData_Get_Struct(self, struct vector, &vector_type, A);
104
+ TypedData_Get_Struct(value, struct vector, &vector_type, B);
105
+
106
+ if(A->n != B->n)
107
+ rb_raise(fm_eIndexError, "Different sizes matrices");
108
+
109
+ int n = A->n;
110
+
111
+ struct vector* C;
112
+ VALUE result = TypedData_Make_Struct(cVector, struct vector, &vector_type, C);
113
+
114
+ c_vector_init(C, n);
115
+ add_d_arrays_to_result(n, A->data, B->data, C->data);
116
+
117
+ return result;
118
+ }
119
+
120
+
121
+ VALUE vector_add_from(VALUE self, VALUE value)
122
+ {
123
+ struct vector* A;
124
+ struct vector* B;
125
+ TypedData_Get_Struct(self, struct vector, &vector_type, A);
126
+ TypedData_Get_Struct(value, struct vector, &vector_type, B);
127
+
128
+ if(A->n != B->n)
129
+ rb_raise(fm_eIndexError, "Different sizes matrices");
130
+
131
+ int n = A->n;
132
+
133
+ add_d_arrays_to_first(n, A->data, B->data);
134
+
135
+ return self;
136
+ }
137
+
138
+ VALUE vector_equal(VALUE self, VALUE value)
139
+ {
140
+ struct vector* A;
141
+ struct vector* B;
142
+ TypedData_Get_Struct(self, struct vector, &vector_type, A);
143
+ TypedData_Get_Struct(value, struct vector, &vector_type, B);
144
+
145
+ if(A->n != B->n)
146
+ return Qfalse;
147
+
148
+ int n = A->n;
149
+
150
+ if(equal_d_arrays(n, A->data, B->data))
151
+ return Qtrue;
152
+ return Qfalse;
153
+ }
154
+
155
+ VALUE vector_copy(VALUE v)
156
+ {
157
+ struct vector* V;
158
+ TypedData_Get_Struct(v, struct vector, &vector_type, V);
159
+
160
+ struct vector* R;
161
+ VALUE result = TypedData_Make_Struct(cVector, struct vector, &vector_type, R);
162
+
163
+ c_vector_init(R, V->n);
164
+ copy_d_array(R->n, V->data, R->data);
165
+
166
+ return result;
167
+ }
168
+
169
+ // V - vector n
170
+ // M - matrix m x 1
171
+ // R - matrix m x n
172
+ void c_vector_matrix_multiply(int n, int m, const double* V, const double* M, double* R)
173
+ {
174
+ fill_d_array(m * n, R, 0);
175
+
176
+ for(int j = 0; j < n; ++j)
177
+ {
178
+ double* p_r = R + m * j;
179
+ double d_v = V[j];
180
+
181
+ for(int i = 0; i < m; ++i)
182
+ p_r[i] += d_v * M[i];
183
+ }
184
+ }
185
+
186
+ VALUE vector_multiply_vm(VALUE self, VALUE other)
187
+ {
188
+ struct vector* V;
189
+ struct matrix* M;
190
+ TypedData_Get_Struct(self, struct vector, &vector_type, V);
191
+ TypedData_Get_Struct(other, struct matrix, &matrix_type, M);
192
+
193
+ if(M->n != 1)
194
+ rb_raise(fm_eIndexError, "Number of rows must be 1");
195
+
196
+ int m = M->m;
197
+ int n = V->n;
198
+
199
+ struct matrix* R;
200
+ VALUE result = TypedData_Make_Struct(cMatrix, struct matrix, &matrix_type, R);
201
+
202
+ c_matrix_init(R, m, n);
203
+ c_vector_matrix_multiply(n, m, V->data, M->data, R->data);
204
+
205
+ return result;
206
+ }
207
+
208
+ VALUE vector_multiply_vn(VALUE self, VALUE value)
209
+ {
210
+ struct vector* A;
211
+ TypedData_Get_Struct(self, struct vector, &vector_type, A);
212
+
213
+ double d = NUM2DBL(value);
214
+
215
+ struct vector* R;
216
+ VALUE result = TypedData_Make_Struct(cVector, struct vector, &vector_type, R);
217
+
218
+ c_vector_init(R, A->n);
219
+ copy_d_array(R->n, A->data, R->data);
220
+ multiply_d_array(R->n, R->data, d);
221
+
222
+ return result;
223
+ }
224
+
225
+ VALUE vector_multiply_vv(VALUE self, VALUE other)
226
+ {
227
+ struct vector* A;
228
+ struct vector* B;
229
+ TypedData_Get_Struct(self, struct vector, &vector_type, A);
230
+ TypedData_Get_Struct(other, struct vector, &vector_type, B);
231
+
232
+ if(B->n != 1)
233
+ rb_raise(fm_eIndexError, "Length of vector must be equal to 1");
234
+
235
+ struct vector* R;
236
+ VALUE result = TypedData_Make_Struct(cVector, struct vector, &vector_type, R);
237
+
238
+ c_vector_init(R, A->n);
239
+ copy_d_array(A->n, A->data, R->data);
240
+ multiply_d_array(R->n, R->data, B->data[0]);
241
+
242
+ return result;
243
+ }
244
+
245
+ VALUE vector_multiply(VALUE self, VALUE v)
246
+ {
247
+ if(RB_FLOAT_TYPE_P(v) || FIXNUM_P(v)
248
+ || RB_TYPE_P(v, T_BIGNUM))
249
+ return vector_multiply_vn(self, v);
250
+ if(RBASIC_CLASS(v) == cMatrix)
251
+ return vector_multiply_vm(self, v);
252
+ if(RBASIC_CLASS(v) == cVector)
253
+ return vector_multiply_vv(self, v);
254
+ rb_raise(fm_eTypeError, "Invalid klass for multiply");
255
+ }
256
+
257
+ void init_fm_vector()
258
+ {
259
+ VALUE mod = rb_define_module("FastMatrix");
260
+ cVector = rb_define_class_under(mod, "Vector", rb_cData);
261
+
262
+ rb_define_alloc_func(cVector, vector_alloc);
263
+
264
+ rb_define_method(cVector, "initialize", vector_initialize, 1);
265
+ rb_define_method(cVector, "[]", vector_get, 1);
266
+ rb_define_method(cVector, "[]=", vector_set, 2);
267
+ rb_define_method(cVector, "size", c_vector_size, 0);
268
+ rb_define_method(cVector, "+", vector_add_with, 1);
269
+ rb_define_method(cVector, "+=", vector_add_from, 1);
270
+ rb_define_method(cVector, "==", vector_equal, 1);
271
+ rb_define_method(cVector, "clone", vector_copy, 0);
272
+ rb_define_method(cVector, "*", vector_multiply, 1);
273
+ }
@@ -0,0 +1,20 @@
1
+ #ifndef FAST_MATRIX_VECTOR_H
2
+ #define FAST_MATRIX_VECTOR_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ extern VALUE cVector;
7
+ extern const rb_data_type_t vector_type;
8
+
9
+ // vector
10
+ struct vector
11
+ {
12
+ int n;
13
+ double* data;
14
+ };
15
+
16
+ void c_vector_init(struct vector* vect, int n);
17
+
18
+ void init_fm_vector();
19
+
20
+ #endif /* FAST_MATRIX_VECTOR_H */
data/lib/errors.rb ADDED
@@ -0,0 +1,9 @@
1
+ module FastMatrix
2
+ # From C:
3
+ # TypeError
4
+ # IndexError
5
+
6
+ class Error < StandardError; end
7
+ class NotSupportedError < NotImplementedError; end
8
+
9
+ end
data/lib/fast_matrix.rb CHANGED
@@ -1,72 +1,3 @@
1
- require "fast_matrix/version"
2
- require "fast_matrix/fast_matrix"
3
- require "constructors"
4
-
5
- module FastMatrix
6
- class Error < StandardError; end
7
-
8
- # Matrix with fast implementations of + - * determinate in C
9
- class Matrix
10
-
11
- # Aliases just for compatibility with standard matrix
12
- #
13
- # Returns the number of rows.
14
- #
15
- alias_method :row_size, :row_count
16
- #
17
- # Returns the number of columns.
18
- #
19
- alias_method :column_size, :column_count
20
-
21
- #
22
- # Create fast matrix from standard matrix
23
- #
24
- def self.convert(matrix)
25
- fast_matrix = Matrix.new(matrix.row_count, matrix.column_count)
26
- (0...matrix.row_count).each do |i|
27
- (0...matrix.column_count).each do |j|
28
- fast_matrix[i, j] = matrix[i, j]
29
- end
30
- end
31
- fast_matrix
32
- end
33
-
34
- def each_with_index
35
- (0...row_count).each do |i|
36
- (0...column_count).each do |j|
37
- yield self[i, j], i, j
38
- end
39
- end
40
- end
41
-
42
- def each_with_index!
43
- (0...row_count).each do |i|
44
- (0...column_count).each do |j|
45
- self[i, j] = yield self[i, j], i, j
46
- end
47
- end
48
- self
49
- end
50
-
51
- #
52
- # Convert to standard ruby matrix.
53
- #
54
- def convert
55
- ::Matrix.build(row_size, column_size) { |i, j| self[i, j] }
56
- end
57
-
58
-
59
- # FIXME for compare with standard matrix
60
- def ==(other)
61
- # TODO check class and use fast compare from C if possibly
62
- return false unless %i[row_size column_size \[\]].all? { |x| other.respond_to? x }
63
- return false unless self.row_size == other.row_size && self.column_size == other.column_size
64
-
65
- result = true
66
- each_with_index do |elem, i, j|
67
- result &&= elem == other[i, j].to_f
68
- end
69
- result
70
- end
71
- end
72
- end
1
+ require 'fast_matrix/version'
2
+ require 'vector/vector'
3
+ require 'matrix/matrix'
@@ -1,3 +1,3 @@
1
1
  module FastMatrix
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -0,0 +1,253 @@
1
+ require 'fast_matrix/fast_matrix'
2
+ require 'errors'
3
+
4
+ module FastMatrix
5
+ #
6
+ # Constructors as in the standard matrix
7
+ #
8
+ class Matrix
9
+
10
+ #
11
+ # Creates a matrix of size +row_count+ x +column_count+.
12
+ # It fills the values by calling the given block,
13
+ # passing the current row and column.
14
+ # Returns random matrix if no block is given.
15
+ #
16
+ # m = Matrix.build(2, 4) {|row, col| col - row }
17
+ # => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]
18
+ # m = Matrix.build(3) { rand }
19
+ # => a 3x3 matrix with random elements
20
+ #
21
+ def self.build(row_count, column_count = row_count, &block)
22
+ check_dimensions(row_count, column_count)
23
+ matrix = self.new(row_count, column_count)
24
+ matrix.each_with_index! { |_, i, j| block.call(i, j) } if block_given?
25
+ matrix
26
+ end
27
+
28
+ #
29
+ # Creates a matrix where +rows+ is an array of arrays, each of which is a row
30
+ # of the matrix.
31
+ # The optional argument +copy+ exists only for compatibility with standard.
32
+ # The optional argument +copy+ cannot be false, unlike standard.
33
+ # Matrix.rows([[25, 93], [-1, 66]])
34
+ # => 25 93
35
+ # -1 66
36
+ #
37
+ def self.rows(rows, copy = true)
38
+ check_flag_copy(copy)
39
+ lines(rows, true)
40
+ end
41
+
42
+ #
43
+ # Creates a matrix using +columns+ as an array of column vectors.
44
+ # Matrix.columns([[25, 93], [-1, 66]])
45
+ # => 25 -1
46
+ # 93 66
47
+ #
48
+ def self.columns(columns)
49
+ lines(columns, false)
50
+ end
51
+
52
+ #
53
+ # Creates a matrix where each argument is a row.
54
+ # Matrix[ [25, 93], [-1, 66] ]
55
+ # => 25 93
56
+ # -1 66
57
+ #
58
+ def self.[](*rows)
59
+ self.rows(rows)
60
+ end
61
+
62
+ #
63
+ # Creates a single-column matrix where the values of that column are as given
64
+ # in +column+.
65
+ # Matrix.column_vector([4,5,6])
66
+ # => 4
67
+ # 5
68
+ # 6
69
+ #
70
+ def self.column_vector(column)
71
+ matrix = Matrix.build(column.size, 1)
72
+ column.each_with_index { |elem, i| matrix[i, 0] = elem }
73
+ matrix
74
+ end
75
+
76
+ #
77
+ # Creates a single-row matrix where the values of that row are as given in
78
+ # +row+.
79
+ # Matrix.row_vector([4,5,6])
80
+ # => 4 5 6
81
+ #
82
+ def self.row_vector(row)
83
+ matrix = Matrix.build(1, row.size)
84
+ row.each_with_index { |elem, j| matrix[0, j] = elem }
85
+ matrix
86
+ end
87
+
88
+ #
89
+ # Creates a matrix where the diagonal elements are composed of +values+.
90
+ # Matrix.diagonal(9, 5, -3)
91
+ # => 9 0 0
92
+ # 0 5 0
93
+ # 0 0 -3
94
+ #
95
+ def self.diagonal(*values)
96
+ matrix = Matrix.build(values.size, values.size) { |i, j| i == j ? values[i] : 0 }
97
+ matrix
98
+ end
99
+
100
+ #
101
+ # Creates an +n+ by +n+ diagonal matrix where each diagonal element is
102
+ # +value+.
103
+ # Matrix.scalar(2, 5)
104
+ # => 5 0
105
+ # 0 5
106
+ #
107
+ def self.scalar(n, value)
108
+ Matrix.build(n, n) { |i, j| i == j ? value : 0 }
109
+ end
110
+
111
+ #
112
+ # Creates an +n+ by +n+ identity matrix.
113
+ # Matrix.identity(2)
114
+ # => 1 0
115
+ # 0 1
116
+ #
117
+ def self.identity(n)
118
+ scalar(n, 1)
119
+ end
120
+ class << Matrix
121
+ alias unit identity
122
+ alias I identity
123
+ end
124
+
125
+ #
126
+ # Creates a zero matrix +n+ by +n+.
127
+ # Matrix.zero(2)
128
+ # => 0 0
129
+ # 0 0
130
+ #
131
+ def self.zero(n)
132
+ Matrix.build(n, n) { 0 }
133
+ end
134
+
135
+ #
136
+ # Empty matrices does not supported
137
+ #
138
+ def self.empty(_ = 0, _ = 0)
139
+ raise NotSupportedError, 'Empty matrices does not supported'
140
+ end
141
+
142
+ #
143
+ # Create a matrix by stacking matrices vertically
144
+ #
145
+ # x = Matrix[[1, 2], [3, 4]]
146
+ # y = Matrix[[5, 6], [7, 8]]
147
+ # Matrix.vstack(x, y) # => Matrix[[1, 2], [3, 4], [5, 6], [7, 8]]
148
+ # TODO: optimize (maybe in C)
149
+ def self.vstack(x, *matrices)
150
+ column_count = x.column_count
151
+ row_count = x.row_count
152
+ matrices.each do |matrix|
153
+ raise IndexError unless matrix.column_count == column_count
154
+
155
+ row_count += matrix.row_count
156
+ end
157
+ result = new(row_count, column_count)
158
+ m_i = 0
159
+ [x, *matrices].each do |matrix|
160
+ matrix.each_with_index do |elem, i, j|
161
+ result[m_i + i, j] = elem
162
+ end
163
+ m_i += matrix.row_count
164
+ end
165
+ result
166
+ end
167
+
168
+ #
169
+ # Create a matrix by stacking matrices horizontally
170
+ #
171
+ # x = Matrix[[1, 2], [3, 4]]
172
+ # y = Matrix[[5, 6], [7, 8]]
173
+ # Matrix.hstack(x, y) # => Matrix[[1, 2, 5, 6], [3, 4, 7, 8]]
174
+ #
175
+ def self.hstack(x, *matrices)
176
+ column_count = x.column_count
177
+ row_count = x.row_count
178
+ matrices.each do |matrix|
179
+ raise IndexError unless matrix.row_count == row_count
180
+
181
+ column_count += matrix.column_count
182
+ end
183
+ result = new(row_count, column_count)
184
+ m_j = 0
185
+ [x, *matrices].each do |matrix|
186
+ matrix.each_with_index do |elem, i, j|
187
+ result[i, m_j + j] = elem
188
+ end
189
+ m_j += matrix.column_count
190
+ end
191
+ result
192
+ end
193
+
194
+ #
195
+ # Create a matrix by combining matrices entrywise, using the given block
196
+ #
197
+ # x = Matrix[[6, 6], [4, 4]]
198
+ # y = Matrix[[1, 2], [3, 4]]
199
+ # Matrix.combine(x, y) {|a, b| a - b} # => Matrix[[5, 4], [1, 0]]
200
+ # TODO: optimize in C
201
+ def self.combine(*matrices)
202
+ return Matrix.empty if matrices.empty?
203
+
204
+ result = convert(matrices.first)
205
+ matrices[1..matrices.size].each do |m|
206
+ raise IndexError unless result.row_count == m.row_count &&
207
+ result.column_count == m.column_count
208
+
209
+ result.each_with_index! { |elem, i, j| yield elem, m[i, j] }
210
+ end
211
+ result
212
+ end
213
+
214
+ class << Matrix
215
+ private
216
+
217
+ def check_empty_matrix(row_count, column_count)
218
+ empty if row_count.zero? || column_count.zero?
219
+ end
220
+
221
+ def check_negative_sizes(row_count, column_count)
222
+ raise IndexError if column_count.negative? || row_count.negative?
223
+ end
224
+
225
+ def check_dimensions(row_count, column_count)
226
+ check_empty_matrix(row_count, column_count)
227
+ check_negative_sizes(row_count, column_count)
228
+ end
229
+
230
+ def check_flag_copy(copy)
231
+ unless copy
232
+ raise NotSupportedError, "Can't create matrix without copy elements"
233
+ end
234
+ end
235
+
236
+ # generalization between ::rows and ::columns
237
+ def lines(lines, main_is_rows)
238
+ sizes = [lines.size, (lines[0] || []).size]
239
+ offset = main_is_rows ? 0 : -1
240
+ matrix = build(sizes[offset], sizes[1 + offset])
241
+ lines.each_with_index do |second_dim, main_index|
242
+ raise IndexError if second_dim.size != sizes[1]
243
+
244
+ second_dim.each_with_index do |elem, second_index|
245
+ indices = [main_index, second_index]
246
+ matrix[indices[offset], indices[1 + offset]] = elem
247
+ end
248
+ end
249
+ matrix
250
+ end
251
+ end
252
+ end
253
+ end