Some refactoring, add test file

This commit is contained in:
Peter Klimai 2019-09-03 20:35:50 +03:00
parent c31c9a9532
commit fbe7988bd0
3 changed files with 99 additions and 54 deletions

View File

@ -9,109 +9,138 @@ import scientifik.kmath.structures.Matrix
import scientifik.kmath.structures.asSequence import scientifik.kmath.structures.asSequence
import kotlin.math.pow import kotlin.math.pow
// Initial implementation of these functions is taken from: /*
// https://github.com/thomasnield/numky/blob/master/src/main/kotlin/org/nield/numky/linear/DoubleOperators.kt * Functions for convenient "numpy-like" operations with Double matrices.
*
* Initial implementation of these functions is taken from:
* https://github.com/thomasnield/numky/blob/master/src/main/kotlin/org/nield/numky/linear/DoubleOperators.kt
*
*/
fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double) = MatrixContext.real.produce(rowNum, colNum, initializer) /*
* Functions that help create a real (Double) matrix
*/
fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double) =
MatrixContext.real.produce(rowNum, colNum, initializer)
fun Sequence<DoubleArray>.toMatrix() = toList().let { fun Sequence<DoubleArray>.toMatrix() = toList().let {
MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] } MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] }
} }
operator fun Matrix<Double>.times(double: Double) = MatrixContext.real.produce(rowNum, colNum) { row, col -> fun Matrix<Double>.repeatStackVertical(n: Int) = VirtualMatrix(rowNum*n, colNum) {
this@times[row, col] * double row, col -> get(if (row == 0) 0 else row % rowNum, col)
} }
fun Matrix<Double>.square() = MatrixContext.real.produce(rowNum, colNum) { row, col -> /*
this@square[row,col].pow(2) * Operations for matrix and real number
*/
operator fun Matrix<Double>.times(double: Double) = MatrixContext.real.produce(rowNum, colNum) {
row, col -> this[row, col] * double
} }
operator fun Matrix<Double>.plus(double: Double) = MatrixContext.real.produce(rowNum, colNum) { row, col -> operator fun Matrix<Double>.plus(double: Double) = MatrixContext.real.produce(rowNum, colNum) {
this@plus[row,col] + double row, col -> this[row, col] + double
} }
operator fun Matrix<Double>.minus(double: Double) = MatrixContext.real.produce(rowNum, colNum) { row, col -> operator fun Matrix<Double>.minus(double: Double) = MatrixContext.real.produce(rowNum, colNum) {
this@minus[row,col] - double row, col -> this[row, col] - double
} }
operator fun Matrix<Double>.div(double: Double) = MatrixContext.real.produce(rowNum, colNum) { row, col -> operator fun Matrix<Double>.div(double: Double) = MatrixContext.real.produce(rowNum, colNum) {
this@div[row,col] / double row, col -> this[row, col] / double
} }
operator fun Double.times(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> operator fun Double.times(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
matrix[row,col] * this row, col -> this * matrix[row, col]
} }
operator fun Double.plus(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> operator fun Double.plus(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
matrix[row,col] + this row, col -> this * matrix[row, col]
} }
operator fun Double.minus(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> operator fun Double.minus(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
matrix[row,col] - this row, col -> this - matrix[row, col]
} }
operator fun Double.div(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col -> // TODO: does this operation make sense? Should it be 'this/matrix[row, col]'?
matrix[row,col] / this //operator fun Double.div(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
// row, col -> matrix[row, col] / this
//}
/*
* Per-element (!) square and power operations
*/
fun Matrix<Double>.square() = MatrixContext.real.produce(rowNum, colNum) {
row, col -> this[row, col].pow(2)
} }
operator fun Matrix<Double>.times(other: Matrix<Double>) = MatrixContext.real.produce(rowNum, colNum) { row, col -> fun Matrix<Double>.pow(n: Int) = MatrixContext.real.produce(rowNum, colNum) {
this@times[row,col] * other[row,col] i, j -> this[i, j].pow(n)
} }
operator fun Matrix<Double>.minus(other: Matrix<Double>) = MatrixContext.real.produce(rowNum, colNum) { row, col -> /*
this@minus[row,col] - other[row,col] * Operations on two matrices (per-element!)
*/
operator fun Matrix<Double>.times(other: Matrix<Double>) = MatrixContext.real.produce(rowNum, colNum) {
row, col -> this[row, col] * other[row, col]
} }
operator fun Matrix<Double>.plus(other: Matrix<Double>) = MatrixContext.real.add(this, other) operator fun Matrix<Double>.plus(other: Matrix<Double>) = MatrixContext.real.add(this, other)
fun Matrix<Double>.repeatStackVertical(n: Int) = VirtualMatrix(rowNum*n, colNum) { row, col -> operator fun Matrix<Double>.minus(other: Matrix<Double>) = MatrixContext.real.produce(rowNum, colNum) {
get(if (row == 0) 0 else row % rowNum, col) row, col -> this[row,col] - other[row,col]
} }
/*
* Operations on columns
*/
inline fun Matrix<Double>.appendColumn(crossinline mapper: (Buffer<Double>) -> Double) = inline fun Matrix<Double>.appendColumn(crossinline mapper: (Buffer<Double>) -> Double) =
MatrixContext.real.produce(rowNum,colNum+1) { row,col -> MatrixContext.real.produce(rowNum,colNum+1) {
row, col ->
if (col < colNum) if (col < colNum)
this[row, col] this[row, col]
else else
mapper(rows[row]) mapper(rows[row])
} }
fun Matrix<Double>.extractColumn(columnIndex: Int) = extractColumns(columnIndex..columnIndex) fun Matrix<Double>.extractColumns(columnRange: IntRange) = MatrixContext.real.produce(rowNum, columnRange.count()) {
row, col -> this[row, columnRange.first + col]
fun Matrix<Double>.extractColumns(columnRange: IntRange) = MatrixContext.real.produce(rowNum, columnRange.count()) { row, col ->
this@extractColumns[row, columnRange.start + col]
} }
fun Matrix<Double>.sumByColumn() = MatrixContext.real.produce(1, colNum) { i, j -> fun Matrix<Double>.extractColumn(columnIndex: Int) = extractColumns(columnIndex..columnIndex)
fun Matrix<Double>.sumByColumn() = MatrixContext.real.produce(1, colNum) { _, j ->
val column = columns[j] val column = columns[j]
with(elementContext) { with(elementContext) {
sum(column.asSequence()) sum(column.asSequence())
} }
} }
fun Matrix<Double>.minByColumn() = MatrixContext.real.produce(1, colNum) { i, j -> fun Matrix<Double>.minByColumn() = MatrixContext.real.produce(1, colNum) {
val column = columns[j] _, j -> columns[j].asSequence().min() ?: throw Exception("Cannot produce min on empty column")
column.asSequence().min()?:throw Exception("Cannot produce min on empty column")
} }
fun Matrix<Double>.maxByColumn() = MatrixContext.real.produce(1, colNum) { i, j -> fun Matrix<Double>.maxByColumn() = MatrixContext.real.produce(1, colNum) {
val column = columns[j] _, j -> columns[j].asSequence().max() ?: throw Exception("Cannot produce min on empty column")
column.asSequence().max()?:throw Exception("Cannot produce min on empty column")
} }
fun Matrix<Double>.averageByColumn() = MatrixContext.real.produce(1, colNum) { i, j -> fun Matrix<Double>.averageByColumn() = MatrixContext.real.produce(1, colNum) {
val column = columns[j] _, j -> columns[j].asSequence().average()
column.asSequence().average()
} }
fun Matrix<Double>.sum() = this.elements().map { (_,value) -> value }.sum() /*
* Operations processing all elements
*/
fun Matrix<Double>.min() = this.elements().map { (_,value) -> value }.min() fun Matrix<Double>.sum() = elements().map { (_, value) -> value }.sum()
fun Matrix<Double>.max() = this.elements().map { (_,value) -> value }.max() fun Matrix<Double>.min() = elements().map { (_, value) -> value }.min()
fun Matrix<Double>.average() = this.elements().map { (_,value) -> value }.average() fun Matrix<Double>.max() = elements().map { (_, value) -> value }.max()
fun Matrix<Double>.pow(n: Int) = MatrixContext.real.produce(rowNum, colNum) { i, j -> fun Matrix<Double>.average() = elements().map { (_, value) -> value }.average()
this@pow[i,j].pow(n)
}

View File

@ -0,0 +1,16 @@
package scientific.kmath.real
import scientifik.kmath.real.average
import scientifik.kmath.real.realMatrix
import scientifik.kmath.real.sum
import kotlin.test.Test
import kotlin.test.assertEquals
class RealMatrixTest {
@Test
fun testSum() {
val m = realMatrix(10, 10) { i, j -> (i + j).toDouble() }
assertEquals(m.sum(), 900.0)
assertEquals(m.average(), 9.0)
}
}