fast_matrix 0.1.4 → 0.1.5

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