Class: FastMatrix::Matrix

Inherits:
Data
  • Object
show all
Defined in:
lib/matrix/matrix.rb,
lib/scalar.rb,
lib/matrix/constructors.rb,
ext/fast_matrix/matrix.c

Overview

Constructors as in the standard matrix

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rows_count, columns_count) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'ext/fast_matrix/matrix.c', line 49

VALUE matrix_initialize(VALUE self, VALUE rows_count, VALUE columns_count)
{
  struct matrix* data;
    int m = raise_rb_value_to_int(columns_count);
    int n = raise_rb_value_to_int(rows_count);

    if(m <= 0 || n <= 0)
        rb_raise(fm_eIndexError, "Size cannot be negative or zero");

  TypedData_Get_Struct(self, struct matrix, &matrix_type, data);

    c_matrix_init(data, m, n);

  return self;
}

Class Method Details

.[](*rows) ⇒ Object

Creates a matrix where each argument is a row.

Matrix[ [25, 93], [-1, 66] ]
   =>  25 93
       -1 66


57
58
59
# File 'lib/matrix/constructors.rb', line 57

def self.[](*rows)
  self.rows(rows)
end

.build(row_count, column_count = row_count, &block) ⇒ Object

Creates a matrix of size row_count x column_count. It fills the values by calling the given block, passing the current row and column. Returns random matrix if no block is given.

m = Matrix.build(2, 4) {|row, col| col - row }
  => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]
m = Matrix.build(3) { rand }
  => a 3x3 matrix with random elements

Raises:

  • (NotImplementedError)


20
21
22
23
24
25
# File 'lib/matrix/constructors.rb', line 20

def self.build(row_count, column_count = row_count, &block)
  matrix = create_with_check(row_count, column_count)
  raise NotImplementedError, 'Issue#17' unless block_given?

  matrix.each_with_index! { |_, i, j| block.call(i, j) }
end

.column_vector(column) ⇒ Object

Creates a single-column matrix where the values of that column are as given in column.

Matrix.column_vector([4,5,6])
  => 4
     5
     6


69
70
71
72
73
# File 'lib/matrix/constructors.rb', line 69

def self.column_vector(column)
  matrix = create_with_check(column.size, 1)
  column.each_with_index { |elem, i| matrix[i, 0] = elem }
  matrix
end

.columns(columns) ⇒ Object

Creates a matrix using columns as an array of column vectors.

Matrix.columns([[25, 93], [-1, 66]])
   =>  25 -1
       93 66


47
48
49
# File 'lib/matrix/constructors.rb', line 47

def self.columns(columns)
  lines(columns, false)
end

.combine(*matrices) ⇒ Object

Create a matrix by combining matrices entrywise, using the given block

x = Matrix[[6, 6], [4, 4]]
y = Matrix[[1, 2], [3, 4]]
Matrix.combine(x, y) {|a, b| a - b} # => Matrix[[5, 4], [1, 0]]

TODO: optimize in C



209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/matrix/constructors.rb', line 209

def self.combine(*matrices)
  return empty if matrices.empty?

  result = convert(matrices.first)
  matrices[1..matrices.size].each do |m|
    raise IndexError unless result.row_count == m.row_count &&
                            result.column_count == m.column_count

    result.each_with_index! { |elem, i, j| yield elem, m[i, j] }
  end
  result
end

.convert(matrix) ⇒ Object

Create fast matrix from standard matrix



45
46
47
48
49
50
51
52
53
# File 'lib/matrix/matrix.rb', line 45

def self.convert(matrix)
  fast_matrix = Matrix.new(matrix.row_count, matrix.column_count)
  (0...matrix.row_count).each do |i|
    (0...matrix.column_count).each do |j|
      fast_matrix[i, j] = matrix[i, j]
    end
  end
  fast_matrix
end

.diagonal(*values) ⇒ Object

Creates a matrix where the diagonal elements are composed of values.

Matrix.diagonal(9, 5, -3)
  =>  9  0  0
      0  5  0
      0  0 -3


94
95
96
# File 'lib/matrix/constructors.rb', line 94

