This commit is contained in:
Alexander Nozik 2023-11-04 11:49:31 +03:00
parent 46eacbb750
commit 2386ecba41
45 changed files with 297 additions and 309 deletions

View File

@ -106,4 +106,11 @@ public fun <T : Any, A : Attribute<T>> Attributes(
attrValue: T, attrValue: T,
): Attributes = Attributes(mapOf(attribute to attrValue)) ): Attributes = Attributes(mapOf(attribute to attrValue))
/**
* Create Attributes with a single [Unit] valued attribute
*/
public fun <A : Attribute<Unit>> Attributes(
attribute: A
): Attributes = Attributes(mapOf(attribute to Unit))
public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(content + other.content) public operator fun Attributes.plus(other: Attributes): Attributes = Attributes(content + other.content)

View File

@ -26,3 +26,10 @@ public inline fun <reified T> safeTypeOf(): SafeType<T> = SafeType(typeOf<T>())
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@UnstableAttributesAPI @UnstableAttributesAPI
public val <T> SafeType<T>.kClass: KClass<T & Any> get() = kType.classifier as KClass<T & Any> public val <T> SafeType<T>.kClass: KClass<T & Any> get() = kType.classifier as KClass<T & Any>
/**
* An interface containing [type] for dynamic type checking.
*/
public interface WithType<out T> {
public val type: SafeType<T>
}

View File

@ -47,7 +47,7 @@ internal class MatrixInverseBenchmark {
@Benchmark @Benchmark
fun ejmlInverse(blackhole: Blackhole) { fun ejmlInverse(blackhole: Blackhole) {
EjmlLinearSpaceDDRM { EjmlLinearSpaceDDRM {
blackhole.consume(matrix.toEjml().inverse()) blackhole.consume(matrix.toEjml().inverted())
} }
} }
} }

View File

