Skip to content

Instantly share code, notes, and snippets.

@h0tk3y
Created October 2, 2016 17:46
Show Gist options
  • Select an option

  • Save h0tk3y/5e55e52fb7710c742ae9b2c0b6692ea8 to your computer and use it in GitHub Desktop.

Select an option

Save h0tk3y/5e55e52fb7710c742ae9b2c0b6692ea8 to your computer and use it in GitHub Desktop.
import java.util.*
/**
* Represents a rectangular matrix of doubles
* Created by Sergey on 19.02.2015.
*/
data class Matrix(val rows: List<List<Double>>) {
constructor(vararg rows: List<Double>): this(rows.toList())
init {
if (rows.size == 0)
throw IllegalArgumentException("Matrix components should have non-zero length.")
if (rows.first().size.let { sz -> rows.any { it.size != sz } })
throw IllegalArgumentException("All of the matrix rows should have the same length.")
}
/**
* Checks if all the components of this and another matrix differ not more than by epsilon.
* @param that A matrix to compare with.
* @param epsilon Permitted difference.
* @return For all i, j: |this[i, j] - that[i, j]| <= epsilon
*/
fun similar(that: Matrix, epsilon: Double = SIMILARITY_EPSILON): Boolean {
if (height != that.height || width != that.width) return false
return rowIndices.all { i ->
colIndices.all { j ->
Math.abs(this[i, j] - that[i, j]) <= epsilon
}
}
}
/**
* Calculates the matrix m-norm.
* @return ||A|| = max {1..n} { Sum {1..m} |a_ij| }
*/
fun norm(): Double {
return rows.map { it.max()!! }.max()!!
}
/**
* Calculates the matrix conditionality value.
* @return Cond A = ||A|| * ||A^-1||
*/
fun conditionality(): Double {
return norm() * reversed()!!.norm()
}
/**
* Returns an item at i-th row, j-th column
* @param i Row
* @param j Column
* @return Matrix item at i-th row, j-th column
*/
operator fun get(i: Int, j: Int): Double {
return rows[i][j]
}
val width: Int get() = rows[0].size
val height: Int get() = rows.size
public val rowIndices: IntRange get() = rows.indices
public val colIndices: IntRange get() = rows[0].indices
/**
* Adds this matrix to another one.
* @param that Matrix to add this to.
* @return A = this + m
*/
operator fun plus(that: Matrix): Matrix {
if (height != that.height || width != that.width)
throw IllegalArgumentException("Matrix should have the same size to be added.")
return Matrix(
rowIndices.map { i ->
colIndices.map { j -> this[i, j] + that[i, j] }
})
}
operator fun minus(b: Matrix): Matrix {
return this + b * -1.0
}
/**
* Multiplies this matrix by another one.
* @param m Matrix to multiply this by.
* @return A = this * m;
*/
operator fun times(m: Matrix): Matrix {
if (width != m.height)
throw IllegalArgumentException("Matrices cannot be multiplied due to their dimensions.")
return Matrix(
rowIndices.map { i ->
m.colIndices.map { j ->
colIndices.map { k -> get(i, k) * m[k, j] }.sum()
}
})
}
/**
* Multiplies this matrix by number.
* @param a Number to multiply this matrix by.
* @return A = this * a
*/
operator fun times(a: Double): Matrix = Matrix(
rowIndices.map { i ->
colIndices.map { j -> this[i, j] * a }
})
/**
* Multiplies the matrix by vector v as Av.
* @param v Vector to multiply the matrix by.
* @return Vector y = Av.
*/
operator fun times(v: Vector): Vector {
if (width != v.dimensions)
throw IllegalArgumentException("Vector cannot be multiplied due to its dimensions.")
return Vector(rowIndices.map { i ->
colIndices.map { j -> this[i, j] * v[j] }.sum()
})
}
/**
* Get an array of the matrix' diagonal items.
* @return a_i = A_i_i {1..n}
*/
fun diagonal(): List<Double> = (0..Math.min(height, width) - 1).map { this[it, it] }
/**
* Calculates square matrix determinant.
* @return det(this)
*/
fun determinant(): Double {
if (height != width)
throw NotImplementedError()
if (height == 1)
return this[0, 0]
if (height == 2)
return this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1]
var result = 0.0
for (i in rowIndices)
result += (if (i % 2 == 0) 1.0 else -1.0) * this[i, 0] * minor(i, 0).determinant()
return result
}
/**
* Calculates the reversed matrix A^-1, so that A*A^-1 = E.
* @return null, if this has determinant approximately equal to zero. this ^ -1 otherwise.
*/
fun reversed(): Matrix? {
val det = determinant()
if (Math.abs(det) < REVERSE_DETERMINANT_EPSILON)
return null
return Matrix(
rowIndices.map { i ->
colIndices.map { j ->
(if ((i + j) % 2 == 0) 1 else -1) * minor(i, j).determinant() / det
}
})
}
/**
* @return (A^T * A)^-1 * A^T
*/
fun pseudoReversed() = transposed().let { (it * this).reversed()?.times(it) }
/**
* Makes a transposed matrix.
* @return a_i_j = this_j_i
*/
fun transposed(): Matrix = Matrix(
colIndices.map { i ->
rowIndices.map { j ->
this[j, i]
}
})
/**
* Makes a minor matrix, which is matrix without one row and one column.
* @param a Row to delete
* @param b Column to delete
* @return Matrix of size [n-1 x n-1]
*/
fun minor(a: Int, b: Int): Matrix = Matrix(
(0..height - 2).map { i ->
(0..width - 2).map { j ->
this[if (i < a) i else i + 1, if (j < b) j else j + 1]
}
})
override fun toString() =
StringBuilder().apply {
for (i in rowIndices) {
for (j in colIndices) {
append(("%$TO_STRING_DIGITS.${TO_STRING_DIGITS}f").format(get(i, j)) + " ")
}
append("\n")
}
}.toString()
/**
* Returns a Vector-representation of specified row
*/
fun getRow(i: Int): Vector {
if (i < 0 || i >= rows.size)
throw IllegalArgumentException("invalid row number\n")
return Vector(ArrayList(rows[i]))
}
companion object {
val REVERSE_DETERMINANT_EPSILON = 1e-14
val SIMILARITY_EPSILON = 1e-6
/**
* Creates an unit matrix of size [n x n].
* @param dimensions n -- size of the matrix.
* @return A of size [n x n] with A_i_j = "i == j"
*/
fun unit(dimensions: Int): Matrix = Matrix(
(0..dimensions - 1).map { i ->
(0..dimensions - 1).map { j -> if (i == j) 1.0 else 0.0 }
})
fun zero(dimensions: Int): Matrix {
return zero(dimensions, dimensions)
}
/**
* Creates a matrix filled with zeros.
* @param height Created matrix height.
* @param width Created matrix width.
* @return Matrix filled with zeros of size [height x width].
*/
fun zero(height: Int, width: Int): Matrix = Matrix(
(0..height - 1).map { i ->
(0..width - 1).map { j -> 0.0 }
})
val TO_STRING_DIGITS = 3
fun byTrace(trace: DoubleArray): Matrix = Matrix(
trace.indices.map { i ->
trace.indices.map { j ->
if (i == j) trace[i] else 0.0
}
})
fun parse(s: String): Matrix {
val splitPoints = ArrayList<Int>()
var bracesBalance = 0
for (i in 0..s.length - 1) {
if (s[i] == '{') {
++bracesBalance
} else if (s[i] == '}') {
--bracesBalance
} else if (s[i] == ',' && bracesBalance == 0) {
splitPoints.add(i)
}
}
splitPoints.add(s.length)
val components = splitPoints.map { ArrayList<Double>() }
for (i in splitPoints.indices) {
val line = s.substring(if (i == 0) 0 else splitPoints[i - 1] + 1, splitPoints[i] - 1).trim { it <= ' ' }
.replace("[\\}\\{,]".toRegex(), " ").split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }
for (j in 1..line.size - 1)
components[i] += line[j].toDouble()
}
return Matrix(components)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment