stick 1.3.2 → 1.3.3

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.
@@ -0,0 +1,1408 @@
1
+ #!/usr/local/bin/ruby
2
+ #--
3
+ # matrix.rb -
4
+ # $Release Version: 1.0$
5
+ # $Revision: 1.13 $
6
+ # $Date: 2001/12/09 14:22:23 $
7
+ # Original Version from Smalltalk-80 version
8
+ # on July 23, 1985 at 8:37:17 am
9
+ # by Keiju ISHITSUKA
10
+ #++
11
+ #
12
+ # = matrix.rb
13
+ #
14
+ # An implementation of Matrix and Vector classes.
15
+ #
16
+ # Author:: Keiju ISHITSUKA
17
+ # Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
18
+ #
19
+ # See classes Matrix and Vector for documentation.
20
+ #
21
+
22
+
23
+ require "e2mmap.rb"
24
+
25
+ module Stick
26
+
27
+ module ExceptionForMatrix # :nodoc:
28
+ extend Exception2MessageMapper
29
+ def_e2message(TypeError, "wrong argument type %s (expected %s)")
30
+ def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)")
31
+
32
+ def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch")
33
+ def_exception("ErrNotRegular", "Not Regular Matrix")
34
+ def_exception("ErrOperationNotDefined", "This operation(%s) can\\'t defined")
35
+ end
36
+
37
+ #
38
+ # The +Matrix+ class represents a mathematical matrix, and provides methods for creating
39
+ # special-case matrices (zero, identity, diagonal, singular, vector), operating on them
40
+ # arithmetically and algebraically, and determining their mathematical properties (trace, rank,
41
+ # inverse, determinant).
42
+ #
43
+ # Note that although matrices should theoretically be rectangular, this is not
44
+ # enforced by the class.
45
+ #
46
+ # Also note that the determinant of integer matrices may be incorrectly calculated unless you
47
+ # also <tt>require 'mathn'</tt>. This may be fixed in the future.
48
+ #
49
+ # == Method Catalogue
50
+ #
51
+ # To create a matrix:
52
+ # * <tt> Matrix[*rows] </tt>
53
+ # * <tt> Matrix.[](*rows) </tt>
54
+ # * <tt> Matrix.rows(rows, copy = true) </tt>
55
+ # * <tt> Matrix.columns(columns) </tt>
56
+ # * <tt> Matrix.diagonal(*values) </tt>
57
+ # * <tt> Matrix.scalar(n, value) </tt>
58
+ # * <tt> Matrix.scalar(n, value) </tt>
59
+ # * <tt> Matrix.identity(n) </tt>
60
+ # * <tt> Matrix.unit(n) </tt>
61
+ # * <tt> Matrix.I(n) </tt>
62
+ # * <tt> Matrix.zero(n) </tt>
63
+ # * <tt> Matrix.row_vector(row) </tt>
64
+ # * <tt> Matrix.column_vector(column) </tt>
65
+ #
66
+ # To access Matrix elements/columns/rows/submatrices/properties:
67
+ # * <tt> [](i, j) </tt>
68
+ # * <tt> #row_size </tt>
69
+ # * <tt> #column_size </tt>
70
+ # * <tt> #row(i) </tt>
71
+ # * <tt> #column(j) </tt>
72
+ # * <tt> #collect </tt>
73
+ # * <tt> #map </tt>
74
+ # * <tt> #minor(*param) </tt>
75
+ #
76
+ # Properties of a matrix:
77
+ # * <tt> #regular? </tt>
78
+ # * <tt> #singular? </tt>
79
+ # * <tt> #square? </tt>
80
+ #
81
+ # Matrix arithmetic:
82
+ # * <tt> *(m) </tt>
83
+ # * <tt> +(m) </tt>
84
+ # * <tt> -(m) </tt>
85
+ # * <tt> #/(m) </tt>
86
+ # * <tt> #inverse </tt>
87
+ # * <tt> #inv </tt>
88
+ # * <tt> ** </tt>
89
+ #
90
+ # Matrix functions:
91
+ # * <tt> #determinant </tt>
92
+ # * <tt> #det </tt>
93
+ # * <tt> #rank </tt>
94
+ # * <tt> #trace </tt>
95
+ # * <tt> #tr </tt>
96
+ # * <tt> #transpose </tt>
97
+ # * <tt> #t </tt>
98
+ #
99
+ # Conversion to other data types:
100
+ # * <tt> #coerce(other) </tt>
101
+ # * <tt> #row_vectors </tt>
102
+ # * <tt> #column_vectors </tt>
103
+ # * <tt> #to_a </tt>
104
+ #
105
+ # String representations:
106
+ # * <tt> #to_s </tt>
107
+ # * <tt> #inspect </tt>
108
+ #
109
+ class Matrix
110
+ @RCS_ID='-$Id: matrix.rb,v 1.13 2001/12/09 14:22:23 keiju Exp keiju $-'
111
+
112
+ # extend Exception2MessageMapper
113
+ include ExceptionForMatrix
114
+
115
+ # instance creations
116
+ private_class_method :new
117
+
118
+ #
119
+ # Creates a matrix where each argument is a row.
120
+ # Matrix[ [25, 93], [-1, 66] ]
121
+ # => 25 93
122
+ # -1 66
123
+ #
124
+ def Matrix.[](*rows)
125
+ new(:init_rows, rows, false)
126
+ end
127
+
128
+ #
129
+ # Creates a matrix where +rows+ is an array of arrays, each of which is a row
130
+ # to the matrix. If the optional argument +copy+ is false, use the given
131
+ # arrays as the internal structure of the matrix without copying.
132
+ # Matrix.rows([[25, 93], [-1, 66]])
133
+ # => 25 93
134
+ # -1 66
135
+ def Matrix.rows(rows, copy = true)
136
+ new(:init_rows, rows, copy)
137
+ end
138
+
139
+ #
140
+ # Creates a matrix using +columns+ as an array of column vectors.
141
+ # Matrix.columns([[25, 93], [-1, 66]])
142
+ # => 25 -1
143
+ # 93 66
144
+ #
145
+ #
146
+ def Matrix.columns(columns)
147
+ rows = (0 .. columns[0].size - 1).collect {
148
+ |i|
149
+ (0 .. columns.size - 1).collect {
150
+ |j|
151
+ columns[j][i]
152
+ }
153
+ }
154
+ Matrix.rows(rows, false)
155
+ end
156
+
157
+ #
158
+ # Creates a matrix where the diagonal elements are composed of +values+.
159
+ # Matrix.diagonal(9, 5, -3)
160
+ # => 9 0 0
161
+ # 0 5 0
162
+ # 0 0 -3
163
+ #
164
+ def Matrix.diagonal(*values)
165
+ size = values.size
166
+ rows = (0 .. size - 1).collect {
167
+ |j|
168
+ row = Array.new(size).fill(0, 0, size)
169
+ row[j] = values[j]
170
+ row
171
+ }
172
+ rows(rows, false)
173
+ end
174
+
175
+ #
176
+ # Creates an +n+ by +n+ diagonal matrix where each diagonal element is
177
+ # +value+.
178
+ # Matrix.scalar(2, 5)
179
+ # => 5 0
180
+ # 0 5
181
+ #
182
+ def Matrix.scalar(n, value)
183
+ Matrix.diagonal(*Array.new(n).fill(value, 0, n))
184
+ end
185
+
186
+ #
187
+ # Creates an +n+ by +n+ identity matrix.
188
+ # Matrix.identity(2)
189
+ # => 1 0
190
+ # 0 1
191
+ #
192
+ def Matrix.identity(n)
193
+ Matrix.scalar(n, 1)
194
+ end
195
+ class << Matrix
196
+ alias unit identity
197
+ alias I identity
198
+ end
199
+
200
+ #
201
+ # Creates an +n+ by +n+ zero matrix.
202
+ # Matrix.zero(2)
203
+ # => 0 0
204
+ # 0 0
205
+ #
206
+ def Matrix.zero(n)
207
+ Matrix.scalar(n, 0)
208
+ end
209
+
210
+ #
211
+ # Creates a single-row matrix where the values of that row are as given in
212
+ # +row+.
213
+ # Matrix.row_vector([4,5,6])
214
+ # => 4 5 6
215
+ #
216
+ def Matrix.row_vector(row)
217
+ case row
218
+ when Vector
219
+ Matrix.rows([row.to_a], false)
220
+ when Array
221
+ Matrix.rows([row.dup], false)
222
+ else
223
+ Matrix.rows([[row]], false)
224
+ end
225
+ end
226
+
227
+ #
228
+ # Creates a single-column matrix where the values of that column are as given
229
+ # in +column+.
230
+ # Matrix.column_vector([4,5,6])
231
+ # => 4
232
+ # 5
233
+ # 6
234
+ #
235
+ def Matrix.column_vector(column)
236
+ case column
237
+ when Vector
238
+ Matrix.columns([column.to_a])
239
+ when Array
240
+ Matrix.columns([column])
241
+ else
242
+ Matrix.columns([[column]])
243
+ end
244
+ end
245
+
246
+ #
247
+ # This method is used by the other methods that create matrices, and is of no
248
+ # use to general users.
249
+ #
250
+ def initialize(init_method, *argv)
251
+ self.send(init_method, *argv)
252
+ end
253
+
254
+ def init_rows(rows, copy)
255
+ if copy
256
+ @rows = rows.collect{|row| row.dup}
257
+ else
258
+ @rows = rows
259
+ end
260
+ self
261
+ end
262
+ private :init_rows
263
+
264
+ #
265
+ # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+.
266
+ #
267
+ def [](i, j)
268
+ @rows[i][j]
269
+ end
270
+ alias element []
271
+ alias component []
272
+
273
+ def []=(i, j, v)
274
+ @rows[i][j] = v
275
+ end
276
+ alias set_element []=
277
+ alias set_component []=
278
+ private :[]=, :set_element, :set_component
279
+
280
+ #
281
+ # Returns the number of rows.
282
+ #
283
+ def row_size
284
+ @rows.size
285
+ end
286
+
287
+ #
288
+ # Returns the number of columns. Note that it is possible to construct a
289
+ # matrix with uneven columns (e.g. Matrix[ [1,2,3], [4,5] ]), but this is
290
+ # mathematically unsound. This method uses the first row to determine the
291
+ # result.
292
+ #
293
+ def column_size
294
+ @rows[0].size
295
+ end
296
+
297
+ #
298
+ # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
299
+ # an array). When a block is given, the elements of that vector are iterated.
300
+ #
301
+ def row(i) # :yield: e
302
+ if block_given?
303
+ for e in @rows[i]
304
+ yield e
305
+ end
306
+ else
307
+ Vector.elements(@rows[i])
308
+ end
309
+ end
310
+
311
+ #
312
+ # Returns column vector number +j+ of the matrix as a Vector (starting at 0
313
+ # like an array). When a block is given, the elements of that vector are
314
+ # iterated.
315
+ #
316
+ def column(j) # :yield: e
317
+ if block_given?
318
+ 0.upto(row_size - 1) do
319
+ |i|
320
+ yield @rows[i][j]
321
+ end
322
+ else
323
+ col = (0 .. row_size - 1).collect {
324
+ |i|
325
+ @rows[i][j]
326
+ }
327
+ Vector.elements(col, false)
328
+ end
329
+ end
330
+
331
+ #
332
+ # Returns a matrix that is the result of iteration of the given block over all
333
+ # elements of the matrix.
334
+ # Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
335
+ # => 1 4
336
+ # 9 16
337
+ #
338
+ def collect # :yield: e
339
+ rows = @rows.collect{|row| row.collect{|e| yield e}}
340
+ Matrix.rows(rows, false)
341
+ end
342
+ alias map collect
343
+
344
+ #
345
+ # Returns a section of the matrix. The parameters are either:
346
+ # * start_row, nrows, start_col, ncols; OR
347
+ # * col_range, row_range
348
+ #
349
+ # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
350
+ # => 9 0 0
351
+ # 0 5 0
352
+ #
353
+ def minor(*param)
354
+ case param.size
355
+ when 2
356
+ from_row = param[0].first
357
+ size_row = param[0].end - from_row
358
+ size_row += 1 unless param[0].exclude_end?
359
+ from_col = param[1].first
360
+ size_col = param[1].end - from_col
361
+ size_col += 1 unless param[1].exclude_end?
362
+ when 4
363
+ from_row = param[0]
364
+ size_row = param[1]
365
+ from_col = param[2]
366
+ size_col = param[3]
367
+ else
368
+ Matrix.Raise ArgumentError, param.inspect
369
+ end
370
+
371
+ rows = @rows[from_row, size_row].collect{
372
+ |row|
373
+ row[from_col, size_col]
374
+ }
375
+ Matrix.rows(rows, false)
376
+ end
377
+
378
+ #--
379
+ # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
380
+ #++
381
+
382
+ #
383
+ # Returns +true+ if this is a regular matrix.
384
+ #
385
+ def regular?
386
+ square? and rank == column_size
387
+ end
388
+
389
+ #
390
+ # Returns +true+ is this is a singular (i.e. non-regular) matrix.
391
+ #
392
+ def singular?
393
+ not regular?
394
+ end
395
+
396
+ #
397
+ # Returns +true+ is this is a square matrix. See note in column_size about this
398
+ # being unreliable, though.
399
+ #
400
+ def square?
401
+ column_size == row_size
402
+ end
403
+
404
+ #--
405
+ # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
406
+ #++
407
+
408
+ #
409
+ # Returns +true+ if and only if the two matrices contain equal elements.
410
+ #
411
+ def ==(other)
412
+ return false unless Matrix === other
413
+
414
+ other.compare_by_row_vectors(@rows)
415
+ end
416
+ alias eql? ==
417
+
418
+ #
419
+ # Not really intended for general consumption.
420
+ #
421
+ def compare_by_row_vectors(rows)
422
+ return false unless @rows.size == rows.size
423
+
424
+ 0.upto(@rows.size - 1) do
425
+ |i|
426
+ return false unless @rows[i] == rows[i]
427
+ end
428
+ true
429
+ end
430
+
431
+ #
432
+ # Returns a clone of the matrix, so that the contents of each do not reference
433
+ # identical objects.
434
+ #
435
+ def clone
436
+ Matrix.rows(@rows)
437
+ end
438
+
439
+ #
440
+ # Returns a hash-code for the matrix.
441
+ #
442
+ def hash
443
+ value = 0
444
+ for row in @rows
445
+ for e in row
446
+ value ^= e.hash
447
+ end
448
+ end
449
+ return value
450
+ end
451
+
452
+ #--
453
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
454
+ #++
455
+
456
+ #
457
+ # Matrix multiplication.
458
+ # Matrix[[2,4], [6,8]] * Matrix.identity(2)
459
+ # => 2 4
460
+ # 6 8
461
+ #
462
+ def *(m) # m is matrix or vector or number
463
+ case(m)
464
+ when Numeric
465
+ rows = @rows.collect {
466
+ |row|
467
+ row.collect {
468
+ |e|
469
+ e * m
470
+ }
471
+ }
472
+ return Matrix.rows(rows, false)
473
+ when Vector
474
+ m = Matrix.column_vector(m)
475
+ r = self * m
476
+ return r.column(0)
477
+ when Matrix
478
+ Matrix.Raise ErrDimensionMismatch if column_size != m.row_size
479
+
480
+ rows = (0 .. row_size - 1).collect {
481
+ |i|
482
+ (0 .. m.column_size - 1).collect {
483
+ |j|
484
+ vij = 0
485
+ 0.upto(column_size - 1) do
486
+ |k|
487
+ vij += self[i, k] * m[k, j]
488
+ end
489
+ vij
490
+ }
491
+ }
492
+ return Matrix.rows(rows, false)
493
+ else
494
+ x, y = m.coerce(self)
495
+ return x * y
496
+ end
497
+ end
498
+
499
+ #
500
+ # Matrix addition.
501
+ # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
502
+ # => 6 0
503
+ # -4 12
504
+ #
505
+ def +(m)
506
+ case m
507
+ when Numeric
508
+ Matrix.Raise ErrOperationNotDefined, "+"
509
+ when Vector
510
+ m = Matrix.column_vector(m)
511
+ when Matrix
512
+ else
513
+ x, y = m.coerce(self)
514
+ return x + y
515
+ end
516
+
517
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
518
+
519
+ rows = (0 .. row_size - 1).collect {
520
+ |i|
521
+ (0 .. column_size - 1).collect {
522
+ |j|
523
+ self[i, j] + m[i, j]
524
+ }
525
+ }
526
+ Matrix.rows(rows, false)
527
+ end
528
+
529
+ #
530
+ # Matrix subtraction.
531
+ # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
532
+ # => -8 2
533
+ # 8 1
534
+ #
535
+ def -(m)
536
+ case m
537
+ when Numeric
538
+ Matrix.Raise ErrOperationNotDefined, "-"
539
+ when Vector
540
+ m = Matrix.column_vector(m)
541
+ when Matrix
542
+ else
543
+ x, y = m.coerce(self)
544
+ return x - y
545
+ end
546
+
547
+ Matrix.Raise ErrDimensionMismatch unless row_size == m.row_size and column_size == m.column_size
548
+
549
+ rows = (0 .. row_size - 1).collect {
550
+ |i|
551
+ (0 .. column_size - 1).collect {
552
+ |j|
553
+ self[i, j] - m[i, j]
554
+ }
555
+ }
556
+ Matrix.rows(rows, false)
557
+ end
558
+
559
+ #
560
+ # Matrix division (multiplication by the inverse).
561
+ # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]]
562
+ # => -7 1
563
+ # -3 -6
564
+ #
565
+ def /(other)
566
+ case other
567
+ when Numeric
568
+ rows = @rows.collect {
569
+ |row|
570
+ row.collect {
571
+ |e|
572
+ e / other
573
+ }
574
+ }
575
+ return Matrix.rows(rows, false)
576
+ when Matrix
577
+ return self * other.inverse
578
+ else
579
+ x, y = other.coerce(self)
580
+ rerurn x / y
581
+ end
582
+ end
583
+
584
+ #
585
+ # Returns the inverse of the matrix.
586
+ # Matrix[[1, 2], [2, 1]].inverse
587
+ # => -1 1
588
+ # 0 -1
589
+ #
590
+ def inverse
591
+ Matrix.Raise ErrDimensionMismatch unless square?
592
+ Matrix.I(row_size).inverse_from(self)
593
+ end
594
+ alias inv inverse
595
+
596
+ #
597
+ # Not for public consumption?
598
+ #
599
+ def inverse_from(src)
600
+ size = row_size - 1
601
+ a = src.to_a
602
+
603
+ for k in 0..size
604
+ i = k
605
+ akk = a[k][k].abs
606
+ for j in (k+1)..size
607
+ v = a[j][k].abs
608
+ if v > akk
609
+ i = j
610
+ akk = v
611
+ end
612
+ end
613
+ Matrix.Raise ErrNotRegular if akk == 0
614
+ if i != k
615
+ a[i], a[k] = a[k], a[i]
616
+ @rows[i], @rows[k] = @rows[k], @rows[i]
617
+ end
618
+ akk = a[k][k]
619
+
620
+ for i in 0 .. size
621
+ next if i == k
622
+ q = a[i][k].quo(akk)
623
+ a[i][k] = 0
624
+
625
+ (k + 1).upto(size) do
626
+ |j|
627
+ a[i][j] -= a[k][j] * q
628
+ end
629
+ 0.upto(size) do
630
+ |j|
631
+ @rows[i][j] -= @rows[k][j] * q
632
+ end
633
+ end
634
+
635
+ (k + 1).upto(size) do
636
+ |j|
637
+ a[k][j] = a[k][j].quo(akk)
638
+ end
639
+ 0.upto(size) do
640
+ |j|
641
+ @rows[k][j] = @rows[k][j].quo(akk)
642
+ end
643
+ end
644
+ self
645
+ end
646
+ #alias reciprocal inverse
647
+
648
+ #
649
+ # Matrix exponentiation. Defined for integer powers only. Equivalent to
650
+ # multiplying the matrix by itself N times.
651
+ # Matrix[[7,6], [3,9]] ** 2
652
+ # => 67 96
653
+ # 48 99
654
+ #
655
+ def ** (other)
656
+ if other.kind_of?(Integer)
657
+ x = self
658
+ if other <= 0
659
+ x = self.inverse
660
+ return Matrix.identity(self.column_size) if other == 0
661
+ other = -other
662
+ end
663
+ z = x
664
+ n = other - 1
665
+ while n != 0
666
+ while (div, mod = n.divmod(2)
667
+ mod == 0)
668
+ x = x * x
669
+ n = div
670
+ end
671
+ z *= x
672
+ n -= 1
673
+ end
674
+ z
675
+ elsif other.kind_of?(Float) || defined?(Rational) && other.kind_of?(Rational)
676
+ Matrix.Raise ErrOperationNotDefined, "**"
677
+ else
678
+ Matrix.Raise ErrOperationNotDefined, "**"
679
+ end
680
+ end
681
+
682
+ #--
683
+ # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
684
+ #++
685
+
686
+ #
687
+ # Returns the determinant of the matrix. If the matrix is not square, the
688
+ # result is 0. This method's algorism is Gaussian elimination method
689
+ # and using Numeric#quo(). Beware that using Float values, with their
690
+ # usual lack of precision, can affect the value returned by this method. Use
691
+ # Rational values or Matrix#det_e instead if this is important to you.
692
+ #
693
+ # Matrix[[7,6], [3,9]].determinant
694
+ # => 63.0
695
+ #
696
+ def determinant
697
+ return 0 unless square?
698
+
699
+ size = row_size - 1
700
+ a = to_a
701
+
702
+ det = 1
703
+ k = 0
704
+ begin
705
+ if (akk = a[k][k]) == 0
706
+ i = k
707
+ begin
708
+ return 0 if (i += 1) > size
709
+ end while a[i][k] == 0
710
+ a[i], a[k] = a[k], a[i]
711
+ akk = a[k][k]
712
+ det *= -1
713
+ end
714
+ (k + 1).upto(size) do
715
+ |i|
716
+ q = a[i][k].quo(akk)
717
+ (k + 1).upto(size) do
718
+ |j|
719
+ a[i][j] -= a[k][j] * q
720
+ end
721
+ end
722
+ det *= akk
723
+ end while (k += 1) <= size
724
+ det
725
+ end
726
+ alias det determinant
727
+
728
+ #
729
+ # Returns the determinant of the matrix. If the matrix is not square, the
730
+ # result is 0. This method's algorism is Gaussian elimination method.
731
+ # This method uses Euclidean algorism. If all elements are integer,
732
+ # really exact value. But, if an element is a float, can't return
733
+ # exact value.
734
+ #
735
+ # Matrix[[7,6], [3,9]].determinant
736
+ # => 63
737
+ #
738
+ def determinant_e
739
+ return 0 unless square?
740
+
741
+ size = row_size - 1
742
+ a = to_a
743
+
744
+ det = 1
745
+ k = 0
746
+ begin
747
+ if a[k][k].zero?
748
+ i = k
749
+ begin
750
+ return 0 if (i += 1) > size
751
+ end while a[i][k].zero?
752
+ a[i], a[k] = a[k], a[i]
753
+ det *= -1
754
+ end
755
+ (k + 1).upto(size) do |i|
756
+ q = a[i][k].quo(a[k][k])
757
+ k.upto(size) do |j|
758
+ a[i][j] -= a[k][j] * q
759
+ end
760
+ unless a[i][k].zero?
761
+ a[i], a[k] = a[k], a[i]
762
+ det *= -1
763
+ redo
764
+ end
765
+ end
766
+ det *= a[k][k]
767
+ end while (k += 1) <= size
768
+ det
769
+ end
770
+ alias det_e determinant_e
771
+
772
+ #
773
+ # Returns the rank of the matrix. Beware that using Float values,
774
+ # probably return faild value. Use Rational values or Matrix#rank_e
775
+ # for getting exact result.
776
+ #
777
+ # Matrix[[7,6], [3,9]].rank
778
+ # => 2
779
+ #
780
+ def rank
781
+ if column_size > row_size
782
+ a = transpose.to_a
783
+ a_column_size = row_size
784
+ a_row_size = column_size
785
+ else
786
+ a = to_a
787
+ a_column_size = column_size
788
+ a_row_size = row_size
789
+ end
790
+ rank = 0
791
+ k = 0
792
+ begin
793
+ if (akk = a[k][k]) == 0
794
+ i = k
795
+ exists = true
796
+ begin
797
+ if (i += 1) > a_column_size - 1
798
+ exists = false
799
+ break
800
+ end
801
+ end while a[i][k] == 0
802
+ if exists
803
+ a[i], a[k] = a[k], a[i]
804
+ akk = a[k][k]
805
+ else
806
+ i = k
807
+ exists = true
808
+ begin
809
+ if (i += 1) > a_row_size - 1
810
+ exists = false
811
+ break
812
+ end
813
+ end while a[k][i] == 0
814
+ if exists
815
+ k.upto(a_column_size - 1) do
816
+ |j|
817
+ a[j][k], a[j][i] = a[j][i], a[j][k]
818
+ end
819
+ akk = a[k][k]
820
+ else
821
+ next
822
+ end
823
+ end
824
+ end
825
+ (k + 1).upto(a_row_size - 1) do
826
+ |i|
827
+ q = a[i][k].quo(akk)
828
+ (k + 1).upto(a_column_size - 1) do
829
+ |j|
830
+ a[i][j] -= a[k][j] * q
831
+ end
832
+ end
833
+ rank += 1
834
+ end while (k += 1) <= a_column_size - 1
835
+ return rank
836
+ end
837
+
838
+ #
839
+ # Returns the rank of the matrix. This method uses Euclidean
840
+ # algorism. If all elements are integer, really exact value. But, if
841
+ # an element is a float, can't return exact value.
842
+ #
843
+ # Matrix[[7,6], [3,9]].rank
844
+ # => 2
845
+ #
846
+ def rank_e
847
+ a = to_a
848
+ a_column_size = column_size
849
+ a_row_size = row_size
850
+ pi = 0
851
+ (0 ... a_column_size).each do |j|
852
+ if i = (pi ... a_row_size).find{|i0| !a[i0][j].zero?}
853
+ if i != pi
854
+ a[pi], a[i] = a[i], a[pi]
855
+ end
856
+ (pi + 1 ... a_row_size).each do |k|
857
+ q = a[k][j].quo(a[pi][j])
858
+ (pi ... a_column_size).each do |j0|
859
+ a[k][j0] -= q * a[pi][j0]
860
+ end
861
+ if k > pi && !a[k][j].zero?
862
+ a[k], a[pi] = a[pi], a[k]
863
+ redo
864
+ end
865
+ end
866
+ pi += 1
867
+ end
868
+ end
869
+ pi
870
+ end
871
+
872
+
873
+ #
874
+ # Returns the trace (sum of diagonal elements) of the matrix.
875
+ # Matrix[[7,6], [3,9]].trace
876
+ # => 16
877
+ #
878
+ def trace
879
+ tr = 0
880
+ 0.upto(column_size - 1) do
881
+ |i|
882
+ tr += @rows[i][i]
883
+ end
884
+ tr
885
+ end
886
+ alias tr trace
887
+
888
+ #
889
+ # Returns the transpose of the matrix.
890
+ # Matrix[[1,2], [3,4], [5,6]]
891
+ # => 1 2
892
+ # 3 4
893
+ # 5 6
894
+ # Matrix[[1,2], [3,4], [5,6]].transpose
895
+ # => 1 3 5
896
+ # 2 4 6
897
+ #
898
+ def transpose
899
+ Matrix.columns(@rows)
900
+ end
901
+ alias t transpose
902
+
903
+ #--
904
+ # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
905
+ #++
906
+
907
+ #
908
+ # FIXME: describe #coerce.
909
+ #
910
+ def coerce(other)
911
+ case other
912
+ when Numeric
913
+ return Scalar.new(other), self
914
+ else
915
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
916
+ end
917
+ end
918
+
919
+ #
920
+ # Returns an array of the row vectors of the matrix. See Vector.
921
+ #
922
+ def row_vectors
923
+ rows = (0 .. row_size - 1).collect {
924
+ |i|
925
+ row(i)
926
+ }
927
+ rows
928
+ end
929
+
930
+ #
931
+ # Returns an array of the column vectors of the matrix. See Vector.
932
+ #
933
+ def column_vectors
934
+ columns = (0 .. column_size - 1).collect {
935
+ |i|
936
+ column(i)
937
+ }
938
+ columns
939
+ end
940
+
941
+ #
942
+ # Returns an array of arrays that describe the rows of the matrix.
943
+ #
944
+ def to_a
945
+ @rows.collect{|row| row.collect{|e| e}}
946
+ end
947
+
948
+ def elements_to_f
949
+ collect{|e| e.to_f}
950
+ end
951
+
952
+ def elements_to_i
953
+ collect{|e| e.to_i}
954
+ end
955
+
956
+ def elements_to_r
957
+ collect{|e| e.to_r}
958
+ end
959
+
960
+ #--
961
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
962
+ #++
963
+
964
+ #
965
+ # Overrides Object#to_s
966
+ #
967
+ def to_s
968
+ "Matrix[" + @rows.collect{
969
+ |row|
970
+ "[" + row.collect{|e| e.to_s}.join(", ") + "]"
971
+ }.join(", ")+"]"
972
+ end
973
+
974
+ #
975
+ # Overrides Object#inspect
976
+ #
977
+ def inspect
978
+ "Matrix"+@rows.inspect
979
+ end
980
+
981
+ # Private CLASS
982
+
983
+ class Scalar < Numeric # :nodoc:
984
+ include ExceptionForMatrix
985
+
986
+ def initialize(value)
987
+ @value = value
988
+ end
989
+
990
+ # ARITHMETIC
991
+ def +(other)
992
+ case other
993
+ when Numeric
994
+ Scalar.new(@value + other)
995
+ when Vector, Matrix
996
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar"
997
+ when Scalar
998
+ Scalar.new(@value + other.value)
999
+ else
1000
+ x, y = other.coerce(self)
1001
+ x + y
1002
+ end
1003
+ end
1004
+
1005
+ def -(other)
1006
+ case other
1007
+ when Numeric
1008
+ Scalar.new(@value - other)
1009
+ when Vector, Matrix
1010
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar"
1011
+ when Scalar
1012
+ Scalar.new(@value - other.value)
1013
+ else
1014
+ x, y = other.coerce(self)
1015
+ x - y
1016
+ end
1017
+ end
1018
+
1019
+ def *(other)
1020
+ case other
1021
+ when Numeric
1022
+ Scalar.new(@value * other)
1023
+ when Vector, Matrix
1024
+ other.collect{|e| @value * e}
1025
+ else
1026
+ x, y = other.coerce(self)
1027
+ x * y
1028
+ end
1029
+ end
1030
+
1031
+ def / (other)
1032
+ case other
1033
+ when Numeric
1034
+ Scalar.new(@value / other)
1035
+ when Vector
1036
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
1037
+ when Matrix
1038
+ self * other.inverse
1039
+ else
1040
+ x, y = other.coerce(self)
1041
+ x.quo(y)
1042
+ end
1043
+ end
1044
+
1045
+ def ** (other)
1046
+ case other
1047
+ when Numeric
1048
+ Scalar.new(@value ** other)
1049
+ when Vector
1050
+ Scalar.Raise WrongArgType, other.class, "Numeric or Scalar or Matrix"
1051
+ when Matrix
1052
+ other.powered_by(self)
1053
+ else
1054
+ x, y = other.coerce(self)
1055
+ x ** y
1056
+ end
1057
+ end
1058
+ end
1059
+ end
1060
+
1061
+
1062
+ #
1063
+ # The +Vector+ class represents a mathematical vector, which is useful in its own right, and
1064
+ # also constitutes a row or column of a Matrix.
1065
+ #
1066
+ # == Method Catalogue
1067
+ #
1068
+ # To create a Vector:
1069
+ # * <tt> Vector.[](*array) </tt>
1070
+ # * <tt> Vector.elements(array, copy = true) </tt>
1071
+ #
1072
+ # To access elements:
1073
+ # * <tt> [](i) </tt>
1074
+ #
1075
+ # To enumerate the elements:
1076
+ # * <tt> #each2(v) </tt>
1077
+ # * <tt> #collect2(v) </tt>
1078
+ #
1079
+ # Vector arithmetic:
1080
+ # * <tt> *(x) "is matrix or number" </tt>
1081
+ # * <tt> +(v) </tt>
1082
+ # * <tt> -(v) </tt>
1083
+ #
1084
+ # Vector functions:
1085
+ # * <tt> #inner_product(v) </tt>
1086
+ # * <tt> #collect </tt>
1087
+ # * <tt> #map </tt>
1088
+ # * <tt> #map2(v) </tt>
1089
+ # * <tt> #r </tt>
1090
+ # * <tt> #size </tt>
1091
+ #
1092
+ # Conversion to other data types:
1093
+ # * <tt> #covector </tt>
1094
+ # * <tt> #to_a </tt>
1095
+ # * <tt> #coerce(other) </tt>
1096
+ #
1097
+ # String representations:
1098
+ # * <tt> #to_s </tt>
1099
+ # * <tt> #inspect </tt>
1100
+ #
1101
+ class Vector
1102
+ include ExceptionForMatrix
1103
+
1104
+ #INSTANCE CREATION
1105
+
1106
+ private_class_method :new
1107
+
1108
+ #
1109
+ # Creates a Vector from a list of elements.
1110
+ # Vector[7, 4, ...]
1111
+ #
1112
+ def Vector.[](*array)
1113
+ new(:init_elements, array, copy = false)
1114
+ end
1115
+
1116
+ #
1117
+ # Creates a vector from an Array. The optional second argument specifies
1118
+ # whether the array itself or a copy is used internally.
1119
+ #
1120
+ def Vector.elements(array, copy = true)
1121
+ new(:init_elements, array, copy)
1122
+ end
1123
+
1124
+ #
1125
+ # For internal use.
1126
+ #
1127
+ def initialize(method, array, copy)
1128
+ self.send(method, array, copy)
1129
+ end
1130
+
1131
+ #
1132
+ # For internal use.
1133
+ #
1134
+ def init_elements(array, copy)
1135
+ if copy
1136
+ @elements = array.dup
1137
+ else
1138
+ @elements = array
1139
+ end
1140
+ end
1141
+
1142
+ # ACCESSING
1143
+
1144
+ #
1145
+ # Returns element number +i+ (starting at zero) of the vector.
1146
+ #
1147
+ def [](i)
1148
+ @elements[i]
1149
+ end
1150
+ alias element []
1151
+ alias component []
1152
+
1153
+ def []=(i, v)
1154
+ @elements[i]= v
1155
+ end
1156
+ alias set_element []=
1157
+ alias set_component []=
1158
+ private :[]=, :set_element, :set_component
1159
+
1160
+ #
1161
+ # Returns the number of elements in the vector.
1162
+ #
1163
+ def size
1164
+ @elements.size
1165
+ end
1166
+
1167
+ #--
1168
+ # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1169
+ #++
1170
+
1171
+ #
1172
+ # Iterate over the elements of this vector and +v+ in conjunction.
1173
+ #
1174
+ def each2(v) # :yield: e1, e2
1175
+ Vector.Raise ErrDimensionMismatch if size != v.size
1176
+ 0.upto(size - 1) do
1177
+ |i|
1178
+ yield @elements[i], v[i]
1179
+ end
1180
+ end
1181
+
1182
+ #
1183
+ # Collects (as in Enumerable#collect) over the elements of this vector and +v+
1184
+ # in conjunction.
1185
+ #
1186
+ def collect2(v) # :yield: e1, e2
1187
+ Vector.Raise ErrDimensionMismatch if size != v.size
1188
+ (0 .. size - 1).collect do
1189
+ |i|
1190
+ yield @elements[i], v[i]
1191
+ end
1192
+ end
1193
+
1194
+ #--
1195
+ # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1196
+ #++
1197
+
1198
+ #
1199
+ # Returns +true+ iff the two vectors have the same elements in the same order.
1200
+ #
1201
+ def ==(other)
1202
+ return false unless Vector === other
1203
+
1204
+ other.compare_by(@elements)
1205
+ end
1206
+ alias eqn? ==
1207
+
1208
+ #
1209
+ # For internal use.
1210
+ #
1211
+ def compare_by(elements)
1212
+ @elements == elements
1213
+ end
1214
+
1215
+ #
1216
+ # Return a copy of the vector.
1217
+ #
1218
+ def clone
1219
+ Vector.elements(@elements)
1220
+ end
1221
+
1222
+ #
1223
+ # Return a hash-code for the vector.
1224
+ #
1225
+ def hash
1226
+ @elements.hash
1227
+ end
1228
+
1229
+ #--
1230
+ # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1231
+ #++
1232
+
1233
+ #
1234
+ # Multiplies the vector by +x+, where +x+ is a number or another vector.
1235
+ #
1236
+ def *(x)
1237
+ case x
1238
+ when Numeric
1239
+ els = @elements.collect{|e| e * x}
1240
+ Vector.elements(els, false)
1241
+ when Matrix
1242
+ Matrix.column_vector(self) * x
1243
+ else
1244
+ s, x = x.coerce(self)
1245
+ s * x
1246
+ end
1247
+ end
1248
+
1249
+ #
1250
+ # Vector addition.
1251
+ #
1252
+ def +(v)
1253
+ case v
1254
+ when Vector
1255
+ Vector.Raise ErrDimensionMismatch if size != v.size
1256
+ els = collect2(v) {
1257
+ |v1, v2|
1258
+ v1 + v2
1259
+ }
1260
+ Vector.elements(els, false)
1261
+ when Matrix
1262
+ Matrix.column_vector(self) + v
1263
+ else
1264
+ s, x = v.coerce(self)
1265
+ s + x
1266
+ end
1267
+ end
1268
+
1269
+ #
1270
+ # Vector subtraction.
1271
+ #
1272
+ def -(v)
1273
+ case v
1274
+ when Vector
1275
+ Vector.Raise ErrDimensionMismatch if size != v.size
1276
+ els = collect2(v) {
1277
+ |v1, v2|
1278
+ v1 - v2
1279
+ }
1280
+ Vector.elements(els, false)
1281
+ when Matrix
1282
+ Matrix.column_vector(self) - v
1283
+ else
1284
+ s, x = v.coerce(self)
1285
+ s - x
1286
+ end
1287
+ end
1288
+
1289
+ #--
1290
+ # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1291
+ #++
1292
+
1293
+ #
1294
+ # Returns the inner product of this vector with the other.
1295
+ # Vector[4,7].inner_product Vector[10,1] => 47
1296
+ #
1297
+ def inner_product(v)
1298
+ Vector.Raise ErrDimensionMismatch if size != v.size
1299
+
1300
+ p = 0
1301
+ each2(v) {
1302
+ |v1, v2|
1303
+ p += v1 * v2
1304
+ }
1305
+ p
1306
+ end
1307
+
1308
+ #
1309
+ # Like Array#collect.
1310
+ #
1311
+ def collect # :yield: e
1312
+ els = @elements.collect {
1313
+ |v|
1314
+ yield v
1315
+ }
1316
+ Vector.elements(els, false)
1317
+ end
1318
+ alias map collect
1319
+
1320
+ #
1321
+ # Like Vector#collect2, but returns a Vector instead of an Array.
1322
+ #
1323
+ def map2(v) # :yield: e1, e2
1324
+ els = collect2(v) {
1325
+ |v1, v2|
1326
+ yield v1, v2
1327
+ }
1328
+ Vector.elements(els, false)
1329
+ end
1330
+
1331
+ #
1332
+ # Returns the modulus (Pythagorean distance) of the vector.
1333
+ # Vector[5,8,2].r => 9.643650761
1334
+ #
1335
+ def r
1336
+ v = 0
1337
+ for e in @elements
1338
+ v += e*e
1339
+ end
1340
+ return Math.sqrt(v)
1341
+ end
1342
+
1343
+ #--
1344
+ # CONVERTING
1345
+ #++
1346
+
1347
+ #
1348
+ # Creates a single-row matrix from this vector.
1349
+ #
1350
+ def covector
1351
+ Matrix.row_vector(self)
1352
+ end
1353
+
1354
+ #
1355
+ # Returns the elements of the vector in an array.
1356
+ #
1357
+ def to_a
1358
+ @elements.dup
1359
+ end
1360
+
1361
+ def elements_to_f
1362
+ collect{|e| e.to_f}
1363
+ end
1364
+
1365
+ def elements_to_i
1366
+ collect{|e| e.to_i}
1367
+ end
1368
+
1369
+ def elements_to_r
1370
+ collect{|e| e.to_r}
1371
+ end
1372
+
1373
+ #
1374
+ # FIXME: describe Vector#coerce.
1375
+ #
1376
+ def coerce(other)
1377
+ case other
1378
+ when Numeric
1379
+ return Matrix::Scalar.new(other), self
1380
+ else
1381
+ raise TypeError, "#{self.class} can't be coerced into #{other.class}"
1382
+ end
1383
+ end
1384
+
1385
+ #--
1386
+ # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1387
+ #++
1388
+
1389
+ #
1390
+ # Overrides Object#to_s
1391
+ #
1392
+ def to_s
1393
+ "Vector[" + @elements.join(", ") + "]"
1394
+ end
1395
+
1396
+ #
1397
+ # Overrides Object#inspect
1398
+ #
1399
+ def inspect
1400
+ str = "Vector"+@elements.inspect
1401
+ end
1402
+ end
1403
+
1404
+
1405
+ # Documentation comments:
1406
+ # - Matrix#coerce and Vector#coerce need to be documented
1407
+
1408
+ end