def self.diagonal(*values)
  build(values.size, values.size) { |i, j| i == j ? values[i] : 0 }
end

.empty(_ = 0, _ = 0) ⇒ Object

Empty matrices does not supported

Raises:



146
147
148
# File 'lib/matrix/constructors.rb', line 146

def self.empty(_ = 0, _ = 0)
  raise NotSupportedError, 'Empty matrices does not supported'
end

.fill(value, row_count, column_count = row_count) ⇒ Object

Creates a filled matrix Matrix.fill(42, 2, 4)

=>  42 42 42 42
    42 42 42 42


129
130
131
# File 'lib/matrix/constructors.rb', line 129

def self.fill(value, row_count, column_count = row_count)
  create_with_check(row_count, column_count).fill!(value)
end

.hstack(x, *matrices) ⇒ Object

Create a matrix by stacking matrices horizontally

x = Matrix[[1, 2], [3, 4]]
y = Matrix[[5, 6], [7, 8]]
Matrix.hstack(x, y) # => Matrix[[1, 2, 5, 6], [3, 4, 7, 8]]


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/matrix/constructors.rb', line 183

def self.hstack(x, *matrices)
  column_count = x.column_count
  row_count = x.row_count
  matrices.each do |matrix|
    raise IndexError unless matrix.row_count == row_count

    column_count += matrix.column_count
  end
  result = new(row_count, column_count)
  m_j = 0
  [x, *matrices].each do |matrix|
    matrix.each_with_index do |elem, i, j|
      result[i, m_j + j] = elem
    end
    m_j += matrix.column_count
  end
  result
end

.identity(n) ⇒ Object Also known as: unit, I

Creates an n by n identity matrix.

Matrix.identity(2)
  => 1 0
     0 1


115
116
117
# File 'lib/matrix/constructors.rb', line 115

def self.identity(n)
  scalar(n, 1)
end

.row_vector(row) ⇒ Object

Creates a single-row matrix where the values of that row are as given in row.

Matrix.row_vector([4,5,6])
  => 4 5 6


81
82
83
84
85
# File 'lib/matrix/constructors.rb', line 81

def self.row_vector(row)
  matrix = create_with_check(1, row.size)
  row.each_with_index { |elem, j| matrix[0, j] = elem }
  matrix
end

.rows(rows, copy = true) ⇒ Object

Creates a matrix where rows is an array of arrays, each of which is a row of the matrix. The optional argument copy exists only for compatibility with standard. The optional argument copy cannot be false, unlike standard.

Matrix.rows([[25, 93], [-1, 66]])
   =>  25 93
       -1 66


36
37
38
39
# File 'lib/matrix/constructors.rb', line 36

def self.rows(rows, copy = true)
  check_flag_copy(copy)
  lines(rows, true)
end

.scalar(n, value) ⇒ Object

Creates an n by n diagonal matrix where each diagonal element is value.

Matrix.scalar(2, 5)
  => 5 0
     0 5


105
106
107
# File 'lib/matrix/constructors.rb', line 105

def self.scalar(n, value)
  build(n, n) { |i, j| i == j ? value : 0 }
end

.vstack(x, *matrices) ⇒ Object

Create a matrix by stacking matrices vertically

x = Matrix[[1, 2], [3, 4]]
y = Matrix[[5, 6], [7, 8]]
Matrix.vstack(x, y) # => Matrix[[1, 2], [3, 4], [5, 6], [7, 8]]

TODO: optimize (maybe in C)



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/matrix/constructors.rb', line 157

def self.vstack(x, *matrices)
  column_count = x.column_count
  row_count = x.row_count
  matrices.each do |matrix|
    raise IndexError unless matrix.column_count == column_count

    row_count += matrix.row_count
  end
  result = new(row_count, column_count)
  m_i = 0
  [x, *matrices].each do |matrix|
    matrix.each_with_index do |elem, i, j|
      result[m_i + i, j] = elem
    end
    m_i += matrix.row_count
  end
  result
end

.zero(row_count, column_count = row_count) ⇒ Object

Creates a zero matrix row_count by column_count.

Matrix.zero(2, 3)
  => 0 0 0
     0 0 0


