lib/matrix: intro Matrix with general services
authorAlexis Laferrière <alexis.laf@xymus.net>
Sun, 19 Apr 2015 14:59:11 +0000 (10:59 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Tue, 15 Sep 2015 17:17:28 +0000 (13:17 -0400)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

lib/matrix/matrix.nit [new file with mode: 0644]
lib/matrix/package.ini [new file with mode: 0644]

diff --git a/lib/matrix/matrix.nit b/lib/matrix/matrix.nit
new file mode 100644 (file)
index 0000000..ab5755d
--- /dev/null
@@ -0,0 +1,255 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services for matrices of `Float` values
+module matrix
+
+# A rectangular array of `Float`
+#
+# Require: `width > 0 and height > 0`
+class Matrix
+       super Cloneable
+
+       # Number of columns
+       var width: Int
+
+       # Number of rows
+       var height: Int
+
+       # Items of this matrix, rows by rows
+       private var items: Array[Float] is lazy do
+               return new Array[Float].filled_with(0.0, width*height)
+       end
+
+       # Create a matrix from nested sequences
+       #
+       # Require: all rows are of the same length
+       #
+       # ~~~
+       # var matrix = new Matrix.from([[1.0, 2.0],
+       #                               [3.0, 4.0]])
+       # assert matrix.to_s == """
+       # 1.0 2.0
+       # 3.0 4.0"""
+       # ~~~
+       init from(items: SequenceRead[SequenceRead[Float]])
+       do
+               if items.is_empty then
+                       init(0, 0)
+                       return
+               end
+
+               init(items.first.length, items.length)
+
+               for j in height.times do assert items[j].length == width
+
+               for j in height.times do
+                       for i in width.times do
+                               self[j, i] = items[j][i]
+                       end
+               end
+       end
+
+       # Create a matrix from an `Array[Float]` composed of rows after rows
+       #
+       # Require: `width > 0 and height > 0`
+       # Require: `array.length >= width*height`
+       #
+       # ~~~
+       # var matrix = new Matrix.from_array(2, 2, [1.0, 2.0,
+       #                                           3.0, 4.0])
+       # assert matrix.to_s == """
+       # 1.0 2.0
+       # 3.0 4.0"""
+       # ~~~
+       init from_array(width, height: Int, array: SequenceRead[Float])
+       do
+               assert width > 0
+               assert height > 0
+               assert array.length >= width*height
+
+               init(width, height)
+
+               for i in height.times do
+                       for j in width.times do
+                               self[j, i] = array[i + j*width]
+                       end
+               end
+       end
+
+       # Create an identity matrix
+       #
+       # Require: `size >= 0`
+       #
+       # ~~~
+       # var i = new Matrix.identity(3)
+       # assert i.to_s == """
+       # 1.0 0.0 0.0
+       # 0.0 1.0 0.0
+       # 0.0 0.0 1.0"""
+       # ~~~
+       new identity(size: Int)
+       do
+               assert size >= 0
+
+               var matrix = new Matrix(size, size)
+               for i in size.times do
+                       for j in size.times do
+                               matrix[j, i] = if i == j then 1.0 else 0.0
+                       end
+               end
+               return matrix
+       end
+
+       # Create a new clone of this matrix
+       redef fun clone do return new Matrix.from_array(width, height, items.clone)
+
+       # Get the value at column `y` and row `x`
+       #
+       # Require: `x >= 0 and x <= width and y >= 0 and y <= height`
+       #
+       # ~~~
+       # var matrix = new Matrix.from([[0.0, 0.1],
+       #                               [1.0, 1.1]])
+       #
+       # assert matrix[0, 0] == 0.0
+       # assert matrix[0, 1] == 0.1
+       # assert matrix[1, 0] == 1.0
+       # assert matrix[1, 1] == 1.1
+       # ~~~
+       fun [](y, x: Int): Float
+       do
+               assert x >= 0 and x < width
+               assert y >= 0 and y < height
+
+               return items[x + y*width]
+       end
+
+       # Set the `value` at row `y` and column `x`
+       #
+       # Require: `x >= 0 and x <= width and y >= 0 and y <= height`
+       #
+       # ~~~
+       # var matrix = new Matrix.identity(2)
+       #
+       # matrix[0, 0] = 0.0
+       # matrix[0, 1] = 0.1
+       # matrix[1, 0] = 1.0
+       # matrix[1, 1] = 1.1
+       #
+       # assert matrix.to_s == """
+       # 0.0 0.1
+       # 1.0 1.1"""
+       # ~~~
+       fun []=(y, x: Int, value: Float)
+       do
+               assert x >= 0 and x < width
+               assert y >= 0 and y < height
+
+               items[x + y*width] = value
+       end
+
+       # Matrix product (×)
+       #
+       # Require: `self.width == other.height`
+       #
+       # ~~~
+       # var m = new Matrix.from([[3.0, 4.0],
+       #                          [5.0, 6.0]])
+       # var i = new Matrix.identity(2)
+       #
+       # assert m * i == m
+       # assert (m * m).to_s == """
+       # 29.0 36.0
+       # 45.0 56.0"""
+       #
+       # var a = new Matrix.from([[1.0, 2.0, 3.0],
+       #                          [4.0, 5.0, 6.0]])
+       # var b = new Matrix.from([[1.0],
+       #                          [2.0],
+       #                          [3.0]])
+       # var c = a * b
+       # assert c.to_s == """
+       # 14.0
+       # 32.0"""
+       # ~~~
+       fun *(other: Matrix): Matrix
+       do
+               assert self.width == other.height
+
+               var out = new Matrix(other.width, self.height)
+               for j in self.height.times do
+                       for i in other.width.times do
+                               var sum = items.first.zero
+                               for k in self.width.times do sum += self[j, k] * other[k, i]
+                               out[j, i] = sum
+                       end
+               end
+               return out
+       end
+
+       # Iterate over the values in this matrix
+       fun iterator: MapIterator[MatrixCoordinate, Float] do return new MatrixIndexIterator(self)
+
+       redef fun to_s
+       do
+               var lines = new Array[String]
+               for y in height.times do
+                       lines.add items.subarray(y*width, width).join(" ")
+               end
+               return lines.join("\n")
+       end
+
+       redef fun ==(other) do return other isa Matrix and other.items == self.items
+       redef fun hash do return items.hash
+end
+
+private class MatrixIndexIterator
+       super MapIterator[MatrixCoordinate, Float]
+
+       var matrix: Matrix
+
+       redef var key = new MatrixCoordinate(0, 0)
+
+       redef fun is_ok do return key.y < matrix.height
+
+       redef fun item
+       do
+               assert is_ok
+               return matrix[key.y, key.x]
+       end
+
+       redef fun next
+       do
+               assert is_ok
+               var key = key
+               if key.x == matrix.width - 1 then
+                       key.x = 0
+                       key.y += 1
+               else
+                       key.x += 1
+               end
+       end
+end
+
+# Position key when iterating over the values of a matrix
+class MatrixCoordinate
+       # Index of the current column
+       var x: Int
+
+       # Index of the current row
+       var y: Int
+
+       redef fun to_s do return "({x},{y})"
+end
diff --git a/lib/matrix/package.ini b/lib/matrix/package.ini
new file mode 100644 (file)
index 0000000..4bd9013
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=matrix
+tags=lib
+maintainer=Alexis Laferrière <alexis.laf@xymus.net>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/matrix/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/matrix/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues