For-real refactoring and test fix.
Never call equals on buffer
This commit is contained in:
parent
7e31a98dc5
commit
e5ffb22126
@ -2,7 +2,7 @@ plugins {
|
|||||||
id("scientifik.publish") apply false
|
id("scientifik.publish") apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
val kmathVersion by extra("0.1.4-dev-5")
|
val kmathVersion by extra("0.1.4-dev-6")
|
||||||
|
|
||||||
val bintrayRepo by extra("scientifik")
|
val bintrayRepo by extra("scientifik")
|
||||||
val githubProject by extra("kmath")
|
val githubProject by extra("kmath")
|
||||||
|
@ -27,7 +27,7 @@ class BufferMatrixContext<T : Any, R : Ring<T>>(
|
|||||||
@Suppress("OVERRIDE_BY_INLINE")
|
@Suppress("OVERRIDE_BY_INLINE")
|
||||||
object RealMatrixContext : GenericMatrixContext<Double, RealField> {
|
object RealMatrixContext : GenericMatrixContext<Double, RealField> {
|
||||||
|
|
||||||
override val elementContext = RealField
|
override val elementContext get() = RealField
|
||||||
|
|
||||||
override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix<Double> {
|
override inline fun produce(rows: Int, columns: Int, initializer: (i: Int, j: Int) -> Double): Matrix<Double> {
|
||||||
val buffer = DoubleBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) }
|
val buffer = DoubleBuffer(rows * columns) { offset -> initializer(offset / columns, offset % columns) }
|
||||||
@ -101,8 +101,15 @@ infix fun BufferMatrix<Double>.dot(other: BufferMatrix<Double>): BufferMatrix<Do
|
|||||||
|
|
||||||
val array = DoubleArray(this.rowNum * other.colNum)
|
val array = DoubleArray(this.rowNum * other.colNum)
|
||||||
|
|
||||||
val a = this.buffer.array
|
//convert to array to insure there is not memory indirection
|
||||||
val b = other.buffer.array
|
fun Buffer<out Double>.unsafeArray(): DoubleArray = if (this is DoubleBuffer) {
|
||||||
|
array
|
||||||
|
} else {
|
||||||
|
DoubleArray(size) { get(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val a = this.buffer.unsafeArray()
|
||||||
|
val b = other.buffer.unsafeArray()
|
||||||
|
|
||||||
for (i in (0 until rowNum)) {
|
for (i in (0 until rowNum)) {
|
||||||
for (j in (0 until other.colNum)) {
|
for (j in (0 until other.colNum)) {
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import scientifik.kmath.operations.Field
|
|
||||||
import scientifik.kmath.operations.Norm
|
|
||||||
import scientifik.kmath.operations.RealField
|
|
||||||
import scientifik.kmath.structures.Buffer
|
import scientifik.kmath.structures.Buffer
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
import scientifik.kmath.structures.VirtualBuffer
|
import scientifik.kmath.structures.VirtualBuffer
|
||||||
import scientifik.kmath.structures.asSequence
|
|
||||||
|
|
||||||
typealias Point<T> = Buffer<T>
|
typealias Point<T> = Buffer<T>
|
||||||
|
|
||||||
@ -19,8 +15,6 @@ interface LinearSolver<T : Any> {
|
|||||||
fun inverse(a: Matrix<T>): Matrix<T>
|
fun inverse(a: Matrix<T>): Matrix<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias RealMatrix = Matrix<Double>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert matrix to vector if it is possible
|
* Convert matrix to vector if it is possible
|
||||||
*/
|
*/
|
||||||
|
@ -1,14 +1,46 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.BufferFactory
|
||||||
import scientifik.kmath.structures.Structure2D
|
import scientifik.kmath.structures.Structure2D
|
||||||
import scientifik.kmath.structures.asBuffer
|
import scientifik.kmath.structures.asBuffer
|
||||||
|
|
||||||
class MatrixBuilder<T : Any>(val rows: Int, val columns: Int) {
|
class MatrixBuilder(val rows: Int, val columns: Int) {
|
||||||
operator fun invoke(vararg elements: T): FeaturedMatrix<T> {
|
operator fun <T : Any> invoke(vararg elements: T): FeaturedMatrix<T> {
|
||||||
if (rows * columns != elements.size) error("The number of elements ${elements.size} is not equal $rows * $columns")
|
if (rows * columns != elements.size) error("The number of elements ${elements.size} is not equal $rows * $columns")
|
||||||
val buffer = elements.asBuffer()
|
val buffer = elements.asBuffer()
|
||||||
return BufferMatrix(rows, columns, buffer)
|
return BufferMatrix(rows, columns, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO add specific matrix builder functions like diagonal, etc
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder<T> = MatrixBuilder(rows, columns)
|
fun Structure2D.Companion.build(rows: Int, columns: Int): MatrixBuilder = MatrixBuilder(rows, columns)
|
||||||
|
|
||||||
|
fun <T : Any> Structure2D.Companion.row(vararg values: T): FeaturedMatrix<T> {
|
||||||
|
val buffer = values.asBuffer()
|
||||||
|
return BufferMatrix(1, values.size, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> Structure2D.Companion.row(
|
||||||
|
size: Int,
|
||||||
|
factory: BufferFactory<T> = Buffer.Companion::auto,
|
||||||
|
noinline builder: (Int) -> T
|
||||||
|
): FeaturedMatrix<T> {
|
||||||
|
val buffer = factory(size, builder)
|
||||||
|
return BufferMatrix(1, size, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T : Any> Structure2D.Companion.column(vararg values: T): FeaturedMatrix<T> {
|
||||||
|
val buffer = values.asBuffer()
|
||||||
|
return BufferMatrix(values.size, 1, buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> Structure2D.Companion.column(
|
||||||
|
size: Int,
|
||||||
|
factory: BufferFactory<T> = Buffer.Companion::auto,
|
||||||
|
noinline builder: (Int) -> T
|
||||||
|
): FeaturedMatrix<T> {
|
||||||
|
val buffer = factory(size, builder)
|
||||||
|
return BufferMatrix(size, 1, buffer)
|
||||||
|
}
|
||||||
|
@ -161,36 +161,7 @@ class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
|
|||||||
override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf())
|
override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Array<T>.asBuffer() = ArrayBuffer(this)
|
fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this)
|
||||||
|
|
||||||
inline class DoubleBuffer(val array: DoubleArray) : MutableBuffer<Double> {
|
|
||||||
override val size: Int get() = array.size
|
|
||||||
|
|
||||||
override fun get(index: Int): Double = array[index]
|
|
||||||
|
|
||||||
override fun set(index: Int, value: Double) {
|
|
||||||
array[index] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun iterator() = array.iterator()
|
|
||||||
|
|
||||||
override fun copy(): MutableBuffer<Double> = DoubleBuffer(array.copyOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
|
||||||
inline fun DoubleBuffer(size: Int, init: (Int) -> Double) = DoubleBuffer(DoubleArray(size) { init(it) })
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform buffer of doubles into array for high performance operations
|
|
||||||
*/
|
|
||||||
val Buffer<out Double>.array: DoubleArray
|
|
||||||
get() = if (this is DoubleBuffer) {
|
|
||||||
array
|
|
||||||
} else {
|
|
||||||
DoubleArray(size) { get(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DoubleArray.asBuffer() = DoubleBuffer(this)
|
|
||||||
|
|
||||||
inline class ShortBuffer(val array: ShortArray) : MutableBuffer<Short> {
|
inline class ShortBuffer(val array: ShortArray) : MutableBuffer<Short> {
|
||||||
override val size: Int get() = array.size
|
override val size: Int get() = array.size
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
inline class DoubleBuffer(val array: DoubleArray) : MutableBuffer<Double> {
|
||||||
|
override val size: Int get() = array.size
|
||||||
|
|
||||||
|
override fun get(index: Int): Double = array[index]
|
||||||
|
|
||||||
|
override fun set(index: Int, value: Double) {
|
||||||
|
array[index] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun iterator() = array.iterator()
|
||||||
|
|
||||||
|
override fun copy(): MutableBuffer<Double> =
|
||||||
|
DoubleBuffer(array.copyOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer = DoubleBuffer(DoubleArray(size) { init(it) })
|
||||||
|
|
||||||
|
@Suppress("FunctionName")
|
||||||
|
fun DoubleBuffer(vararg doubles: Double): DoubleBuffer = DoubleBuffer(doubles)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform buffer of doubles into array for high performance operations
|
||||||
|
*/
|
||||||
|
val MutableBuffer<out Double>.array: DoubleArray
|
||||||
|
get() = if (this is DoubleBuffer) {
|
||||||
|
array
|
||||||
|
} else {
|
||||||
|
DoubleArray(size) { get(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun DoubleArray.asBuffer() = DoubleBuffer(this)
|
@ -20,10 +20,14 @@ interface NDStructure<T> {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean {
|
fun equals(st1: NDStructure<*>, st2: NDStructure<*>): Boolean {
|
||||||
if(st1===st2) return true
|
if (st1 === st2) return true
|
||||||
|
|
||||||
// fast comparison of buffers if possible
|
// fast comparison of buffers if possible
|
||||||
if(st1 is NDBuffer && st2 is NDBuffer && st1.strides == st2.strides){
|
if (
|
||||||
|
st1 is NDBuffer &&
|
||||||
|
st2 is NDBuffer &&
|
||||||
|
st1.strides == st2.strides
|
||||||
|
) {
|
||||||
return st1.buffer.contentEquals(st2.buffer)
|
return st1.buffer.contentEquals(st2.buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +214,7 @@ abstract class NDBuffer<T> : NDStructure<T> {
|
|||||||
class BufferNDStructure<T>(
|
class BufferNDStructure<T>(
|
||||||
override val strides: Strides,
|
override val strides: Strides,
|
||||||
override val buffer: Buffer<T>
|
override val buffer: Buffer<T>
|
||||||
) : NDBuffer<T>(){
|
) : NDBuffer<T>() {
|
||||||
init {
|
init {
|
||||||
if (strides.linearSize != buffer.size) {
|
if (strides.linearSize != buffer.size) {
|
||||||
error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}")
|
error("Expected buffer side of ${strides.linearSize}, but found ${buffer.size}")
|
||||||
|
@ -17,7 +17,7 @@ interface Structure1D<T> : NDStructure<T>, Buffer<T> {
|
|||||||
/**
|
/**
|
||||||
* A 1D wrapper for nd-structure
|
* A 1D wrapper for nd-structure
|
||||||
*/
|
*/
|
||||||
private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Structure1D<T> {
|
private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Structure1D<T>{
|
||||||
|
|
||||||
override val shape: IntArray get() = structure.shape
|
override val shape: IntArray get() = structure.shape
|
||||||
override val size: Int get() = structure.shape[0]
|
override val size: Int get() = structure.shape[0]
|
||||||
|
@ -17,7 +17,7 @@ class MatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testBuilder() {
|
fun testBuilder() {
|
||||||
val matrix = Matrix.build<Double>(2, 3)(
|
val matrix = Matrix.build(2, 3)(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
package scientifik.kmath.real
|
|
||||||
|
|
||||||
import scientifik.kmath.linear.MatrixContext
|
|
||||||
import scientifik.kmath.linear.RealMatrixContext.elementContext
|
|
||||||
import scientifik.kmath.linear.VirtualMatrix
|
|
||||||
import scientifik.kmath.operations.sum
|
|
||||||
import scientifik.kmath.structures.Buffer
|
|
||||||
import scientifik.kmath.structures.Matrix
|
|
||||||
import scientifik.kmath.structures.asSequence
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 {
|
|
||||||
MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.repeatStackVertical(n: Int) = VirtualMatrix(rowNum*n, colNum) {
|
|
||||||
row, col -> get(if (row == 0) 0 else row % rowNum, col)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 -> this[row, col] + double
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Matrix<Double>.minus(double: Double) = MatrixContext.real.produce(rowNum, colNum) {
|
|
||||||
row, col -> this[row, col] - double
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Matrix<Double>.div(double: Double) = MatrixContext.real.produce(rowNum, colNum) {
|
|
||||||
row, col -> this[row, col] / double
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Double.times(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
|
|
||||||
row, col -> this * matrix[row, col]
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Double.plus(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
|
|
||||||
row, col -> this + matrix[row, col]
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun Double.minus(matrix: Matrix<Double>) = MatrixContext.real.produce(matrix.rowNum, matrix.colNum) {
|
|
||||||
row, col -> this - matrix[row, col]
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: does this operation make sense? Should it be 'this/matrix[row, col]'?
|
|
||||||
//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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.pow(n: Int) = MatrixContext.real.produce(rowNum, colNum) {
|
|
||||||
i, j -> this[i, j].pow(n)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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>.minus(other: Matrix<Double>) = MatrixContext.real.produce(rowNum, colNum) {
|
|
||||||
row, col -> this[row,col] - other[row,col]
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Operations on columns
|
|
||||||
*/
|
|
||||||
|
|
||||||
inline fun Matrix<Double>.appendColumn(crossinline mapper: (Buffer<Double>) -> Double) =
|
|
||||||
MatrixContext.real.produce(rowNum,colNum+1) {
|
|
||||||
row, col ->
|
|
||||||
if (col < colNum)
|
|
||||||
this[row, col]
|
|
||||||
else
|
|
||||||
mapper(rows[row])
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.extractColumns(columnRange: IntRange) = MatrixContext.real.produce(rowNum, columnRange.count()) {
|
|
||||||
row, col -> this[row, columnRange.first + col]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.extractColumn(columnIndex: Int) = extractColumns(columnIndex..columnIndex)
|
|
||||||
|
|
||||||
fun Matrix<Double>.sumByColumn() = MatrixContext.real.produce(1, colNum) { _, j ->
|
|
||||||
val column = columns[j]
|
|
||||||
with(elementContext) {
|
|
||||||
sum(column.asSequence())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.minByColumn() = MatrixContext.real.produce(1, colNum) {
|
|
||||||
_, j -> columns[j].asSequence().min() ?: throw Exception("Cannot produce min on empty column")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.maxByColumn() = MatrixContext.real.produce(1, colNum) {
|
|
||||||
_, j -> columns[j].asSequence().max() ?: throw Exception("Cannot produce min on empty column")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Matrix<Double>.averageByColumn() = MatrixContext.real.produce(1, colNum) {
|
|
||||||
_, j -> columns[j].asSequence().average()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Operations processing all elements
|
|
||||||
*/
|
|
||||||
|
|
||||||
fun Matrix<Double>.sum() = elements().map { (_, value) -> value }.sum()
|
|
||||||
|
|
||||||
fun Matrix<Double>.min() = elements().map { (_, value) -> value }.min()
|
|
||||||
|
|
||||||
fun Matrix<Double>.max() = elements().map { (_, value) -> value }.max()
|
|
||||||
|
|
||||||
fun Matrix<Double>.average() = elements().map { (_, value) -> value }.average()
|
|
@ -0,0 +1,8 @@
|
|||||||
|
package scientifik.kmath.real
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.DoubleBuffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* C
|
||||||
|
*/
|
||||||
|
fun DoubleBuffer.contentEquals(vararg doubles: Double) = array.contentEquals(doubles)
|
@ -0,0 +1,161 @@
|
|||||||
|
package scientifik.kmath.real
|
||||||
|
|
||||||
|
import scientifik.kmath.linear.MatrixContext
|
||||||
|
import scientifik.kmath.linear.RealMatrixContext.elementContext
|
||||||
|
import scientifik.kmath.linear.VirtualMatrix
|
||||||
|
import scientifik.kmath.operations.sum
|
||||||
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.DoubleBuffer
|
||||||
|
import scientifik.kmath.structures.Matrix
|
||||||
|
import scientifik.kmath.structures.asIterable
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions that help create a real (Double) matrix
|
||||||
|
*/
|
||||||
|
|
||||||
|
typealias RealMatrix = Matrix<Double>
|
||||||
|
|
||||||
|
fun realMatrix(rowNum: Int, colNum: Int, initializer: (i: Int, j: Int) -> Double): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum, initializer)
|
||||||
|
|
||||||
|
fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let {
|
||||||
|
MatrixContext.real.produce(it.size, it[0].size) { row, col -> it[row][col] }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.repeatStackVertical(n: Int): RealMatrix =
|
||||||
|
VirtualMatrix(rowNum * n, colNum) { row, col ->
|
||||||
|
get(if (row == 0) 0 else row % rowNum, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Operations for matrix and real number
|
||||||
|
*/
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.times(double: Double): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col] * double
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.plus(double: Double): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col] + double
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.minus(double: Double): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col] - double
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.div(double: Double): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col] / double
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Double.times(matrix: Matrix<Double>): RealMatrix =
|
||||||
|
MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col ->
|
||||||
|
this * matrix[row, col]
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Double.plus(matrix: Matrix<Double>): RealMatrix =
|
||||||
|
MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col ->
|
||||||
|
this + matrix[row, col]
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Double.minus(matrix: Matrix<Double>): RealMatrix =
|
||||||
|
MatrixContext.real.produce(matrix.rowNum, matrix.colNum) { row, col ->
|
||||||
|
this - matrix[row, col]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: does this operation make sense? Should it be 'this/matrix[row, col]'?
|
||||||
|
//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(): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col].pow(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.pow(n: Int): RealMatrix = MatrixContext.real.produce(rowNum, colNum) { i, j ->
|
||||||
|
this[i, j].pow(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Operations on two matrices (per-element!)
|
||||||
|
*/
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.times(other: Matrix<Double>): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col] * other[row, col]
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.plus(other: Matrix<Double>): RealMatrix =
|
||||||
|
MatrixContext.real.add(this, other)
|
||||||
|
|
||||||
|
operator fun Matrix<Double>.minus(other: Matrix<Double>): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum) { row, col ->
|
||||||
|
this[row, col] - other[row, col]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Operations on columns
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline fun Matrix<Double>.appendColumn(crossinline mapper: (Buffer<Double>) -> Double) =
|
||||||
|
MatrixContext.real.produce(rowNum, colNum + 1) { row, col ->
|
||||||
|
if (col < colNum)
|
||||||
|
this[row, col]
|
||||||
|
else
|
||||||
|
mapper(rows[row])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.extractColumns(columnRange: IntRange): RealMatrix =
|
||||||
|
MatrixContext.real.produce(rowNum, columnRange.count()) { row, col ->
|
||||||
|
this[row, columnRange.first + col]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.extractColumn(columnIndex: Int): RealMatrix =
|
||||||
|
extractColumns(columnIndex..columnIndex)
|
||||||
|
|
||||||
|
fun Matrix<Double>.sumByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
||||||
|
val column = columns[j]
|
||||||
|
with(elementContext) {
|
||||||
|
sum(column.asIterable())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.minByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
||||||
|
columns[j].asIterable().min() ?: throw Exception("Cannot produce min on empty column")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.maxByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
||||||
|
columns[j].asIterable().max() ?: throw Exception("Cannot produce min on empty column")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Matrix<Double>.averageByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j ->
|
||||||
|
columns[j].asIterable().average()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Operations processing all elements
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Matrix<Double>.sum() = elements().map { (_, value) -> value }.sum()
|
||||||
|
|
||||||
|
fun Matrix<Double>.min() = elements().map { (_, value) -> value }.min()
|
||||||
|
|
||||||
|
fun Matrix<Double>.max() = elements().map { (_, value) -> value }.max()
|
||||||
|
|
||||||
|
fun Matrix<Double>.average() = elements().map { (_, value) -> value }.average()
|
@ -1,11 +1,12 @@
|
|||||||
package scientific.kmath.real
|
package scientific.kmath.real
|
||||||
|
|
||||||
import scientifik.kmath.real.*
|
|
||||||
import scientifik.kmath.linear.VirtualMatrix
|
import scientifik.kmath.linear.VirtualMatrix
|
||||||
import scientifik.kmath.linear.build
|
import scientifik.kmath.linear.build
|
||||||
|
import scientifik.kmath.real.*
|
||||||
import scientifik.kmath.structures.Matrix
|
import scientifik.kmath.structures.Matrix
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class RealMatrixTest {
|
class RealMatrixTest {
|
||||||
@Test
|
@Test
|
||||||
@ -19,72 +20,72 @@ class RealMatrixTest {
|
|||||||
fun testSequenceToMatrix() {
|
fun testSequenceToMatrix() {
|
||||||
val m = Sequence<DoubleArray> {
|
val m = Sequence<DoubleArray> {
|
||||||
listOf(
|
listOf(
|
||||||
DoubleArray(10) { 10.0 },
|
DoubleArray(10) { 10.0 },
|
||||||
DoubleArray(10) { 20.0 },
|
DoubleArray(10) { 20.0 },
|
||||||
DoubleArray(10) { 30.0 }).iterator()
|
DoubleArray(10) { 30.0 }).iterator()
|
||||||
}.toMatrix()
|
}.toMatrix()
|
||||||
assertEquals(m.sum(), 20.0 * 30)
|
assertEquals(m.sum(), 20.0 * 30)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeatStackVertical() {
|
fun testRepeatStackVertical() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 3)(
|
val matrix1 = Matrix.build(2, 3)(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
val matrix2 = Matrix.build<Double>(6, 3)(
|
val matrix2 = Matrix.build(6, 3)(
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0,
|
0.0, 1.0, 2.0,
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0,
|
0.0, 1.0, 2.0,
|
||||||
1.0, 0.0, 0.0,
|
1.0, 0.0, 0.0,
|
||||||
0.0, 1.0, 2.0
|
0.0, 1.0, 2.0
|
||||||
)
|
)
|
||||||
assertEquals(VirtualMatrix.wrap(matrix2), matrix1.repeatStackVertical(3))
|
assertEquals(VirtualMatrix.wrap(matrix2), matrix1.repeatStackVertical(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMatrixAndDouble() {
|
fun testMatrixAndDouble() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 3)(
|
val matrix1 = Matrix.build(2, 3)(
|
||||||
1.0, 0.0, 3.0,
|
1.0, 0.0, 3.0,
|
||||||
4.0, 6.0, 2.0
|
4.0, 6.0, 2.0
|
||||||
)
|
)
|
||||||
val matrix2 = (matrix1 * 2.5 + 1.0 - 2.0) / 2.0
|
val matrix2 = (matrix1 * 2.5 + 1.0 - 2.0) / 2.0
|
||||||
val expectedResult = Matrix.build<Double>(2, 3)(
|
val expectedResult = Matrix.build(2, 3)(
|
||||||
0.75, -0.5, 3.25,
|
0.75, -0.5, 3.25,
|
||||||
4.5, 7.0, 2.0
|
4.5, 7.0, 2.0
|
||||||
)
|
)
|
||||||
assertEquals(matrix2, expectedResult)
|
assertEquals(matrix2, expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDoubleAndMatrix() {
|
fun testDoubleAndMatrix() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 3)(
|
val matrix1 = Matrix.build(2, 3)(
|
||||||
1.0, 0.0, 3.0,
|
1.0, 0.0, 3.0,
|
||||||
4.0, 6.0, 2.0
|
4.0, 6.0, 2.0
|
||||||
)
|
)
|
||||||
val matrix2 = 20.0 - (10.0 + (5.0 * matrix1))
|
val matrix2 = 20.0 - (10.0 + (5.0 * matrix1))
|
||||||
//val matrix2 = 10.0 + (5.0 * matrix1)
|
//val matrix2 = 10.0 + (5.0 * matrix1)
|
||||||
val expectedResult = Matrix.build<Double>(2, 3)(
|
val expectedResult = Matrix.build(2, 3)(
|
||||||
5.0, 10.0, -5.0,
|
5.0, 10.0, -5.0,
|
||||||
-10.0, -20.0, 0.0
|
-10.0, -20.0, 0.0
|
||||||
)
|
)
|
||||||
assertEquals(matrix2, expectedResult)
|
assertEquals(matrix2, expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSquareAndPower() {
|
fun testSquareAndPower() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 3)(
|
val matrix1 = Matrix.build(2, 3)(
|
||||||
-1.0, 0.0, 3.0,
|
-1.0, 0.0, 3.0,
|
||||||
4.0, -6.0, -2.0
|
4.0, -6.0, -2.0
|
||||||
)
|
)
|
||||||
val matrix2 = Matrix.build<Double>(2, 3)(
|
val matrix2 = Matrix.build(2, 3)(
|
||||||
1.0, 0.0, 9.0,
|
1.0, 0.0, 9.0,
|
||||||
16.0, 36.0, 4.0
|
16.0, 36.0, 4.0
|
||||||
)
|
)
|
||||||
val matrix3 = Matrix.build<Double>(2, 3)(
|
val matrix3 = Matrix.build(2, 3)(
|
||||||
-1.0, 0.0, 27.0,
|
-1.0, 0.0, 27.0,
|
||||||
64.0, -216.0, -8.0
|
64.0, -216.0, -8.0
|
||||||
)
|
)
|
||||||
assertEquals(matrix1.square(), matrix2)
|
assertEquals(matrix1.square(), matrix2)
|
||||||
assertEquals(matrix1.pow(3), matrix3)
|
assertEquals(matrix1.pow(3), matrix3)
|
||||||
@ -92,51 +93,61 @@ class RealMatrixTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testTwoMatrixOperations() {
|
fun testTwoMatrixOperations() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 3)(
|
val matrix1 = Matrix.build(2, 3)(
|
||||||
-1.0, 0.0, 3.0,
|
-1.0, 0.0, 3.0,
|
||||||
4.0, -6.0, 7.0
|
4.0, -6.0, 7.0
|
||||||
)
|
)
|
||||||
val matrix2 = Matrix.build<Double>(2, 3)(
|
val matrix2 = Matrix.build(2, 3)(
|
||||||
1.0, 0.0, 3.0,
|
1.0, 0.0, 3.0,
|
||||||
4.0, 6.0, -2.0
|
4.0, 6.0, -2.0
|
||||||
)
|
)
|
||||||
val result = matrix1 * matrix2 + matrix1 - matrix2
|
val result = matrix1 * matrix2 + matrix1 - matrix2
|
||||||
val expectedResult = Matrix.build<Double>(2, 3)(
|
val expectedResult = Matrix.build(2, 3)(
|
||||||
-3.0, 0.0, 9.0,
|
-3.0, 0.0, 9.0,
|
||||||
16.0, -48.0, -5.0
|
16.0, -48.0, -5.0
|
||||||
)
|
)
|
||||||
assertEquals(result, expectedResult)
|
assertEquals(result, expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testColumnOperations() {
|
fun testColumnOperations() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 4)(
|
val matrix1 = Matrix.build(2, 4)(
|
||||||
-1.0, 0.0, 3.0, 15.0,
|
-1.0, 0.0, 3.0, 15.0,
|
||||||
4.0, -6.0, 7.0, -11.0
|
4.0, -6.0, 7.0, -11.0
|
||||||
)
|
)
|
||||||
val matrix2 = Matrix.build<Double>(2, 5)(
|
val matrix2 = Matrix.build(2, 5)(
|
||||||
-1.0, 0.0, 3.0, 15.0, -1.0,
|
-1.0, 0.0, 3.0, 15.0, -1.0,
|
||||||
4.0, -6.0, 7.0, -11.0, 4.0
|
4.0, -6.0, 7.0, -11.0, 4.0
|
||||||
)
|
)
|
||||||
val col1 = Matrix.build<Double>(2, 1)(0.0, -6.0)
|
val col1 = Matrix.build(2, 1)(0.0, -6.0)
|
||||||
val cols1to2 = Matrix.build<Double>(2, 2)(
|
val cols1to2 = Matrix.build(2, 2)(
|
||||||
0.0, 3.0,
|
0.0, 3.0,
|
||||||
-6.0, 7.0
|
-6.0, 7.0
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(matrix1.appendColumn { it[0] }, matrix2)
|
assertEquals(matrix1.appendColumn { it[0] }, matrix2)
|
||||||
assertEquals(matrix1.extractColumn(1), col1)
|
assertEquals(matrix1.extractColumn(1), col1)
|
||||||
assertEquals(matrix1.extractColumns(1..2), cols1to2)
|
assertEquals(matrix1.extractColumns(1..2), cols1to2)
|
||||||
assertEquals(matrix1.sumByColumn(), Matrix.build<Double>(1, 4)(3.0, -6.0, 10.0, 4.0))
|
//equals should never be called on buffers
|
||||||
assertEquals(matrix1.minByColumn(), Matrix.build<Double>(1, 4)(-1.0, -6.0, 3.0, -11.0))
|
assertTrue {
|
||||||
assertEquals(matrix1.maxByColumn(), Matrix.build<Double>(1, 4)(4.0, 0.0, 7.0, 15.0))
|
matrix1.sumByColumn().contentEquals(3.0, -6.0, 10.0, 4.0)
|
||||||
assertEquals(matrix1.averageByColumn(), Matrix.build<Double>(1, 4)(1.5, -3.0, 5.0, 2.0))
|
} //assertEquals(matrix1.sumByColumn(), DoubleBuffer(3.0, -6.0, 10.0, 4.0))
|
||||||
|
assertTrue {
|
||||||
|
matrix1.minByColumn().contentEquals(-1.0, -6.0, 3.0, -11.0)
|
||||||
|
} //assertEquals(matrix1.minByColumn(), DoubleBuffer(-1.0, -6.0, 3.0, -11.0))
|
||||||
|
assertTrue {
|
||||||
|
matrix1.maxByColumn().contentEquals(4.0, 0.0, 7.0, 15.0)
|
||||||
|
} //assertEquals(matrix1.maxByColumn(), DoubleBuffer(4.0, 0.0, 7.0, 15.0))
|
||||||
|
assertTrue {
|
||||||
|
matrix1.averageByColumn().contentEquals(1.5, -3.0, 5.0, 2.0)
|
||||||
|
} //assertEquals(matrix1.averageByColumn(), DoubleBuffer(1.5, -3.0, 5.0, 2.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAllElementOperations() {
|
fun testAllElementOperations() {
|
||||||
val matrix1 = Matrix.build<Double>(2, 4)(
|
val matrix1 = Matrix.build(2, 4)(
|
||||||
-1.0, 0.0, 3.0, 15.0,
|
-1.0, 0.0, 3.0, 15.0,
|
||||||
4.0, -6.0, 7.0, -11.0
|
4.0, -6.0, 7.0, -11.0
|
||||||
)
|
)
|
||||||
assertEquals(matrix1.sum(), 11.0)
|
assertEquals(matrix1.sum(), 11.0)
|
||||||
assertEquals(matrix1.min(), -11.0)
|
assertEquals(matrix1.min(), -11.0)
|
||||||
|
Loading…
Reference in New Issue
Block a user