139
140
141
# File 'lib/matrix/constructors.rb', line 139

def self.zero(row_count, column_count = row_count)
  fill(0, row_count, column_count)
end

Instance Method Details

#*(v) ⇒ Object



438
439
440
441
442
443
444
445
446
447
448
# File 'ext/fast_matrix/matrix.c', line 438

VALUE matrix_multiply(VALUE self, VALUE v)
{
    if(RB_FLOAT_TYPE_P(v) || FIXNUM_P(v)
        || RB_TYPE_P(v, T_BIGNUM))
        return matrix_multiply_mn(self, v);
    if(RBASIC_CLASS(v) == cMatrix)
        return strassen(self, v);
    if(RBASIC_CLASS(v) == cVector);
        return matrix_multiply_mv(self, v);
    rb_raise(fm_eTypeError, "Invalid klass for multiply");
}

#+(value) ⇒ Object



492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'ext/fast_matrix/matrix.c', line 492

VALUE matrix_add_with(VALUE self, VALUE value)
{
    raise_check_rbasic(value, cMatrix, "matrix");
  struct matrix* A;
    struct matrix* B;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);
  TypedData_Get_Struct(value, struct matrix, &matrix_type, B);

    if(A->m != B->m && A->n != B->n)
        rb_raise(fm_eIndexError, "Different sizes matrices");

    int m = B->m;
    int n = A->n;

    struct matrix* C;
    VALUE result = TypedData_Make_Struct(cMatrix, struct matrix, &matrix_type, C);

    c_matrix_init(C, m, n);
    add_d_arrays_to_result(n * m, A->data, B->data, C->data);

    return result;
}

#+=(value) ⇒ Object



515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'ext/fast_matrix/matrix.c', line 515

VALUE matrix_add_from(VALUE self, VALUE value)
{
    raise_check_rbasic(value, cMatrix, "matrix");
  struct matrix* A;
    struct matrix* B;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);
  TypedData_Get_Struct(value, struct matrix, &matrix_type, B);

    if(A->m != B->m && A->n != B->n)
        rb_raise(fm_eIndexError, "Different sizes matrices");

    int m = B->m;
    int n = A->n;

    add_d_arrays_to_first(n * m, A->data, B->data);

    return self;
}

#-(value) ⇒ Object



534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
# File 'ext/fast_matrix/matrix.c', line 534

VALUE matrix_sub_with(VALUE self, VALUE value)
{
    raise_check_rbasic(value, cMatrix, "matrix");
  struct matrix* A;
    struct matrix* B;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);
  TypedData_Get_Struct(value, struct matrix, &matrix_type, B);

    if(A->m != B->m && A->n != B->n)
        rb_raise(fm_eIndexError, "Different sizes matrices");

    int m = B->m;
    int n = A->n;

    struct matrix* C;
    VALUE result = TypedData_Make_Struct(cMatrix, struct matrix, &matrix_type, C);

    c_matrix_init(C, m, n);
    sub_d_arrays_to_result(n * m, A->data, B->data, C->data);

    return result;
}

#-=(value) ⇒ Object



557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'ext/fast_matrix/matrix.c', line 557

VALUE matrix_sub_from(VALUE self, VALUE value)
{
    raise_check_rbasic(value, cMatrix, "matrix");
  struct matrix* A;
    struct matrix* B;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);
  TypedData_Get_Struct(value, struct matrix, &matrix_type, B);

    if(A->m != B->m && A->n != B->n)
        rb_raise(fm_eIndexError, "Different sizes matrices");

    int m = B->m;
    int n = A->n;

    sub_d_arrays_to_first(n * m, A->data, B->data);

    return self;
}

#==(other) ⇒ Object

FIXME: for compare with standard matrix



108
109
110
111
112
113
114
115
116
117
118
# File 'lib/matrix/matrix.rb', line 108

def ==(other)
  return eql?(other) if other.class == Matrix
  return false unless i[row_size column_size \[\]].all? { |x| other.respond_to? x }
  return false unless self.row_size == other.row_size && self.column_size == other.column_size

  result = true
  each_with_index do |elem, i, j|
    result &&= elem == other[i, j].to_f
  end
  result
