forked from kscience/kmath
All fields in Structures, Vectors and Matrices are now generic
This commit is contained in:
parent
29da5bad28
commit
5e3e0eb09d
@ -2,6 +2,7 @@ package scientifik.kmath.histogram
|
|||||||
|
|
||||||
import scientifik.kmath.structures.ArrayBuffer
|
import scientifik.kmath.structures.ArrayBuffer
|
||||||
import scientifik.kmath.structures.Buffer
|
import scientifik.kmath.structures.Buffer
|
||||||
|
import scientifik.kmath.structures.DoubleBuffer
|
||||||
|
|
||||||
typealias Point<T> = Buffer<T>
|
typealias Point<T> = Buffer<T>
|
||||||
|
|
||||||
@ -49,6 +50,8 @@ interface MutableHistogram<T: Any, out B : Bin<T>>: Histogram<T,B>{
|
|||||||
|
|
||||||
fun <T: Any> MutableHistogram<T,*>.put(vararg point: T) = put(ArrayBuffer(point))
|
fun <T: Any> MutableHistogram<T,*>.put(vararg point: T) = put(ArrayBuffer(point))
|
||||||
|
|
||||||
|
fun MutableHistogram<Double,*>.put(vararg point: Number) = put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray()))
|
||||||
|
|
||||||
fun <T: Any> MutableHistogram<T,*>.fill(sequence: Iterable<Point<T>>) = sequence.forEach { put(it) }
|
fun <T: Any> MutableHistogram<T,*>.fill(sequence: Iterable<Point<T>>) = sequence.forEach { put(it) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4,7 +4,7 @@ import scientifik.kmath.linear.Vector
|
|||||||
import scientifik.kmath.operations.Space
|
import scientifik.kmath.operations.Space
|
||||||
import scientifik.kmath.structures.NDStructure
|
import scientifik.kmath.structures.NDStructure
|
||||||
|
|
||||||
data class BinTemplate<T : Comparable<T>>(val center: Vector<T>, val sizes: Vector<T>) {
|
data class BinTemplate<T : Comparable<T>>(val center: Vector<T, *>, val sizes: Vector<T, *>) {
|
||||||
fun contains(vector: Point<out T>): Boolean {
|
fun contains(vector: Point<out T>): Boolean {
|
||||||
if (vector.size != center.size) error("Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}")
|
if (vector.size != center.size) error("Dimension mismatch for input vector. Expected ${center.size}, but found ${vector.size}")
|
||||||
val upper = center + sizes / 2.0
|
val upper = center + sizes / 2.0
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.DoubleField
|
||||||
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.structures.MutableNDStructure
|
import scientifik.kmath.structures.MutableNDStructure
|
||||||
import scientifik.kmath.structures.NDStructure
|
import scientifik.kmath.structures.NDStructure
|
||||||
import scientifik.kmath.structures.genericNdStructure
|
import scientifik.kmath.structures.genericNdStructure
|
||||||
@ -9,7 +11,7 @@ import kotlin.math.absoluteValue
|
|||||||
/**
|
/**
|
||||||
* Implementation based on Apache common-maths LU-decomposition
|
* Implementation based on Apache common-maths LU-decomposition
|
||||||
*/
|
*/
|
||||||
abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
abstract class LUDecomposition<T : Comparable<T>, F : Field<T>>(val matrix: Matrix<T, F>) {
|
||||||
|
|
||||||
private val field get() = matrix.context.field
|
private val field get() = matrix.context.field
|
||||||
/** Entries of LU decomposition. */
|
/** Entries of LU decomposition. */
|
||||||
@ -31,7 +33,7 @@ abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
|||||||
* L is a lower-triangular matrix
|
* L is a lower-triangular matrix
|
||||||
* @return the L matrix (or null if decomposed matrix is singular)
|
* @return the L matrix (or null if decomposed matrix is singular)
|
||||||
*/
|
*/
|
||||||
val l: Matrix<out T> by lazy {
|
val l: Matrix<out T, F> by lazy {
|
||||||
matrix.context.produce { i, j ->
|
matrix.context.produce { i, j ->
|
||||||
when {
|
when {
|
||||||
j < i -> lu[i, j]
|
j < i -> lu[i, j]
|
||||||
@ -48,7 +50,7 @@ abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
|||||||
* U is an upper-triangular matrix
|
* U is an upper-triangular matrix
|
||||||
* @return the U matrix (or null if decomposed matrix is singular)
|
* @return the U matrix (or null if decomposed matrix is singular)
|
||||||
*/
|
*/
|
||||||
val u: Matrix<out T> by lazy {
|
val u: Matrix<out T, F> by lazy {
|
||||||
matrix.context.produce { i, j ->
|
matrix.context.produce { i, j ->
|
||||||
if (j >= i) lu[i, j] else field.zero
|
if (j >= i) lu[i, j] else field.zero
|
||||||
}
|
}
|
||||||
@ -64,7 +66,7 @@ abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
|||||||
* @return the P rows permutation matrix (or null if decomposed matrix is singular)
|
* @return the P rows permutation matrix (or null if decomposed matrix is singular)
|
||||||
* @see .getPivot
|
* @see .getPivot
|
||||||
*/
|
*/
|
||||||
val p: Matrix<out T> by lazy {
|
val p: Matrix<out T, F> by lazy {
|
||||||
matrix.context.produce { i, j ->
|
matrix.context.produce { i, j ->
|
||||||
//TODO ineffective. Need sparse matrix for that
|
//TODO ineffective. Need sparse matrix for that
|
||||||
if (j == pivot[i]) field.one else field.zero
|
if (j == pivot[i]) field.one else field.zero
|
||||||
@ -181,7 +183,7 @@ abstract class LUDecomposition<T : Comparable<T>>(val matrix: Matrix<T>) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class RealLUDecomposition(matrix: Matrix<Double>, private val singularityThreshold: Double = DEFAULT_TOO_SMALL) : LUDecomposition<Double>(matrix) {
|
class RealLUDecomposition(matrix: RealMatrix, private val singularityThreshold: Double = DEFAULT_TOO_SMALL) : LUDecomposition<Double, DoubleField>(matrix) {
|
||||||
override fun isSingular(value: Double): Boolean {
|
override fun isSingular(value: Double): Boolean {
|
||||||
return value.absoluteValue < singularityThreshold
|
return value.absoluteValue < singularityThreshold
|
||||||
}
|
}
|
||||||
@ -194,12 +196,12 @@ class RealLUDecomposition(matrix: Matrix<Double>, private val singularityThresho
|
|||||||
|
|
||||||
|
|
||||||
/** Specialized solver. */
|
/** Specialized solver. */
|
||||||
object RealLUSolver : LinearSolver<Double> {
|
object RealLUSolver : LinearSolver<Double, DoubleField> {
|
||||||
|
|
||||||
|
|
||||||
fun decompose(mat: Matrix<Double>, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold)
|
fun decompose(mat: Matrix<Double, DoubleField>, threshold: Double = 1e-11): RealLUDecomposition = RealLUDecomposition(mat, threshold)
|
||||||
|
|
||||||
override fun solve(a: Matrix<Double>, b: Matrix<Double>): Matrix<Double> {
|
override fun solve(a: RealMatrix, b: RealMatrix): RealMatrix {
|
||||||
val decomposition = decompose(a, a.context.field.zero)
|
val decomposition = decompose(a, a.context.field.zero)
|
||||||
|
|
||||||
if (b.rows != a.rows) {
|
if (b.rows != a.rows) {
|
||||||
|
@ -1,184 +1,17 @@
|
|||||||
package scientifik.kmath.linear
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
import scientifik.kmath.operations.*
|
import scientifik.kmath.operations.DoubleField
|
||||||
import scientifik.kmath.structures.ExtendedNDField
|
import scientifik.kmath.operations.Field
|
||||||
import scientifik.kmath.structures.GenericNDField
|
import scientifik.kmath.operations.Norm
|
||||||
import scientifik.kmath.structures.NDField
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The space for linear elements. Supports scalar product alongside with standard linear operations.
|
|
||||||
* @param T type of individual element of the vector or matrix
|
|
||||||
* @param V the type of vector space element
|
|
||||||
*/
|
|
||||||
abstract class MatrixSpace<T : Any>(val rows: Int, val columns: Int, val field: Field<T>) : Space<Matrix<T>> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce the element of this space
|
|
||||||
*/
|
|
||||||
abstract fun produce(initializer: (Int, Int) -> T): Matrix<T>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce new matrix space with given dimensions. The space produced could be raised from cache since [MatrixSpace] does not have mutable elements
|
|
||||||
*/
|
|
||||||
abstract fun produceSpace(rows: Int, columns: Int): MatrixSpace<T>
|
|
||||||
|
|
||||||
override val zero: Matrix<T> by lazy {
|
|
||||||
produce { _, _ -> field.zero }
|
|
||||||
}
|
|
||||||
|
|
||||||
// val one: Matrix<T> by lazy {
|
|
||||||
// produce { i, j -> if (i == j) field.one else field.zero }
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun add(a: Matrix<T>, b: Matrix<T>): Matrix<T> {
|
|
||||||
return produce { i, j -> with(field) { a[i, j] + b[i, j] } }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun multiply(a: Matrix<T>, k: Double): Matrix<T> {
|
|
||||||
//TODO it is possible to implement scalable linear elements which normed values and adjustable scale to save memory and processing poser
|
|
||||||
return produce { i, j -> with(field) { a[i, j] * k } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dot product. Throws exception on dimension mismatch
|
|
||||||
*/
|
|
||||||
fun multiply(a: Matrix<T>, b: Matrix<T>): Matrix<T> {
|
|
||||||
if (a.rows != b.columns) {
|
|
||||||
//TODO replace by specific exception
|
|
||||||
error("Dimension mismatch in linear structure dot product: [${a.rows},${a.columns}]*[${b.rows},${b.columns}]")
|
|
||||||
}
|
|
||||||
return produceSpace(a.rows, b.columns).produce { i, j ->
|
|
||||||
(0 until a.columns).asSequence().map { k -> field.multiply(a[i, k], b[k, j]) }.reduce { first, second -> field.add(first, second) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (this === other) return true
|
|
||||||
if (other !is MatrixSpace<*>) return false
|
|
||||||
|
|
||||||
if (rows != other.rows) return false
|
|
||||||
if (columns != other.columns) return false
|
|
||||||
if (field != other.field) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
|
||||||
var result = rows
|
|
||||||
result = 31 * result + columns
|
|
||||||
result = 31 * result + field.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
infix fun <T : Any> Matrix<T>.dot(b: Matrix<T>): Matrix<T> = this.context.multiply(this, b)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A matrix-like structure
|
|
||||||
*/
|
|
||||||
interface Matrix<T : Any> : SpaceElement<Matrix<T>, MatrixSpace<T>> {
|
|
||||||
/**
|
|
||||||
* Number of rows
|
|
||||||
*/
|
|
||||||
val rows: Int
|
|
||||||
/**
|
|
||||||
* Number of columns
|
|
||||||
*/
|
|
||||||
val columns: Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get element in row [i] and column [j]. Throws error in case of call ounside structure dimensions
|
|
||||||
*/
|
|
||||||
operator fun get(i: Int, j: Int): T
|
|
||||||
|
|
||||||
override val self: Matrix<T>
|
|
||||||
get() = this
|
|
||||||
|
|
||||||
fun transpose(): Matrix<T> {
|
|
||||||
return object : Matrix<T> {
|
|
||||||
override val context: MatrixSpace<T> = this@Matrix.context
|
|
||||||
override val rows: Int = this@Matrix.columns
|
|
||||||
override val columns: Int = this@Matrix.rows
|
|
||||||
override fun get(i: Int, j: Int): T = this@Matrix[j, i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create [ArrayMatrix] with custom field
|
|
||||||
*/
|
|
||||||
fun <T : Any> of(rows: Int, columns: Int, field: Field<T>, initializer: (Int, Int) -> T) =
|
|
||||||
ArrayMatrix(ArrayMatrixSpace(rows, columns, field), initializer)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create [ArrayMatrix] of doubles. The implementation in general should be faster than generic one due to boxing.
|
|
||||||
*/
|
|
||||||
fun ofReal(rows: Int, columns: Int, initializer: (Int, Int) -> Double) =
|
|
||||||
ArrayMatrix(ArrayMatrixSpace(rows, columns, DoubleField, realNDFieldFactory), initializer)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a diagonal value matrix. By default value equals [Field.one].
|
|
||||||
*/
|
|
||||||
fun <T : Any> diagonal(rows: Int, columns: Int, field: Field<T>, values: (Int) -> T = { field.one }): Matrix<T> {
|
|
||||||
return of(rows, columns, field) { i, j -> if (i == j) values(i) else field.zero }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Equality check on two generic matrices
|
|
||||||
*/
|
|
||||||
fun equals(mat1: Matrix<*>, mat2: Matrix<*>): Boolean {
|
|
||||||
if (mat1 === mat2) return true
|
|
||||||
if (mat1.context != mat2.context) return false
|
|
||||||
for (i in 0 until mat1.rows) {
|
|
||||||
for (j in 0 until mat2.columns) {
|
|
||||||
if (mat1[i, j] != mat2[i, j]) return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typealias NDFieldFactory<T> = (IntArray) -> NDField<T>
|
|
||||||
|
|
||||||
internal fun <T : Any> genericNDFieldFactory(field: Field<T>): NDFieldFactory<T> = { index -> GenericNDField(index, field) }
|
|
||||||
internal val realNDFieldFactory: NDFieldFactory<Double> = { index -> ExtendedNDField(index, DoubleField) }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NDArray-based implementation of vector space. By default uses slow [GenericNDField], but could be overridden with custom [NDField] factory.
|
|
||||||
*/
|
|
||||||
class ArrayMatrixSpace<T : Any>(
|
|
||||||
rows: Int,
|
|
||||||
columns: Int,
|
|
||||||
field: Field<T>,
|
|
||||||
val ndFactory: NDFieldFactory<T> = genericNDFieldFactory(field)
|
|
||||||
) : MatrixSpace<T>(rows, columns, field) {
|
|
||||||
|
|
||||||
val ndField by lazy {
|
|
||||||
ndFactory(intArrayOf(rows, columns))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun produce(initializer: (Int, Int) -> T): Matrix<T> = ArrayMatrix(this, initializer)
|
|
||||||
|
|
||||||
override fun produceSpace(rows: Int, columns: Int): ArrayMatrixSpace<T> {
|
|
||||||
return ArrayMatrixSpace(rows, columns, field, ndFactory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors
|
* A group of methods to resolve equation A dot X = B, where A and B are matrices or vectors
|
||||||
*/
|
*/
|
||||||
interface LinearSolver<T : Any> {
|
interface LinearSolver<T : Any, F : Field<T>> {
|
||||||
fun solve(a: Matrix<T>, b: Matrix<T>): Matrix<T>
|
fun solve(a: Matrix<T, F>, b: Matrix<T, F>): Matrix<T, F>
|
||||||
fun solve(a: Matrix<T>, b: Vector<T>): Vector<T> = solve(a, b.toMatrix()).toVector()
|
fun solve(a: Matrix<T, F>, b: Vector<T, F>): Vector<T, F> = solve(a, b.toMatrix()).toVector()
|
||||||
fun inverse(a: Matrix<T>): Matrix<T> = solve(a, Matrix.diagonal(a.rows, a.columns, a.context.field))
|
fun inverse(a: Matrix<T, F>): Matrix<T, F> = solve(a, Matrix.diagonal(a.rows, a.columns, a.context.field))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,7 +25,7 @@ fun List<Double>.toVector() = Vector.ofReal(this.size) { this[it] }
|
|||||||
/**
|
/**
|
||||||
* Convert matrix to vector if it is possible
|
* Convert matrix to vector if it is possible
|
||||||
*/
|
*/
|
||||||
fun <T : Any> Matrix<T>.toVector(): Vector<T> {
|
fun <T : Any, F : Field<T>> Matrix<T, F>.toVector(): Vector<T, F> {
|
||||||
return when {
|
return when {
|
||||||
this.columns == 1 -> {
|
this.columns == 1 -> {
|
||||||
// if (this is ArrayMatrix) {
|
// if (this is ArrayMatrix) {
|
||||||
@ -208,7 +41,7 @@ fun <T : Any> Matrix<T>.toVector(): Vector<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : Any> Vector<T>.toMatrix(): Matrix<T> {
|
fun <T : Any, F : Field<T>> Vector<T, F>.toMatrix(): Matrix<T, F> {
|
||||||
// return if (this is ArrayVector) {
|
// return if (this is ArrayVector) {
|
||||||
// //Reuse existing underlying array
|
// //Reuse existing underlying array
|
||||||
// ArrayMatrix(ArrayMatrixSpace(size, 1, context.field, context.ndFactory), array)
|
// ArrayMatrix(ArrayMatrixSpace(size, 1, context.field, context.ndFactory), array)
|
||||||
@ -216,11 +49,14 @@ fun <T : Any> Vector<T>.toMatrix(): Matrix<T> {
|
|||||||
// //Generic vector
|
// //Generic vector
|
||||||
// matrix(size, 1, context.field) { i, j -> get(i) }
|
// matrix(size, 1, context.field) { i, j -> get(i) }
|
||||||
// }
|
// }
|
||||||
return Matrix.of(size, 1, context.field) { i, _ -> get(i) }
|
return Matrix.of(size, 1, context.space) { i, _ -> get(i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
object VectorL2Norm: Norm<Vector<out Number>, Double> {
|
object VectorL2Norm : Norm<Vector<out Number, *>, Double> {
|
||||||
override fun norm(arg: Vector<out Number>): Double {
|
override fun norm(arg: Vector<out Number, *>): Double {
|
||||||
return kotlin.math.sqrt(arg.sumByDouble { it.toDouble() })
|
return kotlin.math.sqrt(arg.sumByDouble { it.toDouble() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias RealVector = Vector<Double, DoubleField>
|
||||||
|
typealias RealMatrix = Matrix<Double, DoubleField>
|
||||||
|
@ -0,0 +1,171 @@
|
|||||||
|
package scientifik.kmath.linear
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.*
|
||||||
|
import scientifik.kmath.structures.ExtendedNDField
|
||||||
|
import scientifik.kmath.structures.GenericNDField
|
||||||
|
import scientifik.kmath.structures.NDField
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The space for linear elements. Supports scalar product alongside with standard linear operations.
|
||||||
|
* @param T type of individual element of the vector or matrix
|
||||||
|
* @param V the type of vector space element
|
||||||
|
*/
|
||||||
|
abstract class MatrixSpace<T : Any, F : Ring<T>>(val rows: Int, val columns: Int, val field: F) : Space<Matrix<T, F>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce the element of this space
|
||||||
|
*/
|
||||||
|
abstract fun produce(initializer: (Int, Int) -> T): Matrix<T, F>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produce new matrix space with given dimensions. The space produced could be raised from cache since [MatrixSpace] does not have mutable elements
|
||||||
|
*/
|
||||||
|
abstract fun produceSpace(rows: Int, columns: Int): MatrixSpace<T, F>
|
||||||
|
|
||||||
|
override val zero: Matrix<T, F> by lazy {
|
||||||
|
produce { _, _ -> field.zero }
|
||||||
|
}
|
||||||
|
|
||||||
|
// val one: Matrix<T> by lazy {
|
||||||
|
// produce { i, j -> if (i == j) field.one else field.zero }
|
||||||
|
// }
|
||||||
|
|
||||||
|
override fun add(a: Matrix<T, F>, b: Matrix<T, F>): Matrix<T, F> {
|
||||||
|
return produce { i, j -> with(field) { a[i, j] + b[i, j] } }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun multiply(a: Matrix<T, F>, k: Double): Matrix<T, F> {
|
||||||
|
//TODO it is possible to implement scalable linear elements which normed values and adjustable scale to save memory and processing poser
|
||||||
|
return produce { i, j -> with(field) { a[i, j] * k } }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dot product. Throws exception on dimension mismatch
|
||||||
|
*/
|
||||||
|
fun multiply(a: Matrix<T, F>, b: Matrix<T, F>): Matrix<T, F> {
|
||||||
|
if (a.rows != b.columns) {
|
||||||
|
//TODO replace by specific exception
|
||||||
|
error("Dimension mismatch in linear structure dot product: [${a.rows},${a.columns}]*[${b.rows},${b.columns}]")
|
||||||
|
}
|
||||||
|
return produceSpace(a.rows, b.columns).produce { i, j ->
|
||||||
|
(0 until a.columns).asSequence().map { k -> field.multiply(a[i, k], b[k, j]) }.reduce { first, second -> field.add(first, second) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (other !is MatrixSpace<*,*>) return false
|
||||||
|
|
||||||
|
if (rows != other.rows) return false
|
||||||
|
if (columns != other.columns) return false
|
||||||
|
if (field != other.field) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = rows
|
||||||
|
result = 31 * result + columns
|
||||||
|
result = 31 * result + field.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infix fun <T : Any, F : Field<T>> Matrix<T, F>.dot(b: Matrix<T, F>): Matrix<T, F> = this.context.multiply(this, b)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A matrix-like structure
|
||||||
|
*/
|
||||||
|
interface Matrix<T : Any, F: Ring<T>> : SpaceElement<Matrix<T, F>, MatrixSpace<T, F>> {
|
||||||
|
/**
|
||||||
|
* Number of rows
|
||||||
|
*/
|
||||||
|
val rows: Int
|
||||||
|
/**
|
||||||
|
* Number of columns
|
||||||
|
*/
|
||||||
|
val columns: Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get element in row [i] and column [j]. Throws error in case of call ounside structure dimensions
|
||||||
|
*/
|
||||||
|
operator fun get(i: Int, j: Int): T
|
||||||
|
|
||||||
|
override val self: Matrix<T, F>
|
||||||
|
get() = this
|
||||||
|
|
||||||
|
fun transpose(): Matrix<T, F> {
|
||||||
|
return object : Matrix<T, F> {
|
||||||
|
override val context: MatrixSpace<T, F> = this@Matrix.context
|
||||||
|
override val rows: Int = this@Matrix.columns
|
||||||
|
override val columns: Int = this@Matrix.rows
|
||||||
|
override fun get(i: Int, j: Int): T = this@Matrix[j, i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create [ArrayMatrix] with custom field
|
||||||
|
*/
|
||||||
|
fun <T : Any, F: Field<T>> of(rows: Int, columns: Int, field: F, initializer: (Int, Int) -> T) =
|
||||||
|
ArrayMatrix(ArrayMatrixSpace(rows, columns, field), initializer)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create [ArrayMatrix] of doubles. The implementation in general should be faster than generic one due to boxing.
|
||||||
|
*/
|
||||||
|
fun ofReal(rows: Int, columns: Int, initializer: (Int, Int) -> Double) =
|
||||||
|
ArrayMatrix(ArrayMatrixSpace(rows, columns, DoubleField, realNDFieldFactory), initializer)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a diagonal value matrix. By default value equals [Field.one].
|
||||||
|
*/
|
||||||
|
fun <T : Any, F: Field<T>> diagonal(rows: Int, columns: Int, field: F, values: (Int) -> T = { field.one }): Matrix<T, F> {
|
||||||
|
return of(rows, columns, field) { i, j -> if (i == j) values(i) else field.zero }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equality check on two generic matrices
|
||||||
|
*/
|
||||||
|
fun equals(mat1: Matrix<*, *>, mat2: Matrix<*, *>): Boolean {
|
||||||
|
if (mat1 === mat2) return true
|
||||||
|
if (mat1.context != mat2.context) return false
|
||||||
|
for (i in 0 until mat1.rows) {
|
||||||
|
for (j in 0 until mat2.columns) {
|
||||||
|
if (mat1[i, j] != mat2[i, j]) return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typealias NDFieldFactory<T, F> = (IntArray) -> NDField<T, F>
|
||||||
|
|
||||||
|
internal fun <T : Any, F : Field<T>> genericNDFieldFactory(field: F): NDFieldFactory<T, F> = { index -> GenericNDField(index, field) }
|
||||||
|
internal val realNDFieldFactory: NDFieldFactory<Double, DoubleField> = { index -> ExtendedNDField(index, DoubleField) }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NDArray-based implementation of vector space. By default uses slow [GenericNDField], but could be overridden with custom [NDField] factory.
|
||||||
|
*/
|
||||||
|
class ArrayMatrixSpace<T : Any, F : Field<T>>(
|
||||||
|
rows: Int,
|
||||||
|
columns: Int,
|
||||||
|
field: F,
|
||||||
|
val ndFactory: NDFieldFactory<T, F> = genericNDFieldFactory(field)
|
||||||
|
) : MatrixSpace<T, F>(rows, columns, field) {
|
||||||
|
|
||||||
|
val ndField by lazy {
|
||||||
|
ndFactory(intArrayOf(rows, columns))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun produce(initializer: (Int, Int) -> T): Matrix<T, F> = ArrayMatrix(this, initializer)
|
||||||
|
|
||||||
|
override fun produceSpace(rows: Int, columns: Int): ArrayMatrixSpace<T, F> {
|
||||||
|
return ArrayMatrixSpace(rows, columns, field, ndFactory)
|
||||||
|
}
|
||||||
|
}
|
@ -12,34 +12,34 @@ import scientifik.kmath.structures.get
|
|||||||
* A linear space for vectors.
|
* A linear space for vectors.
|
||||||
* Could be used on any point-like structure
|
* Could be used on any point-like structure
|
||||||
*/
|
*/
|
||||||
abstract class VectorSpace<T : Any>(val size: Int, val field: Field<T>) : Space<Point<T>> {
|
abstract class VectorSpace<T : Any, S : Space<T>>(val size: Int, val space: S) : Space<Point<T>> {
|
||||||
|
|
||||||
abstract fun produce(initializer: (Int) -> T): Vector<T>
|
abstract fun produce(initializer: (Int) -> T): Vector<T, S>
|
||||||
|
|
||||||
override val zero: Vector<T> by lazy { produce { field.zero } }
|
override val zero: Vector<T, S> by lazy { produce { space.zero } }
|
||||||
|
|
||||||
override fun add(a: Point<T>, b: Point<T>): Vector<T> = produce { with(field) { a[it] + b[it] } }
|
override fun add(a: Point<T>, b: Point<T>): Vector<T, S> = produce { with(space) { a[it] + b[it] } }
|
||||||
|
|
||||||
override fun multiply(a: Point<T>, k: Double): Vector<T> = produce { with(field) { a[it] * k } }
|
override fun multiply(a: Point<T>, k: Double): Vector<T, S> = produce { with(space) { a[it] * k } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A point coupled to the linear space
|
* A point coupled to the linear space
|
||||||
*/
|
*/
|
||||||
interface Vector<T : Any> : SpaceElement<Point<T>, VectorSpace<T>>, Point<T>, Iterable<T> {
|
interface Vector<T : Any, S : Space<T>> : SpaceElement<Point<T>, VectorSpace<T, S>>, Point<T>, Iterable<T> {
|
||||||
override val size: Int get() = context.size
|
override val size: Int get() = context.size
|
||||||
|
|
||||||
override operator fun plus(b: Point<T>): Vector<T> = context.add(self, b)
|
override operator fun plus(b: Point<T>): Vector<T, S> = context.add(self, b)
|
||||||
override operator fun minus(b: Point<T>): Vector<T> = context.add(self, context.multiply(b, -1.0))
|
override operator fun minus(b: Point<T>): Vector<T, S> = context.add(self, context.multiply(b, -1.0))
|
||||||
override operator fun times(k: Number): Vector<T> = context.multiply(self, k.toDouble())
|
override operator fun times(k: Number): Vector<T, S> = context.multiply(self, k.toDouble())
|
||||||
override operator fun div(k: Number): Vector<T> = context.multiply(self, 1.0 / k.toDouble())
|
override operator fun div(k: Number): Vector<T, S> = context.multiply(self, 1.0 / k.toDouble())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Create vector with custom field
|
* Create vector with custom field
|
||||||
*/
|
*/
|
||||||
fun <T : Any> of(size: Int, field: Field<T>, initializer: (Int) -> T) =
|
fun <T : Any, F: Field<T>> of(size: Int, field: F, initializer: (Int) -> T) =
|
||||||
ArrayVector(ArrayVectorSpace(size, field), initializer)
|
ArrayVector(ArrayVectorSpace(size, field), initializer)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,7 +50,7 @@ interface Vector<T : Any> : SpaceElement<Point<T>, VectorSpace<T>>, Point<T>, It
|
|||||||
|
|
||||||
fun ofReal(vararg point: Double) = point.toVector()
|
fun ofReal(vararg point: Double) = point.toVector()
|
||||||
|
|
||||||
fun equals(v1: Vector<*>, v2: Vector<*>): Boolean {
|
fun equals(v1: Vector<*,*>, v2: Vector<*,*>): Boolean {
|
||||||
if (v1 === v2) return true
|
if (v1 === v2) return true
|
||||||
if (v1.context != v2.context) return false
|
if (v1.context != v2.context) return false
|
||||||
for (i in 0 until v2.size) {
|
for (i in 0 until v2.size) {
|
||||||
@ -61,24 +61,24 @@ interface Vector<T : Any> : SpaceElement<Point<T>, VectorSpace<T>>, Point<T>, It
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayVectorSpace<T : Any>(
|
class ArrayVectorSpace<T : Any, F : Field<T>>(
|
||||||
size: Int,
|
size: Int,
|
||||||
field: Field<T>,
|
field: F,
|
||||||
val ndFactory: NDFieldFactory<T> = genericNDFieldFactory(field)
|
val ndFactory: NDFieldFactory<T, F> = genericNDFieldFactory(field)
|
||||||
) : VectorSpace<T>(size, field) {
|
) : VectorSpace<T, F>(size, field) {
|
||||||
val ndField by lazy {
|
val ndField by lazy {
|
||||||
ndFactory(intArrayOf(size))
|
ndFactory(intArrayOf(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun produce(initializer: (Int) -> T): Vector<T> = ArrayVector(this, initializer)
|
override fun produce(initializer: (Int) -> T): Vector<T, F> = ArrayVector(this, initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Member of [ArrayMatrixSpace] which wraps 2-D array
|
* Member of [ArrayMatrixSpace] which wraps 2-D array
|
||||||
*/
|
*/
|
||||||
class ArrayMatrix<T : Any> internal constructor(override val context: ArrayMatrixSpace<T>, val element: NDElement<T>) : Matrix<T> {
|
class ArrayMatrix<T : Any, F : Field<T>> internal constructor(override val context: ArrayMatrixSpace<T, F>, val element: NDElement<T, F>) : Matrix<T, F> {
|
||||||
|
|
||||||
constructor(context: ArrayMatrixSpace<T>, initializer: (Int, Int) -> T) : this(context, context.ndField.produce { list -> initializer(list[0], list[1]) })
|
constructor(context: ArrayMatrixSpace<T, F>, initializer: (Int, Int) -> T) : this(context, context.ndField.produce { list -> initializer(list[0], list[1]) })
|
||||||
|
|
||||||
override val rows: Int get() = context.rows
|
override val rows: Int get() = context.rows
|
||||||
|
|
||||||
@ -88,13 +88,13 @@ class ArrayMatrix<T : Any> internal constructor(override val context: ArrayMatri
|
|||||||
return element[i, j]
|
return element[i, j]
|
||||||
}
|
}
|
||||||
|
|
||||||
override val self: ArrayMatrix<T> get() = this
|
override val self: ArrayMatrix<T, F> get() = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ArrayVector<T : Any> internal constructor(override val context: VectorSpace<T>, val element: NDElement<T>) : Vector<T> {
|
class ArrayVector<T : Any, F : Field<T>> internal constructor(override val context: VectorSpace<T, F>, val element: NDElement<T, F>) : Vector<T, F> {
|
||||||
|
|
||||||
constructor(context: ArrayVectorSpace<T>, initializer: (Int) -> T) : this(context, context.ndField.produce { list -> initializer(list[0]) })
|
constructor(context: ArrayVectorSpace<T, F>, initializer: (Int) -> T) : this(context, context.ndField.produce { list -> initializer(list[0]) })
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (context.size != element.shape[0]) {
|
if (context.size != element.shape[0]) {
|
||||||
@ -106,13 +106,12 @@ class ArrayVector<T : Any> internal constructor(override val context: VectorSpac
|
|||||||
return element[index]
|
return element[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
override val self: ArrayVector<T> get() = this
|
override val self: ArrayVector<T, F> get() = this
|
||||||
|
|
||||||
override fun iterator(): Iterator<T> = (0 until size).map { element[it] }.iterator()
|
override fun iterator(): Iterator<T> = (0 until size).map { element[it] }.iterator()
|
||||||
|
|
||||||
override fun copy(): ArrayVector<T> = ArrayVector(context, element)
|
override fun copy(): ArrayVector<T, F> = ArrayVector(context, element)
|
||||||
|
|
||||||
override fun toString(): String = this.joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
|
override fun toString(): String = this.joinToString(prefix = "[", postfix = "]", separator = ", ") { it.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
typealias RealVector = Vector<Double>
|
|
@ -9,32 +9,32 @@ import scientifik.kmath.operations.TrigonometricOperations
|
|||||||
/**
|
/**
|
||||||
* NDField that supports [ExtendedField] operations on its elements
|
* NDField that supports [ExtendedField] operations on its elements
|
||||||
*/
|
*/
|
||||||
class ExtendedNDField<N: Any>(shape: IntArray, override val field: ExtendedField<N>) : NDField<N>(shape, field),
|
class ExtendedNDField<N : Any, F : ExtendedField<N>>(shape: IntArray, field: F) : NDField<N, F>(shape, field),
|
||||||
TrigonometricOperations<NDElement<N>>,
|
TrigonometricOperations<NDElement<N, F>>,
|
||||||
PowerOperations<NDElement<N>>,
|
PowerOperations<NDElement<N, F>>,
|
||||||
ExponentialOperations<NDElement<N>> {
|
ExponentialOperations<NDElement<N, F>> {
|
||||||
|
|
||||||
override fun produceStructure(initializer: (IntArray) -> N): NDStructure<N> {
|
override fun produceStructure(initializer: F.(IntArray) -> N): NDStructure<N> {
|
||||||
return genericNdStructure(shape, initializer)
|
return genericNdStructure(shape) { field.initializer(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun power(arg: NDElement<N>, pow: Double): NDElement<N> {
|
override fun power(arg: NDElement<N, F>, pow: Double): NDElement<N, F> {
|
||||||
return arg.transform { d -> with(field) { power(d, pow) } }
|
return arg.transform { d -> with(field) { power(d, pow) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun exp(arg: NDElement<N>): NDElement<N> {
|
override fun exp(arg: NDElement<N, F>): NDElement<N, F> {
|
||||||
return arg.transform { d -> with(field) { exp(d) } }
|
return arg.transform { d -> with(field) { exp(d) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun ln(arg: NDElement<N>): NDElement<N> {
|
override fun ln(arg: NDElement<N, F>): NDElement<N, F> {
|
||||||
return arg.transform { d -> with(field) { ln(d) } }
|
return arg.transform { d -> with(field) { ln(d) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sin(arg: NDElement<N>): NDElement<N> {
|
override fun sin(arg: NDElement<N, F>): NDElement<N, F> {
|
||||||
return arg.transform { d -> with(field) { sin(d) } }
|
return arg.transform { d -> with(field) { sin(d) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cos(arg: NDElement<N>): NDElement<N> {
|
override fun cos(arg: NDElement<N, F>): NDElement<N, F> {
|
||||||
return arg.transform { d -> with(field) { cos(d) } }
|
return arg.transform { d -> with(field) { cos(d) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,24 +15,24 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run
|
|||||||
* @param field - operations field defined on individual array element
|
* @param field - operations field defined on individual array element
|
||||||
* @param T the type of the element contained in NDArray
|
* @param T the type of the element contained in NDArray
|
||||||
*/
|
*/
|
||||||
abstract class NDField<T>(val shape: IntArray, open val field: Field<T>) : Field<NDElement<T>> {
|
abstract class NDField<T, F : Field<T>>(val shape: IntArray, val field: F) : Field<NDElement<T, F>> {
|
||||||
|
|
||||||
abstract fun produceStructure(initializer: (IntArray) -> T): NDStructure<T>
|
abstract fun produceStructure(initializer: F.(IntArray) -> T): NDStructure<T>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new instance of NDArray using field shape and given initializer
|
* Create new instance of NDArray using field shape and given initializer
|
||||||
* The producer takes list of indices as argument and returns contained value
|
* The producer takes list of indices as argument and returns contained value
|
||||||
*/
|
*/
|
||||||
fun produce(initializer: (IntArray) -> T): NDElement<T> = NDElement(this, produceStructure(initializer))
|
fun produce(initializer: F.(IntArray) -> T): NDElement<T, F> = NDElement(this, produceStructure(initializer))
|
||||||
|
|
||||||
override val zero: NDElement<T> by lazy {
|
override val zero: NDElement<T, F> by lazy {
|
||||||
produce { this.field.zero }
|
produce { zero }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the shape of given NDArray and throw exception if it does not coincide with shape of the field
|
* Check the shape of given NDArray and throw exception if it does not coincide with shape of the field
|
||||||
*/
|
*/
|
||||||
private fun checkShape(vararg elements: NDElement<T>) {
|
private fun checkShape(vararg elements: NDElement<T, F>) {
|
||||||
elements.forEach {
|
elements.forEach {
|
||||||
if (!shape.contentEquals(it.shape)) {
|
if (!shape.contentEquals(it.shape)) {
|
||||||
throw ShapeMismatchException(shape, it.shape)
|
throw ShapeMismatchException(shape, it.shape)
|
||||||
@ -43,7 +43,7 @@ abstract class NDField<T>(val shape: IntArray, open val field: Field<T>) : Field
|
|||||||
/**
|
/**
|
||||||
* Element-by-element addition
|
* Element-by-element addition
|
||||||
*/
|
*/
|
||||||
override fun add(a: NDElement<T>, b: NDElement<T>): NDElement<T> {
|
override fun add(a: NDElement<T, F>, b: NDElement<T, F>): NDElement<T, F> {
|
||||||
checkShape(a, b)
|
checkShape(a, b)
|
||||||
return produce { with(field) { a[it] + b[it] } }
|
return produce { with(field) { a[it] + b[it] } }
|
||||||
}
|
}
|
||||||
@ -51,18 +51,18 @@ abstract class NDField<T>(val shape: IntArray, open val field: Field<T>) : Field
|
|||||||
/**
|
/**
|
||||||
* Multiply all elements by cinstant
|
* Multiply all elements by cinstant
|
||||||
*/
|
*/
|
||||||
override fun multiply(a: NDElement<T>, k: Double): NDElement<T> {
|
override fun multiply(a: NDElement<T, F>, k: Double): NDElement<T, F> {
|
||||||
checkShape(a)
|
checkShape(a)
|
||||||
return produce { with(field) { a[it] * k } }
|
return produce { with(field) { a[it] * k } }
|
||||||
}
|
}
|
||||||
|
|
||||||
override val one: NDElement<T>
|
override val one: NDElement<T, F>
|
||||||
get() = produce { this.field.one }
|
get() = produce { one }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Element-by-element multiplication
|
* Element-by-element multiplication
|
||||||
*/
|
*/
|
||||||
override fun multiply(a: NDElement<T>, b: NDElement<T>): NDElement<T> {
|
override fun multiply(a: NDElement<T, F>, b: NDElement<T, F>): NDElement<T, F> {
|
||||||
checkShape(a)
|
checkShape(a)
|
||||||
return produce { with(field) { a[it] * b[it] } }
|
return produce { with(field) { a[it] * b[it] } }
|
||||||
}
|
}
|
||||||
@ -70,65 +70,65 @@ abstract class NDField<T>(val shape: IntArray, open val field: Field<T>) : Field
|
|||||||
/**
|
/**
|
||||||
* Element-by-element division
|
* Element-by-element division
|
||||||
*/
|
*/
|
||||||
override fun divide(a: NDElement<T>, b: NDElement<T>): NDElement<T> {
|
override fun divide(a: NDElement<T, F>, b: NDElement<T, F>): NDElement<T, F> {
|
||||||
checkShape(a)
|
checkShape(a)
|
||||||
return produce { with(field) { a[it] / b[it] } }
|
return produce { with(field) { a[it] / b[it] } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Reverse sum operation
|
// * Reverse sum operation
|
||||||
*/
|
// */
|
||||||
operator fun <T> T.plus(arg: NDElement<T>): NDElement<T> = arg + this
|
// operator fun T.plus(arg: NDElement<T, F>): NDElement<T, F> = arg + this
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Reverse minus operation
|
// * Reverse minus operation
|
||||||
*/
|
// */
|
||||||
operator fun <T> T.minus(arg: NDElement<T>): NDElement<T> = arg.transform { _, value ->
|
// operator fun T.minus(arg: NDElement<T, F>): NDElement<T, F> = arg.transform { _, value ->
|
||||||
with(arg.context.field) {
|
// with(arg.context.field) {
|
||||||
this@minus - value
|
// this@minus - value
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Reverse product operation
|
// * Reverse product operation
|
||||||
*/
|
// */
|
||||||
operator fun <T> T.times(arg: NDElement<T>): NDElement<T> = arg * this
|
// operator fun T.times(arg: NDElement<T, F>): NDElement<T, F> = arg * this
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Reverse division operation
|
// * Reverse division operation
|
||||||
*/
|
// */
|
||||||
operator fun <T> T.div(arg: NDElement<T>): NDElement<T> = arg.transform { _, value ->
|
// operator fun T.div(arg: NDElement<T, F>): NDElement<T, F> = arg.transform { _, value ->
|
||||||
with(arg.context.field) {
|
// with(arg.context.field) {
|
||||||
this@div / value
|
// this@div / value
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutable [NDStructure] coupled to the context. Emulates Python ndarray
|
* Immutable [NDStructure] coupled to the context. Emulates Python ndarray
|
||||||
*/
|
*/
|
||||||
class NDElement<T>(override val context: NDField<T>, private val structure: NDStructure<T>) : FieldElement<NDElement<T>, NDField<T>>, NDStructure<T> by structure {
|
class NDElement<T, F : Field<T>>(override val context: NDField<T, F>, private val structure: NDStructure<T>) : FieldElement<NDElement<T, F>, NDField<T, F>>, NDStructure<T> by structure {
|
||||||
|
|
||||||
//TODO ensure structure is immutable
|
//TODO ensure structure is immutable
|
||||||
|
|
||||||
override val self: NDElement<T>
|
override val self: NDElement<T, F>
|
||||||
get() = this
|
get() = this
|
||||||
|
|
||||||
inline fun transform(crossinline action: (IntArray, T) -> T): NDElement<T> = context.produce { action(it, get(*it)) }
|
inline fun transform(crossinline action: (IntArray, T) -> T): NDElement<T, F> = context.produce { action(it, get(*it)) }
|
||||||
inline fun transform(crossinline action: (T) -> T): NDElement<T> = context.produce { action(get(*it)) }
|
inline fun transform(crossinline action: (T) -> T): NDElement<T, F> = context.produce { action(get(*it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
||||||
*/
|
*/
|
||||||
operator fun <T> Function1<T, T>.invoke(ndElement: NDElement<T>): NDElement<T> = ndElement.transform { _, value -> this(value) }
|
operator fun <T, F : Field<T>> Function1<T, T>.invoke(ndElement: NDElement<T, F>): NDElement<T, F> = ndElement.transform { _, value -> this(value) }
|
||||||
|
|
||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Summation operation for [NDElement] and single element
|
* Summation operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T> NDElement<T>.plus(arg: T): NDElement<T> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.plus(arg: T): NDElement<T, F> = transform { _, value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg + value
|
arg + value
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ operator fun <T> NDElement<T>.plus(arg: T): NDElement<T> = transform { _, value
|
|||||||
/**
|
/**
|
||||||
* Subtraction operation between [NDElement] and single element
|
* Subtraction operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T> NDElement<T>.minus(arg: T): NDElement<T> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.minus(arg: T): NDElement<T, F> = transform { _, value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg - value
|
arg - value
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ operator fun <T> NDElement<T>.minus(arg: T): NDElement<T> = transform { _, value
|
|||||||
/**
|
/**
|
||||||
* Product operation for [NDElement] and single element
|
* Product operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T> NDElement<T>.times(arg: T): NDElement<T> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.times(arg: T): NDElement<T, F> = transform { _, value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg * value
|
arg * value
|
||||||
}
|
}
|
||||||
@ -157,14 +157,14 @@ operator fun <T> NDElement<T>.times(arg: T): NDElement<T> = transform { _, value
|
|||||||
/**
|
/**
|
||||||
* Division operation between [NDElement] and single element
|
* Division operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T> NDElement<T>.div(arg: T): NDElement<T> = transform { _, value ->
|
operator fun <T, F : Field<T>> NDElement<T, F>.div(arg: T): NDElement<T, F> = transform { _, value ->
|
||||||
with(context.field) {
|
with(context.field) {
|
||||||
arg / value
|
arg / value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GenericNDField<T : Any>(shape: IntArray, field: Field<T>) : NDField<T>(shape, field) {
|
class GenericNDField<T : Any, F : Field<T>>(shape: IntArray, field: F) : NDField<T, F>(shape, field) {
|
||||||
override fun produceStructure(initializer: (IntArray) -> T): NDStructure<T> = genericNdStructure(shape, initializer)
|
override fun produceStructure(initializer: F.(IntArray) -> T): NDStructure<T> = genericNdStructure(shape) { field.initializer(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
//typealias NDFieldFactory<T> = (IntArray)->NDField<T>
|
//typealias NDFieldFactory<T> = (IntArray)->NDField<T>
|
||||||
@ -173,23 +173,24 @@ object NDArrays {
|
|||||||
/**
|
/**
|
||||||
* Create a platform-optimized NDArray of doubles
|
* Create a platform-optimized NDArray of doubles
|
||||||
*/
|
*/
|
||||||
fun realNDArray(shape: IntArray, initializer: (IntArray) -> Double = { 0.0 }): NDElement<Double> {
|
fun realNDArray(shape: IntArray, initializer: DoubleField.(IntArray) -> Double = { 0.0 }): NDElement<Double, DoubleField> {
|
||||||
return ExtendedNDField(shape, DoubleField).produce(initializer)
|
return ExtendedNDField(shape, DoubleField).produce(initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDElement<Double> {
|
fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDElement<Double, DoubleField> {
|
||||||
return realNDArray(intArrayOf(dim)) { initializer(it[0]) }
|
return realNDArray(intArrayOf(dim)) { initializer(it[0]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDElement<Double> {
|
fun real2DArray(dim1: Int, dim2: Int, initializer: (Int, Int) -> Double = { _, _ -> 0.0 }): NDElement<Double, DoubleField> {
|
||||||
return realNDArray(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) }
|
return realNDArray(intArrayOf(dim1, dim2)) { initializer(it[0], it[1]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun real3DArray(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }): NDElement<Double> {
|
fun real3DArray(dim1: Int, dim2: Int, dim3: Int, initializer: (Int, Int, Int) -> Double = { _, _, _ -> 0.0 }): NDElement<Double, DoubleField> {
|
||||||
return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
|
return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun produceReal(shape: IntArray, block: ExtendedNDField<Double>.() -> NDElement<Double>) = ExtendedNDField(shape, DoubleField).run(block)
|
inline fun produceReal(shape: IntArray, block: ExtendedNDField<Double, DoubleField>.() -> NDElement<Double, DoubleField>) =
|
||||||
|
ExtendedNDField(shape, DoubleField).run(block)
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Simple boxing NDField
|
// * Simple boxing NDField
|
||||||
@ -199,7 +200,7 @@ object NDArrays {
|
|||||||
/**
|
/**
|
||||||
* Simple boxing NDArray
|
* Simple boxing NDArray
|
||||||
*/
|
*/
|
||||||
fun <T : Any> create(field: Field<T>, shape: IntArray, initializer: (IntArray) -> T): NDElement<T> {
|
fun <T : Any, F : Field<T>> create(field: F, shape: IntArray, initializer: (IntArray) -> T): NDElement<T, F> {
|
||||||
return GenericNDField(shape, field).produce { initializer(it) }
|
return GenericNDField(shape, field).produce { initializer(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,8 @@ class NumberNDFieldTest {
|
|||||||
assertEquals(2.0, result[0, 2])
|
assertEquals(2.0, result[0, 2])
|
||||||
}
|
}
|
||||||
|
|
||||||
object L2Norm: Norm<NDElement<out Number>, Double> {
|
object L2Norm : Norm<NDElement<out Number, *>, Double> {
|
||||||
override fun norm(arg: NDElement<out Number>): Double {
|
override fun norm(arg: NDElement<out Number, *>): Double {
|
||||||
return kotlin.math.sqrt(arg.sumByDouble { it.second.toDouble() })
|
return kotlin.math.sqrt(arg.sumByDouble { it.second.toDouble() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user