diff --git a/doc/linear.md b/doc/linear.md index 883df275e..6ccc6caac 100644 --- a/doc/linear.md +++ b/doc/linear.md @@ -6,10 +6,10 @@ back-ends. The new operations added as extensions to contexts instead of being m Two major contexts used for linear algebra and hyper-geometry: -* `VectorSpace` forms a mathematical space on top of array-like structure (`Buffer` and its typealias `Point` used for geometry). +* `VectorSpace` forms a mathematical space on top of array-like structure (`Buffer` and its type alias `Point` used for geometry). * `MatrixContext` forms a space-like context for 2d-structures. It does not store matrix size and therefore does not implement -`Space` interface (it is not possible to create zero element without knowing the matrix size). +`Space` interface (it is impossible to create zero element without knowing the matrix size). ## Vector spaces diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts index 3d193efce..4e752fc35 100644 --- a/examples/build.gradle.kts +++ b/examples/build.gradle.kts @@ -28,7 +28,9 @@ dependencies { implementation(project(":kmath-dimensions")) implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:0.2.0-npm-dev-6") implementation("org.jetbrains.kotlinx:kotlinx.benchmark.runtime:0.2.0-dev-20") - "benchmarksCompile"(sourceSets.main.get().output + sourceSets.main.get().compileClasspath) //sourceSets.main.output + sourceSets.main.runtimeClasspath + implementation("org.slf4j:slf4j-simple:1.7.30") + "benchmarksImplementation"("org.jetbrains.kotlinx:kotlinx.benchmark.runtime-jvm:0.2.0-dev-8") + "benchmarksImplementation"(sourceSets.main.get().output + sourceSets.main.get().runtimeClasspath) } // Configure benchmark diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt index 7772facb0..a91d02253 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ArrayBenchmark.kt @@ -6,7 +6,7 @@ import org.openjdk.jmh.annotations.State import java.nio.IntBuffer @State(Scope.Benchmark) -class ArrayBenchmark { +internal class ArrayBenchmark { @Benchmark fun benchmarkArrayRead() { var res = 0 diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt index 40ba49a34..8b6fd4a51 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/BufferBenchmark.kt @@ -7,8 +7,7 @@ import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State @State(Scope.Benchmark) -class BufferBenchmark { - +internal class BufferBenchmark { @Benchmark fun genericRealBufferReadWrite() { val buffer = RealBuffer(size) { it.toDouble() } diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt index 8f37feda4..8ec47ae81 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/NDFieldBenchmark.kt @@ -7,7 +7,7 @@ import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State @State(Scope.Benchmark) -class NDFieldBenchmark { +internal class NDFieldBenchmark { @Benchmark fun autoFieldAdd() { bufferedField { diff --git a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt index 7f55c0560..464925ca0 100644 --- a/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt +++ b/examples/src/benchmarks/kotlin/kscience/kmath/structures/ViktorBenchmark.kt @@ -9,7 +9,7 @@ import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State @State(Scope.Benchmark) -class ViktorBenchmark { +internal class ViktorBenchmark { final val dim: Int = 1000 final val n: Int = 100 @@ -42,7 +42,7 @@ class ViktorBenchmark { } @Benchmark - fun realdFieldLog() { + fun realFieldLog() { realField { val fortyTwo = produce { 42.0 } var res = one diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt index a2d7a0291..9c0a01961 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionBenchmark.kt @@ -14,11 +14,10 @@ private fun runChain(): Duration { val generator = RandomGenerator.fromSource(RandomSource.MT, 123L) val normal = Distribution.normal(NormalSamplerMethod.Ziggurat) val chain = normal.sample(generator) as BlockingRealChain - val startTime = Instant.now() var sum = 0.0 - repeat(10000001) { counter -> + repeat(10000001) { counter -> sum += chain.nextDouble() if (counter % 100000 == 0) { @@ -27,6 +26,7 @@ private fun runChain(): Duration { println("Chain sampler completed $counter elements in $duration: $meanValue") } } + return Duration.between(startTime, Instant.now()) } @@ -34,10 +34,9 @@ private fun runDirect(): Duration { val provider = RandomSource.create(RandomSource.MT, 123L) val sampler = ZigguratNormalizedGaussianSampler(provider) val startTime = Instant.now() - var sum = 0.0 - repeat(10000001) { counter -> + repeat(10000001) { counter -> sum += sampler.sample() if (counter % 100000 == 0) { @@ -46,6 +45,7 @@ private fun runDirect(): Duration { println("Direct sampler completed $counter elements in $duration: $meanValue") } } + return Duration.between(startTime, Instant.now()) } @@ -54,14 +54,8 @@ private fun runDirect(): Duration { */ fun main() { runBlocking(Dispatchers.Default) { - val chainJob = async { - runChain() - } - - val directJob = async { - runDirect() - } - + val chainJob = async { runChain() } + val directJob = async { runDirect() } println("Chain: ${chainJob.await()}") println("Direct: ${directJob.await()}") } diff --git a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt index dbb51267b..7d53e5178 100644 --- a/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/commons/prob/DistributionDemo.kt @@ -7,9 +7,9 @@ import kscience.kmath.prob.Distribution import kscience.kmath.prob.RandomGenerator import kscience.kmath.prob.normal -data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) +private data class AveragingChainState(var num: Int = 0, var value: Double = 0.0) -fun Chain.mean(): Chain = collectWithState(AveragingChainState(), { it.copy() }) { chain -> +private fun Chain.mean(): Chain = collectWithState(AveragingChainState(), { it.copy() }) { chain -> val next = chain.next() num++ value += next diff --git a/examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt b/examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt index 692ea6d8c..0e9811ff8 100644 --- a/examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt +++ b/examples/src/main/kotlin/kscience/kmath/operations/BigIntDemo.kt @@ -1,8 +1,6 @@ package kscience.kmath.operations fun main() { - val res = BigIntField { - number(1) * 2 - } + val res = BigIntField { number(1) * 2 } println("bigint:$res") } \ No newline at end of file diff --git a/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt index 00606ec08..51fd4f956 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureReadBenchmark.kt @@ -4,11 +4,9 @@ import kotlin.system.measureTimeMillis fun main() { val n = 6000 - val array = DoubleArray(n * n) { 1.0 } val buffer = RealBuffer(array) val strides = DefaultStrides(intArrayOf(n, n)) - val structure = BufferNDStructure(strides, buffer) measureTimeMillis { diff --git a/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt index b2975393f..db55b454f 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/StructureWriteBenchmark.kt @@ -4,24 +4,17 @@ import kotlin.system.measureTimeMillis fun main() { val n = 6000 - val structure = NDStructure.build(intArrayOf(n, n), Buffer.Companion::auto) { 1.0 } - structure.mapToBuffer { it + 1 } // warm-up - - val time1 = measureTimeMillis { - val res = structure.mapToBuffer { it + 1 } - } + val time1 = measureTimeMillis { val res = structure.mapToBuffer { it + 1 } } println("Structure mapping finished in $time1 millis") - val array = DoubleArray(n * n) { 1.0 } val time2 = measureTimeMillis { val target = DoubleArray(n * n) - val res = array.forEachIndexed { index, value -> - target[index] = value + 1 - } + val res = array.forEachIndexed { index, value -> target[index] = value + 1 } } + println("Array mapping finished in $time2 millis") val buffer = RealBuffer(DoubleArray(n * n) { 1.0 }) diff --git a/examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt b/examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt index bf83a9f05..987eea16f 100644 --- a/examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt +++ b/examples/src/main/kotlin/kscience/kmath/structures/typeSafeDimensions.kt @@ -6,7 +6,7 @@ import kscience.kmath.dimensions.DMatrixContext import kscience.kmath.dimensions.Dimension import kscience.kmath.operations.RealField -fun DMatrixContext.simple() { +private fun DMatrixContext.simple() { val m1 = produce { i, j -> (i + j).toDouble() } val m2 = produce { i, j -> (i + j).toDouble() } @@ -14,12 +14,11 @@ fun DMatrixContext.simple() { m1.transpose() + m2 } - -object D5 : Dimension { +private object D5 : Dimension { override val dim: UInt = 5u } -fun DMatrixContext.custom() { +private fun DMatrixContext.custom() { val m1 = produce { i, j -> (i + j).toDouble() } val m2 = produce { i, j -> (i - j).toDouble() } val m3 = produce { i, j -> (i - j).toDouble() } diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt index 8a09cc793..c39f0d04c 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DiffExpression.kt @@ -9,14 +9,17 @@ import org.apache.commons.math3.analysis.differentiation.DerivativeStructure import kotlin.properties.ReadOnlyProperty /** - * A field wrapping commons-math derivative structures + * A field over commons-math [DerivativeStructure]. + * + * @property order The derivation order. + * @property parameters The map of free parameters. */ public class DerivativeStructureField( public val order: Int, public val parameters: Map ) : ExtendedField { - public override val zero: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size) } - public override val one: DerivativeStructure by lazy { DerivativeStructure(order, parameters.size, 1.0) } + public override val zero: DerivativeStructure by lazy { DerivativeStructure(parameters.size, order) } + public override val one: DerivativeStructure by lazy { DerivativeStructure(parameters.size, order, 1.0) } private val variables: Map = parameters.mapValues { (key, value) -> DerivativeStructure(parameters.size, order, parameters.keys.indexOf(key), value) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt index 9aec6e178..d410d304a 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Complex.kt @@ -3,6 +3,7 @@ package kscience.kmath.operations import kscience.kmath.structures.Buffer import kscience.kmath.structures.MemoryBuffer import kscience.kmath.structures.MutableBuffer +import kscience.kmath.structures.MutableMemoryBuffer import kscience.memory.MemoryReader import kscience.memory.MemorySpec import kscience.memory.MemoryWriter @@ -177,10 +178,10 @@ public data class Complex(val re: Double, val im: Double) : FieldElement { - override val objectSize: Int = 16 + override val objectSize: Int + get() = 16 - override fun MemoryReader.read(offset: Int): Complex = - Complex(readDouble(offset), readDouble(offset + 8)) + override fun MemoryReader.read(offset: Int): Complex = Complex(readDouble(offset), readDouble(offset + 8)) override fun MemoryWriter.write(offset: Int, value: Complex) { writeDouble(offset, value.re) @@ -197,8 +198,16 @@ public data class Complex(val re: Double, val im: Double) : FieldElement Complex): Buffer = MemoryBuffer.create(Complex, size, init) -public inline fun MutableBuffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer = - MemoryBuffer.create(Complex, size, init) +/** + * Creates a new buffer of complex numbers with the specified [size], where each element is calculated by calling the + * specified [init] function. + */ +public inline fun MutableBuffer.Companion.complex(size: Int, init: (Int) -> Complex): MutableBuffer = + MutableMemoryBuffer.create(Complex, size, init) diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt index c3c859f7a..dc65b12c4 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDField.kt @@ -15,8 +15,9 @@ public class BoxingNDField>( public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) - public override fun check(vararg elements: NDBuffer) { - check(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } + public override fun check(vararg elements: NDBuffer): Array> { + require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } + return elements } public override fun produce(initializer: F.(IntArray) -> T): BufferedNDFieldElement = @@ -75,6 +76,6 @@ public inline fun , R> F.nd( vararg shape: Int, action: NDField.() -> R ): R { - val ndfield: BoxingNDField = NDField.boxing(this, *shape, bufferFactory = bufferFactory) + val ndfield = NDField.boxing(this, *shape, bufferFactory = bufferFactory) return ndfield.action() } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt index 461b0387c..b6794984c 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BoxingNDRing.kt @@ -14,8 +14,9 @@ public class BoxingNDRing>( public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer = bufferFactory(size, initializer) - override fun check(vararg elements: NDBuffer) { - require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" } + override fun check(vararg elements: NDBuffer): Array> { + if (!elements.all { it.strides == this.strides }) error("Element strides are not the same as context strides") + return elements } override fun produce(initializer: R.(IntArray) -> T): BufferedNDRingElement = diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt index 66b4f19e1..3dcd0322c 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/BufferedNDAlgebra.kt @@ -5,8 +5,10 @@ import kscience.kmath.operations.* public interface BufferedNDAlgebra : NDAlgebra> { public val strides: Strides - public override fun check(vararg elements: NDBuffer): Unit = - require(elements.all { it.strides == strides }) { ("Strides mismatch") } + public override fun check(vararg elements: NDBuffer): Array> { + require(elements.all { it.strides == strides }) { "Strides mismatch" } + return elements + } /** * Convert any [NDStructure] to buffered structure using strides from this context. diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt index 53587e503..5174eb314 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/Buffers.kt @@ -46,35 +46,48 @@ public interface Buffer { asSequence().mapIndexed { index, value -> value == other[index] }.all { it } public companion object { - public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer { - val array = DoubleArray(size) { initializer(it) } - return RealBuffer(array) - } + /** + * Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer = + RealBuffer(size) { initializer(it) } /** - * Create a boxing buffer of given type + * Creates a [ListBuffer] of given type [T] with given [size]. Each element is calculated by calling the + * specified [initializer] function. */ public inline fun boxing(size: Int, initializer: (Int) -> T): Buffer = ListBuffer(List(size, initializer)) + // TODO add resolution based on Annotation or companion resolution + + /** + * Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([IntBuffer], + * [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * + * The [size] is specified, and each element is calculated by calling the specified [initializer] function. + */ @Suppress("UNCHECKED_CAST") - public inline fun auto(type: KClass, size: Int, crossinline initializer: (Int) -> T): Buffer { - //TODO add resolution based on Annotation or companion resolution - return when (type) { - Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer - Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer - Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer - Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer + public inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): Buffer = + when (type) { + Double::class -> RealBuffer(size) { initializer(it) as Double } as Buffer + Short::class -> ShortBuffer(size) { initializer(it) as Short } as Buffer + Int::class -> IntBuffer(size) { initializer(it) as Int } as Buffer + Long::class -> LongBuffer(size) { initializer(it) as Long } as Buffer + Float::class -> FloatBuffer(size) { initializer(it) as Float } as Buffer Complex::class -> complex(size) { initializer(it) as Complex } as Buffer else -> boxing(size, initializer) } - } /** - * Create most appropriate immutable buffer for given type avoiding boxing wherever possible + * Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([IntBuffer], + * [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * + * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @Suppress("UNCHECKED_CAST") - public inline fun auto(size: Int, crossinline initializer: (Int) -> T): Buffer = + public inline fun auto(size: Int, initializer: (Int) -> T): Buffer = auto(T::class, size, initializer) } } @@ -117,25 +130,40 @@ public interface MutableBuffer : Buffer { public inline fun boxing(size: Int, initializer: (Int) -> T): MutableBuffer = MutableListBuffer(MutableList(size, initializer)) + /** + * Creates a [MutableBuffer] of given [type]. If the type is primitive, specialized buffers are used + * ([IntBuffer], [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * + * The [size] is specified, and each element is calculated by calling the specified [initializer] function. + */ @Suppress("UNCHECKED_CAST") public inline fun auto(type: KClass, size: Int, initializer: (Int) -> T): MutableBuffer = when (type) { - Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer - Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as MutableBuffer - Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer - Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as MutableBuffer + Double::class -> RealBuffer(size) { initializer(it) as Double } as MutableBuffer + Short::class -> ShortBuffer(size) { initializer(it) as Short } as MutableBuffer + Int::class -> IntBuffer(size) { initializer(it) as Int } as MutableBuffer + Float::class -> FloatBuffer(size) { initializer(it) as Float } as MutableBuffer + Long::class -> LongBuffer(size) { initializer(it) as Long } as MutableBuffer + Complex::class -> complex(size) { initializer(it) as Complex } as MutableBuffer else -> boxing(size, initializer) } /** - * Create most appropriate mutable buffer for given type avoiding boxing wherever possible + * Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used + * ([IntBuffer], [RealBuffer], etc.), [ListBuffer] is returned otherwise. + * + * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @Suppress("UNCHECKED_CAST") public inline fun auto(size: Int, initializer: (Int) -> T): MutableBuffer = auto(T::class, size, initializer) - public val real: MutableBufferFactory = - { size, initializer -> RealBuffer(DoubleArray(size) { initializer(it) }) } + /** + * Creates a [RealBuffer] with the specified [size], where each element is calculated by calling the specified + * [initializer] function. + */ + public inline fun real(size: Int, initializer: (Int) -> Double): RealBuffer = + RealBuffer(size) { initializer(it) } } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt index b7e6a8218..308256992 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/MemoryBuffer.kt @@ -53,7 +53,7 @@ public class MutableMemoryBuffer(memory: Memory, spec: MemorySpec) : public inline fun create( spec: MemorySpec, size: Int, - crossinline initializer: (Int) -> T + initializer: (Int) -> T ): MutableMemoryBuffer = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer -> (0 until size).forEach { buffer[it] = initializer(it) } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt index 28eaef2f1..4315f0423 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDAlgebra.kt @@ -6,49 +6,77 @@ import kscience.kmath.operations.Ring import kscience.kmath.operations.Space /** - * An exception is thrown when the expected ans actual shape of NDArray differs + * An exception is thrown when the expected ans actual shape of NDArray differs. + * + * @property expected the expected shape. + * @property actual the actual shape. */ -public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : RuntimeException() +public class ShapeMismatchException(public val expected: IntArray, public val actual: IntArray) : + RuntimeException("Shape ${actual.contentToString()} doesn't fit in expected shape ${expected.contentToString()}.") /** - * The base interface for all nd-algebra implementations - * @param T the type of nd-structure element - * @param C the type of the element context - * @param N the type of the structure + * The base interface for all ND-algebra implementations. + * + * @param T the type of ND-structure element. + * @param C the type of the element context. + * @param N the type of the structure. */ public interface NDAlgebra> { + /** + * The shape of ND-structures this algebra operates on. + */ public val shape: IntArray + + /** + * The algebra over elements of ND structure. + */ public val elementContext: C /** - * Produce a new [N] structure using given initializer function + * Produces a new [N] structure using given initializer function. */ public fun produce(initializer: C.(IntArray) -> T): N /** - * Map elements from one structure to another one + * Maps elements from one structure to another one by applying [transform] to them. */ public fun map(arg: N, transform: C.(T) -> T): N /** - * Map indexed elements + * Maps elements from one structure to another one by applying [transform] to them alongside with their indices. */ public fun mapIndexed(arg: N, transform: C.(index: IntArray, T) -> T): N /** - * Combine two structures into one + * Combines two structures into one. */ public fun combine(a: N, b: N, transform: C.(T, T) -> T): N /** - * Check if given elements are consistent with this context + * Checks if given element is consistent with this context. + * + * @param element the structure to check. + * @return the valid structure. */ - public fun check(vararg elements: N): Unit = elements.forEach { - if (!shape.contentEquals(it.shape)) throw ShapeMismatchException(shape, it.shape) + public fun check(element: N): N { + if (!element.shape.contentEquals(shape)) throw ShapeMismatchException(shape, element.shape) + return element } /** - * element-by-element invoke a function working on [T] on a [NDStructure] + * Checks if given elements are consistent with this context. + * + * @param elements the structures to check. + * @return the array of valid structures. + */ + public fun check(vararg elements: N): Array = elements + .map(NDStructure::shape) + .singleOrNull { !shape.contentEquals(it) } + ?.let { throw ShapeMismatchException(shape, it) } + ?: elements + + /** + * Element-wise invocation of function working on [T] on a [NDStructure]. */ public operator fun Function1.invoke(structure: N): N = map(structure) { value -> this@invoke(value) } @@ -56,42 +84,107 @@ public interface NDAlgebra> { } /** - * An nd-space over element space + * Space of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param S the type of space of structure elements. */ public interface NDSpace, N : NDStructure> : Space, NDAlgebra { /** - * Element-by-element addition + * Element-wise addition. + * + * @param a the addend. + * @param b the augend. + * @return the sum. */ - override fun add(a: N, b: N): N = combine(a, b) { aValue, bValue -> add(aValue, bValue) } + public override fun add(a: N, b: N): N = combine(a, b) { aValue, bValue -> add(aValue, bValue) } /** - * Multiply all elements by constant + * Element-wise multiplication by scalar. + * + * @param a the multiplicand. + * @param k the multiplier. + * @return the product. */ - override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) } + public override fun multiply(a: N, k: Number): N = map(a) { multiply(it, k) } - //TODO move to extensions after KEEP-176 + // TODO move to extensions after KEEP-176 + + /** + * Adds an ND structure to an element of it. + * + * @receiver the addend. + * @param arg the augend. + * @return the sum. + */ public operator fun N.plus(arg: T): N = map(this) { value -> add(arg, value) } + /** + * Subtracts an element from ND structure of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ public operator fun N.minus(arg: T): N = map(this) { value -> add(arg, -value) } + /** + * Adds an element to ND structure of it. + * + * @receiver the addend. + * @param arg the augend. + * @return the sum. + */ public operator fun T.plus(arg: N): N = map(arg) { value -> add(this@plus, value) } + + /** + * Subtracts an ND structure from an element of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ public operator fun T.minus(arg: N): N = map(arg) { value -> add(-this@minus, value) } public companion object } /** - * An nd-ring over element ring + * Ring of [NDStructure]. + * + * @param T the type of the element contained in ND structure. + * @param N the type of ND structure. + * @param R the type of ring of structure elements. */ public interface NDRing, N : NDStructure> : Ring, NDSpace { /** - * Element-by-element multiplication + * Element-wise multiplication. + * + * @param a the multiplicand. + * @param b the multiplier. + * @return the product. */ - override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } + public override fun multiply(a: N, b: N): N = combine(a, b) { aValue, bValue -> multiply(aValue, bValue) } //TODO move to extensions after KEEP-176 + + /** + * Multiplies an ND structure by an element of it. + * + * @receiver the multiplicand. + * @param arg the multiplier. + * @return the product. + */ public operator fun N.times(arg: T): N = map(this) { value -> multiply(arg, value) } + /** + * Multiplies an element by a ND structure of it. + * + * @receiver the multiplicand. + * @param arg the multiplier. + * @return the product. + */ public operator fun T.times(arg: N): N = map(arg) { value -> multiply(this@times, value) } public companion object @@ -102,29 +195,47 @@ public interface NDRing, N : NDStructure> : Ring, NDSpace, N : NDStructure> : Field, NDRing { /** - * Element-by-element division + * Element-wise division. + * + * @param a the dividend. + * @param b the divisor. + * @return the quotient. */ - override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) } + public override fun divide(a: N, b: N): N = combine(a, b) { aValue, bValue -> divide(aValue, bValue) } //TODO move to extensions after KEEP-176 + /** + * Divides an ND structure by an element of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ public operator fun N.div(arg: T): N = map(this) { value -> divide(arg, value) } + /** + * Divides an element by an ND structure of it. + * + * @receiver the dividend. + * @param arg the divisor. + * @return the quotient. + */ public operator fun T.div(arg: N): N = map(arg) { divide(it, this@div) } public companion object { private val realNDFieldCache: MutableMap = hashMapOf() /** - * Create a nd-field for [Double] values or pull it from cache if it was created previously + * Create a nd-field for [Double] values or pull it from cache if it was created previously. */ public fun real(vararg shape: Int): RealNDField = realNDFieldCache.getOrPut(shape) { RealNDField(shape) } /** - * Create a nd-field with boxing generic buffer + * Create an ND field with boxing generic buffer. */ public fun > boxing( field: F, diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt index 4ab49c9bc..b8c8c855b 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/structures/NDStructure.kt @@ -37,9 +37,8 @@ public interface NDStructure { */ public fun elements(): Sequence> - override fun equals(other: Any?): Boolean - - override fun hashCode(): Int + public override fun equals(other: Any?): Boolean + public override fun hashCode(): Int public companion object { /** @@ -49,13 +48,8 @@ public interface NDStructure { if (st1 === st2) return true // fast comparison of buffers if possible - if ( - st1 is NDBuffer && - st2 is NDBuffer && - st1.strides == st2.strides - ) { + if (st1 is NDBuffer && st2 is NDBuffer && st1.strides == st2.strides) return st1.buffer.contentEquals(st2.buffer) - } //element by element comparison if it could not be avoided return st1.elements().all { (index, value) -> value == st2[index] } diff --git a/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt index f49e1e0f0..9450f9174 100644 --- a/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt +++ b/kmath-dimensions/src/commonMain/kotlin/kscience/kmath/dimensions/Dimensions.kt @@ -3,8 +3,9 @@ package kscience.kmath.dimensions import kotlin.reflect.KClass /** - * An abstract class which is not used in runtime. Designates a size of some structure. - * Could be replaced later by fully inline constructs + * Represents a quantity of dimensions in certain structure. + * + * @property dim The number of dimensions. */ public interface Dimension { public val dim: UInt @@ -16,18 +17,33 @@ public fun KClass.dim(): UInt = Dimension.resolve(this).dim public expect fun Dimension.Companion.resolve(type: KClass): D +/** + * Finds or creates [Dimension] with [Dimension.dim] equal to [dim]. + */ public expect fun Dimension.Companion.of(dim: UInt): Dimension +/** + * Finds [Dimension.dim] of given type [D]. + */ public inline fun Dimension.Companion.dim(): UInt = D::class.dim() +/** + * Type representing 1 dimension. + */ public object D1 : Dimension { override val dim: UInt get() = 1U } +/** + * Type representing 2 dimensions. + */ public object D2 : Dimension { override val dim: UInt get() = 2U } +/** + * Type representing 3 dimensions. + */ public object D3 : Dimension { override val dim: UInt get() = 3U } diff --git a/kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt b/kmath-dimensions/src/commonTest/kotlin/kscience/dimensions/DMatrixContextTest.kt similarity index 100% rename from kmath-dimensions/src/commonTest/kotlin/scientifik/dimensions/DMatrixContextTest.kt rename to kmath-dimensions/src/commonTest/kotlin/kscience/dimensions/DMatrixContextTest.kt diff --git a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt index 98300dada..370a01215 100644 --- a/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/kscience/kmath/histogram/Histogram.kt @@ -10,9 +10,10 @@ import kscience.kmath.structures.RealBuffer */ public interface Bin : Domain { /** - * The value of this bin + * The value of this bin. */ public val value: Number + public val center: Point } diff --git a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt index 0d95d6f97..2dd4ce51e 100644 --- a/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt +++ b/kmath-prob/src/commonMain/kotlin/kscience/kmath/prob/RandomGenerator.kt @@ -3,16 +3,59 @@ package kscience.kmath.prob import kotlin.random.Random /** - * A basic generator + * An interface that is implemented by random number generator algorithms. */ public interface RandomGenerator { + /** + * Gets the next random [Boolean] value. + */ public fun nextBoolean(): Boolean + + /** + * Gets the next random [Double] value uniformly distributed between 0 (inclusive) and 1 (exclusive). + */ public fun nextDouble(): Double + + /** + * Gets the next random `Int` from the random number generator. + * + * Generates an `Int` random value uniformly distributed between [Int.MIN_VALUE] and [Int.MAX_VALUE] (inclusive). + */ public fun nextInt(): Int + + /** + * Gets the next random non-negative `Int` from the random number generator less than the specified [until] bound. + * + * Generates an `Int` random value uniformly distributed between `0` (inclusive) and the specified [until] bound + * (exclusive). + */ public fun nextInt(until: Int): Int + + /** + * Gets the next random `Long` from the random number generator. + * + * Generates a `Long` random value uniformly distributed between [Long.MIN_VALUE] and [Long.MAX_VALUE] (inclusive). + */ public fun nextLong(): Long + + /** + * Gets the next random non-negative `Long` from the random number generator less than the specified [until] bound. + * + * Generates a `Long` random value uniformly distributed between `0` (inclusive) and the specified [until] bound (exclusive). + */ public fun nextLong(until: Long): Long + + /** + * Fills a subrange of the specified byte [array] starting from [fromIndex] inclusive and ending [toIndex] exclusive + * with random bytes. + * + * @return [array] with the subrange filled with random bytes. + */ public fun fillBytes(array: ByteArray, fromIndex: Int = 0, toIndex: Int = array.size) + + /** + * Creates a byte array of the specified [size], filled with random bytes. + */ public fun nextBytes(size: Int): ByteArray = ByteArray(size).also { fillBytes(it) } /** @@ -25,12 +68,21 @@ public interface RandomGenerator { public fun fork(): RandomGenerator public companion object { - public val default: DefaultGenerator by lazy { DefaultGenerator() } + /** + * The [DefaultGenerator] instance. + */ + public val default: DefaultGenerator by lazy(::DefaultGenerator) + /** + * Returns [DefaultGenerator] of given [seed]. + */ public fun default(seed: Long): DefaultGenerator = DefaultGenerator(Random(seed)) } } +/** + * Implements [RandomGenerator] by delegating all operations to [Random]. + */ public inline class DefaultGenerator(public val random: Random = Random) : RandomGenerator { public override fun nextBoolean(): Boolean = random.nextBoolean() public override fun nextDouble(): Double = random.nextDouble()