end

#>=(value) ⇒ Object



668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
# File 'ext/fast_matrix/matrix.c', line 668

VALUE matrix_greater_or_equal(VALUE self, VALUE value)
{
    if(RBASIC_CLASS(value) != cMatrix)
        rb_raise(fm_eTypeError, "Expected class matrix");
  struct matrix* A;
    struct matrix* B;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);
  TypedData_Get_Struct(value, struct matrix, &matrix_type, B);

    if(A->m != B->m && A->n != B->n)
        rb_raise(fm_eIndexError, "Different sizes matrices");

    int m = B->m;
    int n = A->n;

    if(greater_or_equal_d_array(n * m, A->data, B->data))
        return Qtrue;
    return Qfalse;
}

#[](row, column) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'ext/fast_matrix/matrix.c', line 86

VALUE matrix_get(VALUE self, VALUE row, VALUE column)
{
    int m = raise_rb_value_to_int(column);
    int n = raise_rb_value_to_int(row);

  struct matrix* data;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, data);
    
    m = (m < 0) ? data->m + m : m;
    n = (n < 0) ? data->n + n : n;
    
    if(m < 0 || n < 0 || n >= data->n || m >= data->m)
        return Qnil;

    return DBL2NUM(data->data[m + data->m * n]);
}

#[]=(row, column, v) ⇒ Object

[]=



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'ext/fast_matrix/matrix.c', line 66

VALUE matrix_set(VALUE self, VALUE row, VALUE column, VALUE v)
{
    int m = raise_rb_value_to_int(column);
    int n = raise_rb_value_to_int(row);
    double x = raise_rb_value_to_double(v);

  struct matrix* data;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, data);
    
    m = (m < 0) ? data->m + m : m;
    n = (n < 0) ? data->n + n : n;

    raise_check_range(m, 0, data->m);
    raise_check_range(n, 0, data->n);

    data->data[m + data->m * n] = x;
    return v;
}

#absObject

rb_define_method(cMatrix, “strassen”, strassen, 1);



651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
# File 'ext/fast_matrix/matrix.c', line 651

VALUE matrix_abs(VALUE self)
{
  struct matrix* A;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);

    int m = A->m;
    int n = A->n;

    struct matrix* B;
    VALUE result = TypedData_Make_Struct(cMatrix, struct matrix, &matrix_type, B);

    c_matrix_init(B, m, n);
    abs_d_array(n * m, A->data, B->data);

    return result;
}

#cloneObject



450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'ext/fast_matrix/matrix.c', line 450

VALUE matrix_copy(VALUE mtrx)
{
  struct matrix* M;
  TypedData_Get_Struct(mtrx, struct matrix, &matrix_type, M);

    struct matrix* R;
    VALUE result = TypedData_Make_Struct(cMatrix, struct matrix, &matrix_type, R);

    c_matrix_init(R, M->m, M->n);
    copy_d_array(M->m * M->n, M->data, R->data);

    return result;
}

#coerce(other) ⇒ Object

The coerce method provides support for Ruby type coercion. This coercion mechanism is used by Ruby to handle mixed-type numeric operations: it is intended to find a compatible common type between the two operands of the operator. See also Numeric#coerce.



13
14
15
16
17
18
19
20
# File 'lib/scalar.rb', line 13

def coerce(other)
  case other
  when Numeric
    return Scalar.new(other), self
  else
    raise TypeError, "#{self.class} can't be coerced into #{other.class}"
  end
end

#collectObject



22
23
24
25
26
27
28
# File 'lib/matrix/matrix.rb', line 22

def collect
  collected_rows = []
  rows.each do |i|
    collected_rows.push(yield i)
  end
  collected_rows
end

#column_countObject Also known as: column_size



464
465
466
467
468
469
# File 'ext/fast_matrix/matrix.c', line 464

VALUE row_size(VALUE self)
{
  struct matrix* data;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, data);
    return INT2NUM(data->m);
}

#componentObject



20
# File 'lib/matrix/matrix.rb', line 20

alias component []

#convertObject

Convert to standard ruby matrix.



103
104
105
# File 'lib/matrix/matrix.rb', line 103

