summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarcandre <marcandre@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-10-03 03:44:20 +0000
committermarcandre <marcandre@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-10-03 03:44:20 +0000
commit5b46b07bf363da57d53394e58e4bccf69ef78865 (patch)
tree787ab0d0ea19ad89d723520a6e5f41a844f5d2c2
parentf59098d1182f08594078bc458820c8ab8c972c46 (diff)
* lib/matrix.rb: Add hstack & vstack methods.
Based on a patch by creasywuqiong. [Fix GH-344] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47769 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog5
-rw-r--r--NEWS2
-rw-r--r--lib/matrix.rb73
-rw-r--r--test/matrix/test_matrix.rb35
4 files changed, 115 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 79a3bf4a4c..5b34746905 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Fri Oct 3 12:42:15 2014 Marc-Andre Lafortune <ruby-core@marc-andre.ca>
+
+ * lib/matrix.rb: Add hstack & vstack methods.
+ Based on a patch by creasywuqiong. [Fix GH-344]
+
Fri Oct 3 12:37:48 2014 Marc-Andre Lafortune <ruby-core@marc-andre.ca>
* lib/matrix.rb: Fix Matrix.rows copy bug.
diff --git a/NEWS b/NEWS
index b2fd835325..fcf2e655ed 100644
--- a/NEWS
+++ b/NEWS
@@ -78,6 +78,8 @@ with all sufficient information, see the ChangeLog file.
by deleting the specified row and column.
* Matrix#cofactor(row, column) returns the (row, column) cofactor
which is obtained by multiplying the first minor by (-1)**(row + column).
+ * hstack and vstack are new instance and class methods to stack matrices
+ horizontally and vertically.
* Method
* New methods:
diff --git a/lib/matrix.rb b/lib/matrix.rb
index 45c24d1275..81834ab898 100644
--- a/lib/matrix.rb
+++ b/lib/matrix.rb
@@ -45,6 +45,8 @@ end
# * Matrix.zero(n)
# * Matrix.row_vector(row)
# * Matrix.column_vector(column)
+# * Matrix.hstack(*matrices)
+# * Matrix.vstack(*matrices)
#
# To access Matrix elements/columns/rows/submatrices/properties:
# * #[](i, j)
@@ -90,12 +92,14 @@ end
# Matrix functions:
# * #determinant
# * #det
+# * #hstack(*matrices)
# * #rank
# * #round
# * #trace
# * #tr
# * #transpose
# * #t
+# * #vstack(*matrices)
#
# Matrix decompositions:
# * #eigen
@@ -296,6 +300,51 @@ class Matrix
end
#
+ # 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]]
+ #
+ def Matrix.vstack(x, *matrices)
+ raise TypeError, "Expected a Matrix, got a #{x.class}" unless x.is_a?(Matrix)
+ result = x.send(:rows).map(&:dup)
+ matrices.each do |m|
+ raise TypeError, "Expected a Matrix, got a #{m.class}" unless m.is_a?(Matrix)
+ if m.column_count != x.column_count
+ raise ErrDimensionMismatch, "The given matrices must have #{x.column_count} columns, but one has #{m.column_count}"
+ end
+ result.concat(m.send(:rows))
+ end
+ new result, x.column_count
+ end
+
+
+ #
+ # 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]]
+ #
+ def Matrix.hstack(x, *matrices)
+ raise TypeError, "Expected a Matrix, got a #{x.class}" unless x.is_a?(Matrix)
+ result = x.send(:rows).map(&:dup)
+ total_column_count = x.column_count
+ matrices.each do |m|
+ raise TypeError, "Expected a Matrix, got a #{m.class}" unless m.is_a?(Matrix)
+ if m.row_count != x.row_count
+ raise ErrDimensionMismatch, "The given matrices must have #{x.row_count} rows, but one has #{m.row_count}"
+ end
+ result.each_with_index do |row, i|
+ row.concat m.send(:rows)[i]
+ end
+ total_column_count += m.column_count
+ end
+ new result, total_column_count
+ end
+
+ #
# Matrix.new is private; use Matrix.rows, columns, [], etc... to create.
#
def initialize(rows, column_count = rows[0].size)
@@ -1143,6 +1192,18 @@ class Matrix
alias det_e determinant_e
#
+ # Returns a new matrix resulting by stacking horizontally
+ # the receiver with the given matrices
+ #
+ # x = Matrix[[1, 2], [3, 4]]
+ # y = Matrix[[5, 6], [7, 8]]
+ # x.hstack(y) # => Matrix[[1, 2, 5, 6], [3, 4, 7, 8]]
+ #
+ def hstack(*matrices)
+ self.class.hstack(self, *matrices)
+ end
+
+ #
# Returns the rank of the matrix.
# Beware that using Float values can yield erroneous results
# because of their lack of precision.
@@ -1223,6 +1284,18 @@ class Matrix
end
alias t transpose
+ #
+ # Returns a new matrix resulting by stacking vertically
+ # the receiver with the given matrices
+ #
+ # x = Matrix[[1, 2], [3, 4]]
+ # y = Matrix[[5, 6], [7, 8]]
+ # x.vstack(y) # => Matrix[[1, 2], [3, 4], [5, 6], [7, 8]]
+ #
+ def vstack(*matrices)
+ self.class.vstack(self, *matrices)
+ end
+
#--
# DECOMPOSITIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#++
diff --git a/test/matrix/test_matrix.rb b/test/matrix/test_matrix.rb
index 3e1e0db91e..eb62a326b6 100644
--- a/test/matrix/test_matrix.rb
+++ b/test/matrix/test_matrix.rb
@@ -1,6 +1,9 @@
require 'test/unit'
require 'matrix'
+class SubMatrix < Matrix
+end
+
class TestMatrix < Test::Unit::TestCase
def setup
@m1 = Matrix[[1,2,3], [4,5,6]]
@@ -9,6 +12,8 @@ class TestMatrix < Test::Unit::TestCase
@m4 = Matrix[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
@n1 = Matrix[[2,3,4], [5,6,7]]
@c1 = Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
+ @e1 = Matrix.empty(2,0)
+ @e2 = Matrix.empty(0,3)
end
def test_matrix
@@ -499,4 +504,34 @@ class TestMatrix < Test::Unit::TestCase
end
assert_equal(1, s1 ** o)
end
+
+ def test_hstack
+ assert_equal Matrix[[1,2,3,2,3,4,1,2,3], [4,5,6,5,6,7,4,5,6]],
+ @m1.hstack(@n1, @m1)
+ # Error checking:
+ assert_raise(TypeError) { @m1.hstack(42) }
+ assert_raise(TypeError) { Matrix.hstack(42, @m1) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @m1.hstack(Matrix.identity(3)) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @e1.hstack(@e2) }
+ # Corner cases:
+ assert_equal @m1, @m1.hstack
+ assert_equal @e1, @e1.hstack(@e1)
+ assert_equal Matrix.empty(0,6), @e2.hstack(@e2)
+ assert_equal SubMatrix, SubMatrix.hstack(@e1).class
+ end
+
+ def test_vstack
+ assert_equal Matrix[[1,2,3], [4,5,6], [2,3,4], [5,6,7], [1,2,3], [4,5,6]],
+ @m1.vstack(@n1, @m1)
+ # Error checking:
+ assert_raise(TypeError) { @m1.vstack(42) }
+ assert_raise(TypeError) { Matrix.vstack(42, @m1) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @m1.vstack(Matrix.identity(2)) }
+ assert_raise(Matrix::ErrDimensionMismatch) { @e1.vstack(@e2) }
+ # Corner cases:
+ assert_equal @m1, @m1.vstack
+ assert_equal Matrix.empty(4,0), @e1.vstack(@e1)
+ assert_equal @e2, @e2.vstack(@e2)
+ assert_equal SubMatrix, SubMatrix.vstack(@e1).class
+ end
end