@ -10,6 +10,7 @@ import space.kscience.kmath.complex.ComplexField
import space.kscience.kmath.expressions.interpret import space.kscience.kmath.expressions.interpret
import space.kscience.kmath.operations.Algebra import space.kscience.kmath.operations.Algebra
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -39,6 +40,8 @@ internal class TestParser {
@Test @Test
fun evaluateMstBinary() { fun evaluateMstBinary() {
val magicalAlgebra = object : Algebra<String> { val magicalAlgebra = object : Algebra<String> {
override val bufferFactory: MutableBufferFactory<String> get() = MutableBufferFactory()
override fun bindSymbolOrNull(value: String): String = value override fun bindSymbolOrNull(value: String): String = value
override fun unaryOperationFunction(operation: String): (arg: String) -> String { override fun unaryOperationFunction(operation: String): (arg: String) -> String {

View File

@ -10,6 +10,7 @@ kscience {
dependencies { dependencies {
api(projects.kmathCore) api(projects.kmathCore)
api(projects.kmathMemory)
} }
testDependencies { testDependencies {

View File

@ -5,12 +5,14 @@
package space.kscience.kmath.complex package space.kscience.kmath.complex
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.memory.MemoryReader import space.kscience.kmath.memory.*
import space.kscience.kmath.memory.MemorySpec
import space.kscience.kmath.memory.MemoryWriter
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.* import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.math.* import kotlin.math.*
/** /**
@ -51,8 +53,11 @@ public object ComplexField :
Norm<Complex, Complex>, Norm<Complex, Complex>,
NumbersAddOps<Complex>, NumbersAddOps<Complex>,
ScaleOperations<Complex> { ScaleOperations<Complex> {
override val bufferFactory: MutableBufferFactory<Complex> = MutableBufferFactory { size, init -> override val bufferFactory: MutableBufferFactory<Complex> = object : MutableBufferFactory<Complex> {
MutableMemoryBuffer.create(Complex, size, init) override fun invoke(size: Int, builder: (Int) -> Complex): MutableBuffer<Complex> =
MutableMemoryBuffer.create(Complex, size, builder)
override val type: SafeType<Complex> = safeTypeOf()
} }
override val zero: Complex = 0.0.toComplex() override val zero: Complex = 0.0.toComplex()
@ -202,8 +207,10 @@ public data class Complex(val re: Double, val im: Double) {
override fun toString(): String = "($re + i * $im)" override fun toString(): String = "($re + i * $im)"
public companion object : MemorySpec<Complex> { public companion object : MemorySpec<Complex> {
override val objectSize: Int
get() = 16 override val type: SafeType<Complex> get() = safeTypeOf()
override val objectSize: Int get() = 16
override fun MemoryReader.read(offset: Int): Complex = override fun MemoryReader.read(offset: Int): Complex =
Complex(readDouble(offset), readDouble(offset + 8)) Complex(readDouble(offset), readDouble(offset + 8))

View File

@ -5,15 +5,14 @@
package space.kscience.kmath.complex package space.kscience.kmath.complex
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.memory.MemoryReader import space.kscience.kmath.memory.*
import space.kscience.kmath.memory.MemorySpec
import space.kscience.kmath.memory.MemoryWriter
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MemoryBuffer
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableMemoryBuffer import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.math.* import kotlin.math.*
/** /**
@ -37,6 +36,8 @@ public class Quaternion(
require(!z.isNaN()) { "z-component of quaternion is not-a-number" } require(!z.isNaN()) { "z-component of quaternion is not-a-number" }
} }
override val type: SafeType<Double> get() = safeTypeOf()
/** /**
* Returns a string representation of this quaternion. * Returns a string representation of this quaternion.
*/ */
@ -78,6 +79,7 @@ public class Quaternion(
public companion object : MemorySpec<Quaternion> { public companion object : MemorySpec<Quaternion> {
override val objectSize: Int get() = 32 override val objectSize: Int get() = 32
override val type: SafeType<Quaternion> get() = safeTypeOf()
override fun MemoryReader.read(offset: Int): Quaternion = Quaternion( override fun MemoryReader.read(offset: Int): Quaternion = Quaternion(
readDouble(offset), readDouble(offset),
@ -122,7 +124,7 @@ public val Quaternion.reciprocal: Quaternion
/** /**
* Produce a normalized version of this quaternion * Produce a normalized version of this quaternion
*/ */
public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra){ this@normalized / norm(this@normalized) } public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra) { this@normalized / norm(this@normalized) }
/** /**
* A field of [Quaternion]. * A field of [Quaternion].
@ -131,6 +133,8 @@ public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra){ this@n
public object QuaternionAlgebra : Group<Quaternion>, Norm<Quaternion, Double>, PowerOperations<Quaternion>, public object QuaternionAlgebra : Group<Quaternion>, Norm<Quaternion, Double>, PowerOperations<Quaternion>,
ExponentialOperations<Quaternion>, NumbersAddOps<Quaternion>, ScaleOperations<Quaternion> { ExponentialOperations<Quaternion>, NumbersAddOps<Quaternion>, ScaleOperations<Quaternion> {
override val bufferFactory: MutableBufferFactory<Quaternion> = MutableBufferFactory()
override val zero: Quaternion = Quaternion(0.0) override val zero: Quaternion = Quaternion(0.0)
public val one: Quaternion = Quaternion(1.0) public val one: Quaternion = Quaternion(1.0)

View File

@ -9,7 +9,6 @@ kscience{
wasm() wasm()
dependencies { dependencies {
api(projects.kmathMemory)
api(projects.attributesKt) api(projects.attributesKt)
} }

View File

@ -188,7 +188,7 @@ public abstract class DSAlgebra<T, A : Ring<T>>(
vararg derivatives: T, vararg derivatives: T,
): DS<T, A> { ): DS<T, A> {
require(derivatives.size == compiler.size) { "dimension mismatch: ${derivatives.size} and ${compiler.size}" } require(derivatives.size == compiler.size) { "dimension mismatch: ${derivatives.size} and ${compiler.size}" }
val data = derivatives.asBuffer() val data = derivatives.asList().asBuffer(algebra.type)
return DS(data) return DS(data)
} }

View File

@ -6,6 +6,7 @@
package space.kscience.kmath.expressions package space.kscience.kmath.expressions
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.linear.Point import space.kscience.kmath.linear.Point
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*

View File

@ -5,15 +5,12 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.attributes.SafeType
import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.* import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices import space.kscience.kmath.structures.indices
import kotlin.reflect.KType
import kotlin.reflect.typeOf
public class BufferedLinearSpace<T, out A : Ring<T>>( public class BufferedLinearSpace<T, out A : Ring<T>>(

View File

@ -6,11 +6,11 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.* import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.BufferRingOps import space.kscience.kmath.operations.BufferRingOps
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.WithType
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
@ -173,15 +173,15 @@ public interface LinearSpace<T, out A : Ring<T>> : MatrixOperations<T> {
public operator fun T.times(v: Point<T>): Point<T> = v * this public operator fun T.times(v: Point<T>): Point<T> = v * this
/** /**
* Get an attribute value for the structure in this scope. Structure features take precedence other context features. * Get an attribute value for the structure in this scope. Structure attributes are preferred to computed attributes.
* *
* @param structure the structure. * @param structure the structure.
* @param attribute to be computed. * @param attribute to be computed.
* @return a feature object or `null` if it isn't present. * @return a feature object or `null` if it isn't present.
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public fun <T, A : StructureAttribute<T>> attributeFor(structure: StructureND<*>, attribute: A): T? = public fun <T, A : StructureAttribute<T>> attributeFor(structure: StructureND<*>, attribute: A): T =
structure.attributes[attribute] structure.attributes[attribute] ?: error("Can't compute attribute $attribute for $structure")
public companion object { public companion object {

View File

@ -7,6 +7,7 @@
package space.kscience.kmath.linear package space.kscience.kmath.linear
import space.kscience.attributes.Attributes
import space.kscience.attributes.PolymorphicAttribute import space.kscience.attributes.PolymorphicAttribute
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf import space.kscience.attributes.safeTypeOf
@ -19,32 +20,42 @@ import space.kscience.kmath.structures.*
* *a* is the owning matrix. * *a* is the owning matrix.
* *
* @param T the type of matrices' items. * @param T the type of matrices' items.
* @param l The lower triangular matrix in this decomposition. It may have [LowerTriangular]. * @param lu combined L and U matrix
* @param u The upper triangular matrix in this decomposition. It may have [UpperTriangular].
*/ */
public class LupDecomposition<T>( public class LupDecomposition<T>(
public val linearSpace: LinearSpace<T, Ring<T>>, public val linearSpace: LinearSpace<T, Ring<T>>,
public val l: Matrix<T>, private val lu: Matrix<T>,
public val u: Matrix<T>,
public val pivot: IntBuffer, public val pivot: IntBuffer,
private val even: Boolean,
) { ) {
public val elementAlgebra: Ring<T> get() = linearSpace.elementAlgebra public val elementAlgebra: Ring<T> get() = linearSpace.elementAlgebra
public val pivotMatrix: VirtualMatrix<T> public val l: Matrix<T>
get() = VirtualMatrix(lu.type, lu.rowNum, lu.colNum, attributes = Attributes(LowerTriangular)) { i, j ->
when {
j < i -> lu[i, j]
j == i -> elementAlgebra.one
else -> elementAlgebra.zero
}
}
public val u: Matrix<T>
get() = VirtualMatrix(lu.type, lu.rowNum, lu.colNum, attributes = Attributes(UpperTriangular)) { i, j ->
if (j >= i) lu[i, j] else elementAlgebra.zero
}
public val pivotMatrix: Matrix<T>
get() = VirtualMatrix(linearSpace.type, l.rowNum, l.colNum) { row, column -> get() = VirtualMatrix(linearSpace.type, l.rowNum, l.colNum) { row, column ->
if (column == pivot[row]) elementAlgebra.one else elementAlgebra.zero if (column == pivot[row]) elementAlgebra.one else elementAlgebra.zero
} }
public val <T> LupDecomposition<T>.determinant by lazy { public val determinant: T by lazy {
elementAlgebra { (0 until l.shape[0]).fold(if (even) one else -one) { value, i -> value * lu[i, i] } } elementAlgebra { (0 until l.shape[0]).fold(if (even) one else -one) { value, i -> value * lu[i, i] } }
} }
} }
public class LupDecompositionAttribute<T>(type: SafeType<LupDecomposition<T>>) : public class LupDecompositionAttribute<T>(type: SafeType<LupDecomposition<T>>) :
PolymorphicAttribute<LupDecomposition<T>>(type), PolymorphicAttribute<LupDecomposition<T>>(type),
MatrixAttribute<LupDecomposition<T>> MatrixAttribute<LupDecomposition<T>>
@ -52,59 +63,6 @@ public class LupDecompositionAttribute<T>(type: SafeType<LupDecomposition<T>>) :
public val <T> MatrixOperations<T>.LUP: LupDecompositionAttribute<T> public val <T> MatrixOperations<T>.LUP: LupDecompositionAttribute<T>
get() = LupDecompositionAttribute(safeTypeOf()) get() = LupDecompositionAttribute(safeTypeOf())
///**
// * Common implementation of [LupDecomposition].
// */
//private class LupDecompositionImpl<T : Any>(
// public val elementContext: Field<T>,
// public val lu: Matrix<T>,
// public val pivot: IntBuffer,
// private val even: Boolean,
//) : LupDecomposition<T> {
// /**
// * Returns the matrix L of the decomposition.
// *
// * L is a lower-triangular matrix with [Ring.one] in diagonal
// */
// override val l: Matrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j ->
// when {
// j < i -> lu[i, j]
// j == i -> elementContext.one
// else -> elementContext.zero
// }
// }.withFeature(LowerTriangular)
//
//
// /**
// * Returns the matrix U of the decomposition.
// *
// * U is an upper-triangular matrix including the diagonal
// */
// override val u: Matrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j ->
// if (j >= i) lu[i, j] else elementContext.zero
// }.withFeature(UpperTriangular)
//
// /**
// * Returns the P rows permutation matrix.
// *
// * P is a sparse matrix with exactly one element set to [Ring.one] in
// * each row and each column, all other elements being set to [Ring.zero].
// */
// override val p: Matrix<T> = VirtualMatrix(lu.shape[0], lu.shape[1]) { i, j ->
// if (j == pivot[i]) elementContext.one else elementContext.zero
// }
//
// /**
// * Return the determinant of the matrix
// * @return determinant of the matrix
// */
// override val determinant: T by lazy {
// elementContext { (0 until lu.shape[0]).fold(if(even) one else -one) { value, i -> value * lu[i, i] } }
// }
//
//}
@PublishedApi @PublishedApi
internal fun <T : Comparable<T>> LinearSpace<T, Ring<T>>.abs(value: T): T = internal fun <T : Comparable<T>> LinearSpace<T, Ring<T>>.abs(value: T): T =
if (value > elementAlgebra.zero) value else elementAlgebra { -value } if (value > elementAlgebra.zero) value else elementAlgebra { -value }
@ -115,97 +73,81 @@ internal fun <T : Comparable<T>> LinearSpace<T, Ring<T>>.abs(value: T): T =
public fun <T : Comparable<T>> LinearSpace<T, Field<T>>.lup( public fun <T : Comparable<T>> LinearSpace<T, Field<T>>.lup(
matrix: Matrix<T>, matrix: Matrix<T>,
checkSingular: (T) -> Boolean, checkSingular: (T) -> Boolean,
): LupDecomposition<T> { ): LupDecomposition<T> = elementAlgebra {
require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" } require(matrix.rowNum == matrix.colNum) { "LU decomposition supports only square matrices" }
val m = matrix.colNum val m = matrix.colNum
val pivot = IntArray(matrix.rowNum) val pivot = IntArray(matrix.rowNum)
//TODO just waits for multi-receivers //TODO just waits for multi-receivers
BufferAccessor2D(matrix.rowNum, matrix.colNum, elementAlgebra.bufferFactory).run { with(BufferAccessor2D(matrix.rowNum, matrix.colNum, elementAlgebra.bufferFactory)){
elementAlgebra {
val lu = create(matrix)
// Initialize the permutation array and parity val lu = create(matrix)
for (row in 0 until m) pivot[row] = row
var even = true
// Initialize the permutation array and parity // Initialize the permutation array and parity
for (row in 0 until m) pivot[row] = row for (row in 0 until m) pivot[row] = row
var even = true
// Loop over columns // Initialize the permutation array and parity
for (col in 0 until m) { for (row in 0 until m) pivot[row] = row
// upper
for (row in 0 until col) {
val luRow = lu.row(row)
var sum = luRow[col]
for (i in 0 until row) sum -= luRow[i] * lu[i, col]
luRow[col] = sum
}
// lower // Loop over columns
var max = col // permutation row for (col in 0 until m) {
var largest = -one // upper
for (row in 0 until col) {
for (row in col until m) { val luRow = lu.row(row)
val luRow = lu.row(row) var sum = luRow[col]
var sum = luRow[col] for (i in 0 until row) sum -= luRow[i] * lu[i, col]
for (i in 0 until col) sum -= luRow[i] * lu[i, col] luRow[col] = sum
luRow[col] = sum
// maintain the best permutation choice
if (abs(sum) > largest) {
largest = abs(sum)
max = row
}
}
// Singularity check
check(!checkSingular(abs(lu[max, col]))) { "The matrix is singular" }
// Pivot if necessary
if (max != col) {
val luMax = lu.row(max)
val luCol = lu.row(col)
for (i in 0 until m) {
val tmp = luMax[i]
luMax[i] = luCol[i]
luCol[i] = tmp
}
val temp = pivot[max]
pivot[max] = pivot[col]
pivot[col] = temp
even = !even
}
// Divide the lower elements by the "winning" diagonal elt.
val luDiag = lu[col, col]
for (row in col + 1 until m) lu[row, col] /= luDiag
} }
val l: MatrixWrapper<T> = VirtualMatrix(type, rowNum, colNum) { i, j -> // lower
when { var max = col // permutation row
j < i -> lu[i, j] var largest = -one
j == i -> one
else -> zero for (row in col until m) {
val luRow = lu.row(row)
var sum = luRow[col]
for (i in 0 until col) sum -= luRow[i] * lu[i, col]
luRow[col] = sum
// maintain the best permutation choice
if (abs(sum) > largest) {
largest = abs(sum)
max = row
} }
}.withAttribute(LowerTriangular) }
val u = VirtualMatrix(type, rowNum, colNum) { i, j -> // Singularity check
if (j >= i) lu[i, j] else zero check(!checkSingular(abs(lu[max, col]))) { "The matrix is singular" }
}.withAttribute(UpperTriangular)
//
// val p = VirtualMatrix(rowNum, colNum) { i, j ->
// if (j == pivot[i]) one else zero
// }.withAttribute(Determinant, if (even) one else -one)
return LupDecomposition(this@lup, l, u, pivot.asBuffer()) // Pivot if necessary
if (max != col) {
val luMax = lu.row(max)
val luCol = lu.row(col)
for (i in 0 until m) {
val tmp = luMax[i]
luMax[i] = luCol[i]
luCol[i] = tmp
}
val temp = pivot[max]
pivot[max] = pivot[col]
pivot[col] = temp
even = !even
}
// Divide the lower elements by the "winning" diagonal elt.
val luDiag = lu[col, col]
for (row in col + 1 until m) lu[row, col] /= luDiag
} }
return LupDecomposition(this@lup, lu.toStructure2D(), pivot.asBuffer(), even)
} }
} }
public fun LinearSpace<Double, Float64Field>.lup( public fun LinearSpace<Double, Float64Field>.lup(
matrix: Matrix<Double>, matrix: Matrix<Double>,
singularityThreshold: Double = 1e-11, singularityThreshold: Double = 1e-11,

View File

@ -7,9 +7,9 @@ package space.kscience.kmath.linear
import space.kscience.attributes.FlagAttribute import space.kscience.attributes.FlagAttribute
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.WithType
import space.kscience.kmath.structures.BufferAccessor2D import space.kscience.kmath.structures.BufferAccessor2D
import space.kscience.kmath.structures.MutableBufferFactory import space.kscience.kmath.structures.MutableBufferFactory

View File

@ -16,7 +16,7 @@ import space.kscience.kmath.operations.Ring
* *
* @param T the type of items. * @param T the type of items.
*/ */
public class MatrixWrapper<out T : Any> internal constructor( public class MatrixWrapper<out T> internal constructor(
public val origin: Matrix<T>, public val origin: Matrix<T>,
override val attributes: Attributes, override val attributes: Attributes,
) : Matrix<T> by origin { ) : Matrix<T> by origin {

View File

@ -5,17 +5,12 @@
package space.kscience.kmath.nd package space.kscience.kmath.nd
import space.kscience.attributes.Attribute import space.kscience.attributes.*
import space.kscience.attributes.AttributeContainer
import space.kscience.attributes.Attributes
import space.kscience.attributes.SafeType
import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.LinearSpace
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.WithType
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import kotlin.jvm.JvmName
import kotlin.math.abs import kotlin.math.abs
public interface StructureAttribute<T> : Attribute<T> public interface StructureAttribute<T> : Attribute<T>
@ -148,28 +143,20 @@ public inline fun <reified T : Any> BufferND(
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): BufferND<T> = BufferND(strides, Buffer(strides.linearSize) { i -> initializer(strides.index(i)) }) ): BufferND<T> = BufferND(strides, Buffer(strides.linearSize) { i -> initializer(strides.index(i)) })
public inline fun <T : Any> BufferND(
type: SafeType<T>,
strides: Strides,
crossinline initializer: (IntArray) -> T,
): BufferND<T> = BufferND(strides, Buffer(type, strides.linearSize) { i -> initializer(strides.index(i)) })
public inline fun <reified T : Any> BufferND( public inline fun <reified T : Any> BufferND(
shape: ShapeND, shape: ShapeND,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): BufferND<T> = BufferND(ColumnStrides(shape), initializer) ): BufferND<T> = BufferND(ColumnStrides(shape), initializer)
@JvmName("autoVarArg")
public inline fun <reified T : Any> BufferND( public inline fun <reified T : Any> BufferND(
vararg shape: Int, vararg shape: Int,
crossinline initializer: (IntArray) -> T, crossinline initializer: (IntArray) -> T,
): BufferND<T> = BufferND(ColumnStrides(ShapeND(shape)), initializer) ): BufferND<T> = BufferND(ColumnStrides(ShapeND(shape)), initializer)
public inline fun <T : Any> BufferND( public fun <T : Any> BufferND(
type: SafeType<T>, type: SafeType<T>,
vararg shape: Int, vararg shape: Int,
crossinline initializer: (IntArray) -> T, initializer: (IntArray) -> T,
): BufferND<T> = BufferND(type, ColumnStrides(ShapeND(shape)), initializer) ): BufferND<T> = BufferND(type, ColumnStrides(ShapeND(shape)), initializer)

View File

@ -6,19 +6,12 @@
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.operations.Ring.Companion.optimizedPower import space.kscience.kmath.operations.Ring.Companion.optimizedPower
import space.kscience.kmath.structures.MutableBufferFactory import space.kscience.kmath.structures.MutableBufferFactory
/**
* An interface containing [type] for dynamic type checking.
*/
public interface WithType<out T> {
public val type: SafeType<T>
}
/** /**
* Represents an algebraic structure. * Represents an algebraic structure.
* *

View File

@ -5,20 +5,18 @@
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.BufferedRingOpsND import space.kscience.kmath.nd.BufferedRingOpsND
import space.kscience.kmath.operations.BigInt.Companion.BASE import space.kscience.kmath.operations.BigInt.Companion.BASE
import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.math.log2 import kotlin.math.log2
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.sign import kotlin.math.sign
private typealias Magnitude = UIntArray private typealias Magnitude = UIntArray
private typealias TBase = ULong
/** /**
* Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger). * Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger).
@ -29,7 +27,7 @@ private typealias TBase = ULong
@OptIn(UnstableKMathAPI::class) @OptIn(UnstableKMathAPI::class)
public object BigIntField : Field<BigInt>, NumbersAddOps<BigInt>, ScaleOperations<BigInt> { public object BigIntField : Field<BigInt>, NumbersAddOps<BigInt>, ScaleOperations<BigInt> {
override val type: SafeType<BigInt> = safeTypeOf() override val bufferFactory: MutableBufferFactory<BigInt> = MutableBufferFactory()
override val zero: BigInt = BigInt.ZERO override val zero: BigInt = BigInt.ZERO
override val one: BigInt = BigInt.ONE override val one: BigInt = BigInt.ONE

View File

@ -5,8 +5,6 @@
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableBufferFactory import space.kscience.kmath.structures.MutableBufferFactory
@ -21,10 +19,10 @@ public interface WithSize {
public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> { public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
public val elementAlgebra: A public val elementAlgebra: A
override val type: SafeType<Buffer<T>> get() = safeTypeOf<Buffer<T>>()
public val elementBufferFactory: MutableBufferFactory<T> get() = elementAlgebra.bufferFactory public val elementBufferFactory: MutableBufferFactory<T> get() = elementAlgebra.bufferFactory
override val bufferFactory: MutableBufferFactory<Buffer<T>> get() = MutableBufferFactory()
public fun buffer(size: Int, vararg elements: T): Buffer<T> { public fun buffer(size: Int, vararg elements: T): Buffer<T> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" } require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
return elementBufferFactory(size) { elements[it] } return elementBufferFactory(size) { elements[it] }

View File

@ -5,10 +5,9 @@
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.structures.MutableBufferFactory
/** /**
* An algebra for generic boolean logic * An algebra for generic boolean logic
@ -74,7 +73,7 @@ public interface LogicAlgebra<T : Any> : Algebra<T> {
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public object BooleanAlgebra : LogicAlgebra<Boolean> { public object BooleanAlgebra : LogicAlgebra<Boolean> {
override val type: SafeType<Boolean> get() = safeTypeOf() override val bufferFactory: MutableBufferFactory<Boolean> get() = MutableBufferFactory()
override fun const(boolean: Boolean): Boolean = boolean override fun const(boolean: Boolean): Boolean = boolean

View File

@ -4,7 +4,7 @@
*/ */
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.kmath.structures.* import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.math.pow as kpow import kotlin.math.pow as kpow
@ -129,7 +129,7 @@ public val Double.Companion.algebra: Float64Field get() = Float64Field
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public object Float32Field : ExtendedField<Float>, Norm<Float, Float> { public object Float32Field : ExtendedField<Float>, Norm<Float, Float> {
override val bufferFactory: MutableBufferFactory<Float> = MutableBufferFactory(::Float32Buffer) override val bufferFactory: MutableBufferFactory<Float> = MutableBufferFactory()
override val zero: Float get() = 0.0f override val zero: Float get() = 0.0f
override val one: Float get() = 1.0f override val one: Float get() = 1.0f
@ -187,7 +187,7 @@ public val Float.Companion.algebra: Float32Field get() = Float32Field
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public object Int32Ring : Ring<Int>, Norm<Int, Int>, NumericAlgebra<Int> { public object Int32Ring : Ring<Int>, Norm<Int, Int>, NumericAlgebra<Int> {
override val bufferFactory: MutableBufferFactory<Int> = MutableBufferFactory(::Int32Buffer) override val bufferFactory: MutableBufferFactory<Int> = MutableBufferFactory()
override val zero: Int get() = 0 override val zero: Int get() = 0
override val one: Int get() = 1 override val one: Int get() = 1
@ -212,7 +212,7 @@ public val Int.Companion.algebra: Int32Ring get() = Int32Ring
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public object Int16Ring : Ring<Short>, Norm<Short, Short>, NumericAlgebra<Short> { public object Int16Ring : Ring<Short>, Norm<Short, Short>, NumericAlgebra<Short> {
override val bufferFactory: MutableBufferFactory<Short> = MutableBufferFactory(::Int16Buffer) override val bufferFactory: MutableBufferFactory<Short> = MutableBufferFactory()
override val zero: Short get() = 0 override val zero: Short get() = 0
override val one: Short get() = 1 override val one: Short get() = 1
@ -237,7 +237,7 @@ public val Short.Companion.algebra: Int16Ring get() = Int16Ring
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public object Int8Ring : Ring<Byte>, Norm<Byte, Byte>, NumericAlgebra<Byte> { public object Int8Ring : Ring<Byte>, Norm<Byte, Byte>, NumericAlgebra<Byte> {
override val bufferFactory: MutableBufferFactory<Byte> = MutableBufferFactory(::Int8Buffer) override val bufferFactory: MutableBufferFactory<Byte> = MutableBufferFactory()
override val zero: Byte get() = 0 override val zero: Byte get() = 0
override val one: Byte get() = 1 override val one: Byte get() = 1
@ -262,7 +262,7 @@ public val Byte.Companion.algebra: Int8Ring get() = Int8Ring
*/ */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") @Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public object Int64Ring : Ring<Long>, Norm<Long, Long>, NumericAlgebra<Long> { public object Int64Ring : Ring<Long>, Norm<Long, Long>, NumericAlgebra<Long> {
override val bufferFactory: MutableBufferFactory<Long> = MutableBufferFactory(::Int64Buffer) override val bufferFactory: MutableBufferFactory<Long> = MutableBufferFactory()
override val zero: Long get() = 0L override val zero: Long get() = 0L
override val one: Long get() = 1L override val one: Long get() = 1L

View File

@ -5,13 +5,16 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
/** /**
* [MutableBuffer] implementation over [Array]. * [MutableBuffer] implementation over [Array].
* *
* @param T the type of elements contained in the buffer. * @param T the type of elements contained in the buffer.
* @property array The underlying array. * @property array The underlying array.
*/ */
public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> { public class ArrayBuffer<T>(override val type: SafeType<T>, internal val array: Array<T>) : MutableBuffer<T> {
// Can't inline because array is invariant // Can't inline because array is invariant
override val size: Int get() = array.size override val size: Int get() = array.size
@ -22,13 +25,17 @@ public class ArrayBuffer<T>(internal val array: Array<T>) : MutableBuffer<T> {
} }
override operator fun iterator(): Iterator<T> = array.iterator() override operator fun iterator(): Iterator<T> = array.iterator()
override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf()) override fun copy(): MutableBuffer<T> = ArrayBuffer(type, array.copyOf())
override fun toString(): String = Buffer.toString(this) override fun toString(): String = Buffer.toString(this)
} }
/**
* Returns an [ArrayBuffer] that wraps the original array.
*/
public fun <T> Array<T>.asBuffer(type: SafeType<T>): ArrayBuffer<T> = ArrayBuffer(type, this)
/** /**
* Returns an [ArrayBuffer] that wraps the original array. * Returns an [ArrayBuffer] that wraps the original array.
*/ */
public fun <T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(this) public inline fun <reified T> Array<T>.asBuffer(): ArrayBuffer<T> = ArrayBuffer(safeTypeOf<T>(), this)

View File

@ -6,9 +6,9 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.attributes.SafeType import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
import space.kscience.attributes.safeTypeOf import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.operations.WithSize import space.kscience.kmath.operations.WithSize
import space.kscience.kmath.operations.WithType
import space.kscience.kmath.operations.asSequence import space.kscience.kmath.operations.asSequence
import kotlin.reflect.typeOf import kotlin.reflect.typeOf

View File

@ -1,5 +1,6 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import space.kscience.attributes.SafeType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
/** /**
@ -8,6 +9,8 @@ import space.kscience.kmath.UnstableKMathAPI
public interface BufferView<T> : Buffer<T> { public interface BufferView<T> : Buffer<T> {
public val origin: Buffer<T> public val origin: Buffer<T>
override val type: SafeType<T> get() = origin.type
/** /**
* Get the index in [origin] buffer from index in this buffer. * Get the index in [origin] buffer from index in this buffer.
* Return -1 if element not present in the original buffer * Return -1 if element not present in the original buffer
@ -36,6 +39,7 @@ public class BufferSlice<T>(
} }
} }
override fun get(index: Int): T = if (index >= size) { override fun get(index: Int): T = if (index >= size) {
throw IndexOutOfBoundsException("$index is out of ${0 until size} rage") throw IndexOutOfBoundsException("$index is out of ${0 until size} rage")
} else { } else {
@ -100,7 +104,8 @@ public fun <T> Buffer<T>.slice(range: IntRange): BufferView<T> = if (this is Buf
* Dynamically create a range from the initial range * Dynamically create a range from the initial range
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public inline fun <T> Buffer<T>.slice(rangeBuilder: IntRange.() -> IntRange): BufferView<T> = slice(rangeBuilder(indices)) public inline fun <T> Buffer<T>.slice(rangeBuilder: IntRange.() -> IntRange): BufferView<T> =
slice(rangeBuilder(indices))
/** /**
* Resize original buffer to a given range using given [range], filling additional segments with [defaultValue]. * Resize original buffer to a given range using given [range], filling additional segments with [defaultValue].

View File

@ -72,15 +72,21 @@ public interface MutableBuffer<T> : Buffer<T> {
* The [size] is specified, and each element is calculated by calling the specified [initializer] function. * The [size] is specified, and each element is calculated by calling the specified [initializer] function.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
public inline fun <T> MutableBuffer(type: SafeType<T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> = public inline fun <T> MutableBuffer(
when (type.kType) { type: SafeType<T>,
typeOf<Double>() -> MutableBuffer.double(size) { initializer(it) as Double } as MutableBuffer<T> size: Int,
typeOf<Short>() -> MutableBuffer.short(size) { initializer(it) as Short } as MutableBuffer<T> initializer: (Int) -> T,
typeOf<Int>() -> MutableBuffer.int(size) { initializer(it) as Int } as MutableBuffer<T> ): MutableBuffer<T> = when (type.kType) {
typeOf<Float>() -> MutableBuffer.float(size) { initializer(it) as Float } as MutableBuffer<T> typeOf<Boolean>() -> TODO()
typeOf<Long>() -> MutableBuffer.long(size) { initializer(it) as Long } as MutableBuffer<T> typeOf<Int8>() -> Int8Buffer(size) { initializer(it) as Int8 } as MutableBuffer<T>
else -> MutableListBuffer(type, MutableList(size, initializer)) typeOf<Int16>() -> MutableBuffer.short(size) { initializer(it) as Int16 } as MutableBuffer<T>
} typeOf<Int32>() -> MutableBuffer.int(size) { initializer(it) as Int32 } as MutableBuffer<T>
typeOf<Int64>() -> MutableBuffer.long(size) { initializer(it) as Int64 } as MutableBuffer<T>
typeOf<Float>() -> MutableBuffer.float(size) { initializer(it) as Float } as MutableBuffer<T>
typeOf<Double>() -> MutableBuffer.double(size) { initializer(it) as Double } as MutableBuffer<T>
//TODO add unsigned types
else -> MutableListBuffer(type, MutableList(size, initializer))
}
/** /**
* Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used * Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used

View File

@ -8,7 +8,6 @@ package space.kscience.kmath.linear
import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.algebra
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -22,7 +21,7 @@ class MatrixTest {
@Test @Test
fun testTranspose() = Double.algebra.linearSpace.run { fun testTranspose() = Double.algebra.linearSpace.run {
val matrix = one(3, 3) val matrix = one(3, 3)
val transposed = matrix.transpose() val transposed = matrix.transposed
assertTrue { StructureND.contentEquals(matrix, transposed) } assertTrue { StructureND.contentEquals(matrix, transposed) }
} }
@ -38,7 +37,7 @@ class MatrixTest {
@Test @Test
fun testMatrixExtension() = Double.algebra.linearSpace.run { fun testMatrixExtension() = Double.algebra.linearSpace.run {
val transitionMatrix: Matrix<Double> = VirtualMatrix(6, 6) { row, col -> val transitionMatrix: Matrix<Double> = VirtualMatrix(type,6, 6) { row, col ->
when { when {
col == 0 -> .50 col == 0 -> .50
row + 1 == col -> .50 row + 1 == col -> .50
@ -60,8 +59,8 @@ class MatrixTest {
@Test @Test
fun test2DDot() = Double.algebra.linearSpace.run { fun test2DDot() = Double.algebra.linearSpace.run {
val firstMatrix = StructureND.auto(2, 3) { (i, j) -> (i + j).toDouble() }.as2D() val firstMatrix = buildMatrix(2, 3) { i, j -> (i + j).toDouble() }
val secondMatrix = StructureND.auto(3, 2) { (i, j) -> (i + j).toDouble() }.as2D() val secondMatrix = buildMatrix(3, 2) { i, j -> (i + j).toDouble() }
// val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() } // val firstMatrix = produce(2, 3) { i, j -> (i + j).toDouble() }
// val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() } // val secondMatrix = produce(3, 2) { i, j -> (i + j).toDouble() }

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.operations package space.kscience.kmath.operations
import space.kscience.kmath.structures.MutableBufferFactory
import java.math.BigDecimal import java.math.BigDecimal
import java.math.BigInteger import java.math.BigInteger
import java.math.MathContext import java.math.MathContext
@ -13,6 +14,8 @@ import java.math.MathContext
* A field over [BigInteger]. * A field over [BigInteger].
*/ */
public object JBigIntegerField : Ring<BigInteger>, NumericAlgebra<BigInteger> { public object JBigIntegerField : Ring<BigInteger>, NumericAlgebra<BigInteger> {
override val bufferFactory: MutableBufferFactory<BigInteger> = MutableBufferFactory()
override val zero: BigInteger get() = BigInteger.ZERO override val zero: BigInteger get() = BigInteger.ZERO
override val one: BigInteger get() = BigInteger.ONE override val one: BigInteger get() = BigInteger.ONE
@ -33,6 +36,7 @@ public object JBigIntegerField : Ring<BigInteger>, NumericAlgebra<BigInteger> {
public abstract class JBigDecimalFieldBase internal constructor( public abstract class JBigDecimalFieldBase internal constructor(
private val mathContext: MathContext = MathContext.DECIMAL64, private val mathContext: MathContext = MathContext.DECIMAL64,
) : Field<BigDecimal>, PowerOperations<BigDecimal>, NumericAlgebra<BigDecimal>, ScaleOperations<BigDecimal> { ) : Field<BigDecimal>, PowerOperations<BigDecimal>, NumericAlgebra<BigDecimal>, ScaleOperations<BigDecimal> {
override val bufferFactory: MutableBufferFactory<BigDecimal> = MutableBufferFactory()
override val zero: BigDecimal override val zero: BigDecimal
get() = BigDecimal.ZERO get() = BigDecimal.ZERO

View File

@ -43,7 +43,7 @@ public interface BlockingBufferChain<out T> : BlockingChain<T>, BufferChain<T> {
public suspend inline fun <reified T : Any> Chain<T>.nextBuffer(size: Int): Buffer<T> = if (this is BufferChain) { public suspend inline fun <reified T : Any> Chain<T>.nextBuffer(size: Int): Buffer<T> = if (this is BufferChain) {
nextBuffer(size) nextBuffer(size)
} else { } else {
Buffer.auto(size) { next() } Buffer(size) { next() }
} }
public inline fun <reified T : Any> BlockingChain<T>.nextBufferBlocking( public inline fun <reified T : Any> BlockingChain<T>.nextBufferBlocking(
@ -51,5 +51,5 @@ public inline fun <reified T : Any> BlockingChain<T>.nextBufferBlocking(
): Buffer<T> = if (this is BlockingBufferChain) { ): Buffer<T> = if (this is BlockingBufferChain) {
nextBufferBlocking(size) nextBufferBlocking(size)
} else { } else {
Buffer.auto(size) { nextBlocking() } Buffer(size) { nextBlocking() }
} }

View File

@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.flatMapConcat import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.BlockingDoubleChain
import space.kscience.kmath.operations.Group
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.Float64Buffer
@ -84,9 +85,9 @@ public fun Flow<Double>.chunked(bufferSize: Int): Flow<Float64Buffer> = flow {
* Map a flow to a moving window buffer. The window step is one. * Map a flow to a moving window buffer. The window step is one.
* To get different steps, one could use skip operation. * To get different steps, one could use skip operation.
*/ */
public fun <T> Flow<T>.windowed(window: Int): Flow<Buffer<T>> = flow { public fun <T> Flow<T>.windowed(window: Int, algebra: Group<T>): Flow<Buffer<T>> = flow {
require(window > 1) { "Window size must be more than one" } require(window > 1) { "Window size must be more than one" }
val ringBuffer = RingBuffer.boxing<T>(window) val ringBuffer = RingBuffer(window, algebra)
this@windowed.collect { element -> this@windowed.collect { element ->
ringBuffer.push(element) ringBuffer.push(element)

View File

@ -7,6 +7,8 @@ package space.kscience.kmath.streaming
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import space.kscience.attributes.SafeType
import space.kscience.kmath.operations.Group
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.VirtualBuffer
@ -14,12 +16,14 @@ import space.kscience.kmath.structures.VirtualBuffer
/** /**
* Thread-safe ring buffer * Thread-safe ring buffer
*/ */
@Suppress("UNCHECKED_CAST")
public class RingBuffer<T>( public class RingBuffer<T>(
private val buffer: MutableBuffer<T?>, private val buffer: MutableBuffer<T>,
private var startIndex: Int = 0, private var startIndex: Int = 0,
size: Int = 0, size: Int = 0,
) : Buffer<T> { ) : Buffer<T> {
override val type: SafeType<T> get() = buffer.type
private val mutex: Mutex = Mutex() private val mutex: Mutex = Mutex()
override var size: Int = size override var size: Int = size
@ -28,7 +32,7 @@ public class RingBuffer<T>(
override operator fun get(index: Int): T { override operator fun get(index: Int): T {
require(index >= 0) { "Index must be positive" } require(index >= 0) { "Index must be positive" }
require(index < size) { "Index $index is out of circular buffer size $size" } require(index < size) { "Index $index is out of circular buffer size $size" }
return buffer[startIndex.forward(index)] as T return buffer[startIndex.forward(index)]
} }
public fun isFull(): Boolean = size == buffer.size public fun isFull(): Boolean = size == buffer.size
@ -43,7 +47,7 @@ public class RingBuffer<T>(
override fun computeNext() { override fun computeNext() {
if (count == 0) done() else { if (count == 0) done() else {
setNext(copy[index] as T) setNext(copy[index])
index = index.forward(1) index = index.forward(1)
count-- count--
} }
@ -55,7 +59,7 @@ public class RingBuffer<T>(
*/ */
public suspend fun snapshot(): Buffer<T> = mutex.withLock { public suspend fun snapshot(): Buffer<T> = mutex.withLock {
val copy = buffer.copy() val copy = buffer.copy()
VirtualBuffer(size) { i -> copy[startIndex.forward(i)] as T } VirtualBuffer(type, size) { i -> copy[startIndex.forward(i)] }
} }
public suspend fun push(element: T) { public suspend fun push(element: T) {
@ -68,19 +72,17 @@ public class RingBuffer<T>(
private fun Int.forward(n: Int): Int = (this + n) % (buffer.size) private fun Int.forward(n: Int): Int = (this + n) % (buffer.size)
override fun toString(): String = Buffer.toString(this) override fun toString(): String = Buffer.toString(this)
}
public companion object {
public inline fun <reified T : Any> build(size: Int, empty: T): RingBuffer<T> { public inline fun <reified T : Any> RingBuffer(size: Int, empty: T): RingBuffer<T> {
val buffer = MutableBuffer.auto(size) { empty } as MutableBuffer<T?> val buffer = MutableBuffer(size) { empty }
return RingBuffer(buffer) return RingBuffer(buffer)
} }
/** /**
* Slow yet universal buffer * Slow yet universal buffer
*/ */
public fun <T> boxing(size: Int): RingBuffer<T> { public fun <T> RingBuffer(size: Int, algebra: Group<T>): RingBuffer<T> {
val buffer: MutableBuffer<T?> = MutableBuffer.boxing(size) { null } val buffer: MutableBuffer<T> = MutableBuffer(algebra.type, size) { algebra.zero }
return RingBuffer(buffer) return RingBuffer(buffer)
}
}
} }

View File

@ -6,6 +6,8 @@
package space.kscience.kmath.structures package space.kscience.kmath.structures
import kotlinx.coroutines.* import kotlinx.coroutines.*
import space.kscience.attributes.SafeType
import space.kscience.attributes.safeTypeOf
import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.PerformancePitfall
import space.kscience.kmath.coroutines.Math import space.kscience.kmath.coroutines.Math
import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ColumnStrides
@ -14,9 +16,11 @@ import space.kscience.kmath.nd.StructureND
public class LazyStructureND<out T>( public class LazyStructureND<out T>(
public val scope: CoroutineScope, public val scope: CoroutineScope,
override val type: SafeType<T>,
override val shape: ShapeND, override val shape: ShapeND,
public val function: suspend (IntArray) -> T, public val function: suspend (IntArray) -> T,
) : StructureND<T> { ) : StructureND<T> {
private val cache: MutableMap<IntArray, Deferred<T>> = HashMap() private val cache: MutableMap<IntArray, Deferred<T>> = HashMap()
public fun async(index: IntArray): Deferred<T> = cache.getOrPut(index) { public fun async(index: IntArray): Deferred<T> = cache.getOrPut(index) {
@ -47,13 +51,13 @@ public suspend fun <T> StructureND<T>.await(index: IntArray): T =
* PENDING would benefit from KEEP-176 * PENDING would benefit from KEEP-176
*/ */
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
public inline fun <T, R> StructureND<T>.mapAsyncIndexed( public inline fun <T, reified R> StructureND<T>.mapAsyncIndexed(
scope: CoroutineScope, scope: CoroutineScope,
crossinline function: suspend (T, index: IntArray) -> R, crossinline function: suspend (T, index: IntArray) -> R,
): LazyStructureND<R> = LazyStructureND(scope, shape) { index -> function(get(index), index) } ): LazyStructureND<R> = LazyStructureND(scope, safeTypeOf(), shape) { index -> function(get(index), index) }
@OptIn(PerformancePitfall::class) @OptIn(PerformancePitfall::class)
public inline fun <T, R> StructureND<T>.mapAsync( public inline fun <T, reified R> StructureND<T>.mapAsync(
scope: CoroutineScope, scope: CoroutineScope,
crossinline function: suspend (T) -> R, crossinline function: suspend (T) -> R,
): LazyStructureND<R> = LazyStructureND(scope, shape) { index -> function(get(index)) } ): LazyStructureND<R> = LazyStructureND(scope, safeTypeOf(), shape) { index -> function(get(index)) }

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.dimensions package space.kscience.kmath.dimensions
import space.kscience.attributes.SafeType
import space.kscience.kmath.linear.* import space.kscience.kmath.linear.*
import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.ShapeND
import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.Structure2D
@ -48,6 +49,9 @@ public interface DMatrix<out T, R : Dimension, C : Dimension> : Structure2D<T> {
public value class DMatrixWrapper<out T, R : Dimension, C : Dimension>( public value class DMatrixWrapper<out T, R : Dimension, C : Dimension>(
private val structure: Structure2D<T>, private val structure: Structure2D<T>,
) : DMatrix<T, R, C> { ) : DMatrix<T, R, C> {
override val type: SafeType<T> get() = structure.type
override val shape: ShapeND get() = structure.shape override val shape: ShapeND get() = structure.shape
override val rowNum: Int get() = shape[0] override val rowNum: Int get() = shape[0]
override val colNum: Int get() = shape[1] override val colNum: Int get() = shape[1]
@ -75,8 +79,10 @@ public interface DPoint<out T, D : Dimension> : Point<T> {
* Dimension-safe point wrapper * Dimension-safe point wrapper
*/ */
@JvmInline @JvmInline
public value class DPointWrapper<out T, D : Dimension>(public val point: Point<T>) : public value class DPointWrapper<out T, D : Dimension>(public val point: Point<T>) : DPoint<T, D> {
DPoint<T, D> {
override val type: SafeType<T> get() = point.type
override val size: Int get() = point.size override val size: Int get() = point.size
override operator fun get(index: Int): T = point[index] override operator fun get(index: Int): T = point[index]

View File

@ -6,14 +6,9 @@
package space.kscience.kmath.ejml package space.kscience.kmath.ejml
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.linear.InverseMatrixFeature import space.kscience.kmath.linear.*
import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.linear.Matrix
import space.kscience.kmath.linear.Point
import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import kotlin.reflect.KType
import kotlin.reflect.typeOf
/** /**
* [LinearSpace] implementation specialized for a certain EJML type. * [LinearSpace] implementation specialized for a certain EJML type.
@ -42,8 +37,7 @@ public abstract class EjmlLinearSpace<T : Any, out A : Ring<T>, out M : org.ejml
public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector<T, M> public abstract override fun buildVector(size: Int, initializer: A.(Int) -> T): EjmlVector<T, M>
@Suppress("UNCHECKED_CAST")
@UnstableKMathAPI @UnstableKMathAPI
public fun EjmlMatrix<T, *>.inverse(): Structure2D<Double> = public fun EjmlMatrix<T, *>.inverted(): Matrix<Double> =
attributeFor(this, InverseMatrixFeature::class)?.inverse as Structure2D<Double> attributeFor(this, Float64Field.linearSpace.Inverted)
} }

View File

@ -96,7 +96,7 @@ internal class EjmlMatrixTest {
val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 } val u = space.buildMatrix(dim, dim) { i, j -> if (i <= j) random.nextDouble() else 0.0 }
val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 } val l = space.buildMatrix(dim, dim) { i, j -> if (i >= j) random.nextDouble() else 0.0 }
val matrix = space { l dot u } val matrix = space { l dot u }
val inverted = matrix.toEjml().inverse() val inverted = matrix.toEjml().inverted()
val res = matrix dot inverted val res = matrix dot inverted

View File

@ -48,7 +48,7 @@ public fun Sequence<DoubleArray>.toMatrix(): RealMatrix = toList().let {
} }
public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix = public fun RealMatrix.repeatStackVertical(n: Int): RealMatrix =
VirtualMatrix(rowNum * n, colNum) { row, col -> VirtualMatrix(type, rowNum * n, colNum) { row, col ->
get(if (row == 0) 0 else row % rowNum, col) get(if (row == 0) 0 else row % rowNum, col)
} }

View File

@ -7,14 +7,12 @@
package space.kscience.kmath.functions package space.kscience.kmath.functions
import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.ScaleOperations import space.kscience.kmath.operations.ScaleOperations
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.MutableBufferFactory
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.reflect.KType
import kotlin.reflect.typeOf
/** /**
@ -67,6 +65,7 @@ public data class Polynomial<out C>(
public open class PolynomialSpace<C, A>( public open class PolynomialSpace<C, A>(
public val ring: A, public val ring: A,
) : Ring<Polynomial<C>>, ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> { ) : Ring<Polynomial<C>>, ScaleOperations<Polynomial<C>> where A : Ring<C>, A : ScaleOperations<C> {
override val bufferFactory: MutableBufferFactory<Polynomial<C>> get() = MutableBufferFactory()
/** /**
* Instance of zero constant (zero of the underlying ring). * Instance of zero constant (zero of the underlying ring).

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.integration package space.kscience.kmath.integration
import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.mapToBuffer import space.kscience.kmath.operations.mapToBuffer
import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.Float64Buffer
@ -32,11 +33,11 @@ public fun GaussIntegratorRuleFactory.build(
val normalized: Pair<Buffer<Double>, Buffer<Double>> = build(numPoints) val normalized: Pair<Buffer<Double>, Buffer<Double>> = build(numPoints)
val length = range.endInclusive - range.start val length = range.endInclusive - range.start
val points = normalized.first.mapToBuffer(::Float64Buffer) { val points = normalized.first.mapToBuffer(Float64Field.bufferFactory) {
range.start + length / 2 + length / 2 * it range.start + length / 2 + length / 2 * it
} }
val weights = normalized.second.mapToBuffer(::Float64Buffer) { val weights = normalized.second.mapToBuffer(Float64Field.bufferFactory) {
it * length / 2 it * length / 2
} }

View File

@ -88,7 +88,7 @@ public class SplineIntegrator<T : Comparable<T>>(
public object DoubleSplineIntegrator : UnivariateIntegrator<Double> { public object DoubleSplineIntegrator : UnivariateIntegrator<Double> {
override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> { override fun integrate(integrand: UnivariateIntegrand<Double>): UnivariateIntegrand<Double> {
val range = integrand[IntegrationRange] ?: 0.0..1.0 val range = integrand[IntegrationRange] ?: 0.0..1.0
val interpolator: PolynomialInterpolator<Double> = SplineInterpolator(Float64Field, ::Float64Buffer) val interpolator: PolynomialInterpolator<Double> = SplineInterpolator(Float64Field, Float64Field.bufferFactory)
val nodes: Buffer<Double> = integrand[UnivariateIntegrationNodes] ?: run { val nodes: Buffer<Double> = integrand[UnivariateIntegrationNodes] ?: run {
val numPoints = integrand[IntegrandMaxCalls] ?: 100 val numPoints = integrand[IntegrandMaxCalls] ?: 100
@ -96,7 +96,7 @@ public object DoubleSplineIntegrator : UnivariateIntegrator<Double> {
Float64Buffer(numPoints) { i -> range.start + i * step } Float64Buffer(numPoints) { i -> range.start + i * step }
} }
val values = nodes.mapToBuffer(::Float64Buffer) { integrand.function(it) } val values = nodes.mapToBuffer(Float64Field.bufferFactory) { integrand.function(it) }
val polynomials = interpolator.interpolatePolynomials(nodes, values) val polynomials = interpolator.interpolatePolynomials(nodes, values)
val res = polynomials.integrate(Float64Field, range) val res = polynomials.integrate(Float64Field, range)
return integrand.withAttributes { return integrand.withAttributes {

View File

@ -7,6 +7,8 @@
package space.kscience.kmath.interpolation package space.kscience.kmath.interpolation
import space.kscience.attributes.SafeType
import space.kscience.attributes.WithType
import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.UnstableKMathAPI
import space.kscience.kmath.data.XYColumnarData import space.kscience.kmath.data.XYColumnarData
import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.PiecewisePolynomial
@ -26,9 +28,11 @@ public fun interface Interpolator<T, in X : T, Y : T> {
/** /**
* And interpolator returning [PiecewisePolynomial] function * And interpolator returning [PiecewisePolynomial] function
*/ */
public interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T, T> { public interface PolynomialInterpolator<T : Comparable<T>> : Interpolator<T, T, T>, WithType<T> {
public val algebra: Ring<T> public val algebra: Ring<T>
override val type: SafeType<T> get() = algebra.type
public fun getDefaultValue(): T = error("Out of bounds") public fun getDefaultValue(): T = error("Out of bounds")
public fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T> public fun interpolatePolynomials(points: XYColumnarData<T, T, T>): PiecewisePolynomial<T>
@ -50,14 +54,14 @@ public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
data: Map<T, T>, data: Map<T, T>,
): PiecewisePolynomial<T> { ): PiecewisePolynomial<T> {
val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(), data.values.toList().asBuffer()) val pointSet = XYColumnarData.of(data.keys.toList().asBuffer(type), data.values.toList().asBuffer(type))
return interpolatePolynomials(pointSet) return interpolatePolynomials(pointSet)
} }
public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials( public fun <T : Comparable<T>> PolynomialInterpolator<T>.interpolatePolynomials(
data: List<Pair<T, T>>, data: List<Pair<T, T>>,
): PiecewisePolynomial<T> { ): PiecewisePolynomial<T> {
val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(), data.map { it.second }.asBuffer()) val pointSet = XYColumnarData.of(data.map { it.first }.asBuffer(type), data.map { it.second }.asBuffer(type))
return interpolatePolynomials(pointSet) return interpolatePolynomials(pointSet)
} }

View File

@ -12,7 +12,6 @@ import space.kscience.kmath.functions.Polynomial
import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Field
import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Float64Field
import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Float64Buffer
import space.kscience.kmath.structures.MutableBufferFactory import space.kscience.kmath.structures.MutableBufferFactory
/** /**
@ -80,4 +79,4 @@ public fun <T : Comparable<T>> Field<T>.splineInterpolator(
): SplineInterpolator<T> = SplineInterpolator(this, bufferFactory) ): SplineInterpolator<T> = SplineInterpolator(this, bufferFactory)
public val Float64Field.splineInterpolator: SplineInterpolator<Double> public val Float64Field.splineInterpolator: SplineInterpolator<Double>
get() = SplineInterpolator(this, ::Float64Buffer) get() = SplineInterpolator(this, bufferFactory)

View File

@ -7,6 +7,10 @@ kscience {
js() js()
native() native()
wasm() wasm()
dependencies {
api(projects.kmathCore)
}
} }
readme { readme {

View File

@ -1,11 +1,13 @@
/* /*
* Copyright 2018-2022 KMath contributors. * Copyright 2018-2023 KMath contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package space.kscience.kmath.structures package space.kscience.kmath.memory
import space.kscience.kmath.memory.* import space.kscience.attributes.SafeType
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer
/** /**
* A non-boxing buffer over [Memory] object. * A non-boxing buffer over [Memory] object.
@ -15,6 +17,9 @@ import space.kscience.kmath.memory.*
* @property spec the spec of [T] type. * @property spec the spec of [T] type.
*/ */
public open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> { public open class MemoryBuffer<T : Any>(protected val memory: Memory, protected val spec: MemorySpec<T>) : Buffer<T> {
override val type: SafeType<T> get() = spec.type
override val size: Int get() = memory.size / spec.objectSize override val size: Int get() = memory.size / spec.objectSize
override operator fun get(index: Int): T = memory.read { read(spec, spec.objectSize * index) } override operator fun get(index: Int): T = memory.read { read(spec, spec.objectSize * index) }
@ -43,8 +48,10 @@ public open class MemoryBuffer<T : Any>(protected val memory: Memory, protected
* @property memory the underlying memory segment. * @property memory the underlying memory segment.
* @property spec the spec of [T] type. * @property spec the spec of [T] type.
*/ */
public class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) : MemoryBuffer<T>(memory, spec), public class MutableMemoryBuffer<T : Any>(
MutableBuffer<T> { memory: Memory,
spec: MemorySpec<T>,
) : MemoryBuffer<T>(memory, spec), MutableBuffer<T> {
private val writer: MemoryWriter = memory.writer() private val writer: MemoryWriter = memory.writer()

View File

@ -5,12 +5,15 @@
package space.kscience.kmath.memory package space.kscience.kmath.memory
import space.kscience.attributes.WithType
/** /**
* A specification to read or write custom objects with fixed size in bytes. * A specification to read or write custom objects with fixed size in bytes.
* *
* @param T the type of object this spec manages. * @param T the type of object this spec manages.
*/ */
public interface MemorySpec<T : Any> { public interface MemorySpec<T : Any>: WithType<T> {
/** /**
* Size of [T] in bytes after serialization. * Size of [T] in bytes after serialization.
*/ */