def convert
  ::Matrix.build(row_size, column_size) { |i, j| self[i, j] }
end

#determinantObject



607
608
609
610
611
612
613
614
615
616
617
618
# File 'ext/fast_matrix/matrix.c', line 607

VALUE matrix_determinant(VALUE self)
{
    struct matrix* A;
    TypedData_Get_Struct(self, struct matrix, &matrix_type, A);

    int m = A->m;
    int n = A->n;
    if(m != n)
        rb_raise(fm_eIndexError, "Not a square matrix");

    return DBL2NUM(determinant(n, A->data));
}

#each(&block) ⇒ Object

Yields all elements of the matrix, starting with those of the first row

Matrix[ [1,2], [3,4] ].each { |e| puts e }
  # => prints the numbers 1 to 4

Raises:



60
61
62
63
64
65
# File 'lib/matrix/matrix.rb', line 60

def each(&block)
  raise NotSupportedError unless block_given?

  each_with_index { |elem, _, _| block.call(elem) }
  self
end

#each_with_indexObject

Same as #each, but the row index and column index in addition to the element

Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col|
  puts "#{e} at #{row}, #{col}"
end
  # => Prints:
  #    1 at 0, 0
  #    2 at 0, 1
  #    3 at 1, 0
  #    4 at 1, 1

Raises:



79
80
81
82
83
84
85
86
87
88
# File 'lib/matrix/matrix.rb', line 79

def each_with_index
  raise NotSupportedError unless block_given?

  (0...row_count).each do |i|
    (0...column_count).each do |j|
      yield self[i, j], i, j
    end
  end
  self
end

#each_with_index!Object

don’t use (Issue#1)



91
92
93
94
95
96
97
98
# File 'lib/matrix/matrix.rb', line 91

def each_with_index!
  (0...row_count).each do |i|
    (0...column_count).each do |j|
      self[i, j] = yield self[i, j], i, j
    end
  end
  self
end

#elementObject

Returns element (i,j) of the matrix. That is: row i, column j.



19
# File 'lib/matrix/matrix.rb', line 19

alias element []

#eql?(value) ⇒ Boolean

Returns:

  • (Boolean)


631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
# File 'ext/fast_matrix/matrix.c', line 631

VALUE matrix_equal(VALUE self, VALUE value)
{
    if(RBASIC_CLASS(value) != cMatrix)
        return Qfalse;
  struct matrix* A;
    struct matrix* B;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);
  TypedData_Get_Struct(value, struct matrix, &matrix_type, B);

    if(A->n != B->n || A->m != B->m)
        return Qfalse;

    int n = A->n;
    int m = B->m;

    if(equal_d_arrays(n * m, A->data, B->data))
    return Qtrue;
  return Qfalse;
}

#fill!(value) ⇒ Object



620
621
622
623
624
625
626
627
628
629
# File 'ext/fast_matrix/matrix.c', line 620

VALUE matrix_fill(VALUE self, VALUE value)
{
    double d = raise_rb_value_to_double(value);
  struct matrix* A;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, A);

    fill_d_array(A->m * A->n, A->data, d);

    return self;
}

#row_countObject Also known as: row_size



471
472
473
474
475
476
# File 'ext/fast_matrix/matrix.c', line 471

VALUE column_size(VALUE self)
{
  struct matrix* data;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, data);
    return INT2NUM(data->n);
}

#to_sObject Also known as: to_str

Overrides Object#to_s



33
34
35
36
37
# File 'lib/matrix/matrix.rb', line 33

def to_s
  "#{self.class}[#{collect do |row|
    '[' + row.join(', ') + ']'
  end.join(', ')}]"
end

#transposeObject



478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'ext/fast_matrix/matrix.c', line 478

VALUE transpose(VALUE self)
{
  struct matrix* M;
  TypedData_Get_Struct(self, struct matrix, &matrix_type, M);

    struct matrix* R;
    VALUE result = TypedData_Make_Struct(cMatrix, struct matrix, &matrix_type, R);

    c_matrix_init(R, M->n, M->m);
    matrix_transpose(M->m, M->n, M->data, R->data);

    return result;
}