Even more documentation comments and minor refactorings #144

Merged
CommanderTvis merged 5 commits from even-more-docs into dev 2020-10-01 20:44:30 +03:00
38 changed files with 421 additions and 191 deletions

View File

@ -2,9 +2,9 @@ plugins {
id("ru.mipt.npm.project")
}
val kmathVersion by extra("0.2.0-dev-2")
val bintrayRepo by extra("kscience")
val githubProject by extra("kmath")
val kmathVersion: String by extra("0.2.0-dev-2")
val bintrayRepo: String by extra("kscience")
val githubProject: String by extra("kmath")
allprojects {
repositories {
@ -22,6 +22,6 @@ subprojects {
if (name.startsWith("kmath")) apply<ru.mipt.npm.gradle.KSciencePublishPlugin>()
}
readme{
readme {
readmeTemplate = file("docs/templates/README-TEMPLATE.md")
}

View File

@ -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

View File

@ -28,7 +28,9 @@ dependencies {
implementation(project(":kmath-dimensions"))
implementation("org.jetbrains.kotlinx:kotlinx-io:0.2.0-npm-dev-11")
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

View File

@ -6,34 +6,33 @@ import org.openjdk.jmh.annotations.State
import java.nio.IntBuffer
@State(Scope.Benchmark)
class ArrayBenchmark {
internal class ArrayBenchmark {
@Benchmark
fun benchmarkArrayRead() {
var res = 0
for (i in 1.._root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) res += _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.array[_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size - i]
for (i in 1..size) res += array[size - i]
}
@Benchmark
fun benchmarkBufferRead() {
var res = 0
for (i in 1.._root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) res += _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.arrayBuffer.get(
_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size - i)
for (i in 1..size) res += arrayBuffer.get(
size - i
)
}
@Benchmark
fun nativeBufferRead() {
var res = 0
for (i in 1.._root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) res += _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.nativeBuffer.get(
_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size - i)
for (i in 1..size) res += nativeBuffer.get(
size - i
)
}
companion object {
const val size: Int = 1000
val array: IntArray = IntArray(_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) { it }
val arrayBuffer: IntBuffer = IntBuffer.wrap(_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.array)
val nativeBuffer: IntBuffer = IntBuffer.allocate(_root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size).also {
for (i in 0 until _root_ide_package_.kscience.kmath.structures.ArrayBenchmark.Companion.size) it.put(i, i)
}
val array: IntArray = IntArray(size) { it }
val arrayBuffer: IntBuffer = IntBuffer.wrap(array)
val nativeBuffer: IntBuffer = IntBuffer.allocate(size).also { for (i in 0 until size) it.put(i, i) }
}
}

View File

@ -7,11 +7,10 @@ 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()}
val buffer = RealBuffer(size) { it.toDouble() }
(0 until size).forEach {
buffer[it]
@ -20,7 +19,7 @@ class BufferBenchmark {
@Benchmark
fun complexBufferReadWrite() {
val buffer = MutableBuffer.complex(size / 2){Complex(it.toDouble(), -it.toDouble())}
val buffer = MutableBuffer.complex(size / 2) { Complex(it.toDouble(), -it.toDouble()) }
(0 until size / 2).forEach {
buffer[it]
@ -28,6 +27,6 @@ class BufferBenchmark {
}
companion object {
const val size = 100
const val size: Int = 100
}
}

View File

@ -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 {
@ -40,11 +40,10 @@ class NDFieldBenchmark {
}
companion object {
val dim = 1000
val n = 100
val bufferedField = NDField.auto(RealField, dim, dim)
val specializedField = NDField.real(dim, dim)
val genericField = NDField.boxing(RealField, dim, dim)
const val dim: Int = 1000
const val n: Int = 100
val bufferedField: BufferedNDField<Double, RealField> = NDField.auto(RealField, dim, dim)
val specializedField: RealNDField = NDField.real(dim, dim)
val genericField: BoxingNDField<Double, RealField> = NDField.boxing(RealField, dim, dim)
}
}

View File

@ -9,9 +9,9 @@ import org.openjdk.jmh.annotations.Scope
import org.openjdk.jmh.annotations.State
@State(Scope.Benchmark)
class ViktorBenchmark {
final val dim = 1000
final val n = 100
internal class ViktorBenchmark {
final val dim: Int = 1000
final val n: Int = 100
// automatically build context most suited for given type.
final val autoField: BufferedNDField<Double, RealField> = NDField.auto(RealField, dim, dim)
@ -42,7 +42,7 @@ class ViktorBenchmark {
}
@Benchmark
fun realdFieldLog() {
fun realFieldLog() {
realField {
val fortyTwo = produce { 42.0 }
var res = one

View File

@ -1,4 +1,4 @@
//package kscience.kmath.ast
package kscience.kmath.ast
//
//import kscience.kmath.asm.compile
//import kscience.kmath.expressions.Expression

View File

@ -10,17 +10,14 @@ import org.apache.commons.rng.simple.RandomSource
import java.time.Duration
import java.time.Instant
private suspend fun runChain(): Duration {
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) {
@ -29,6 +26,7 @@ private suspend fun runChain(): Duration {
println("Chain sampler completed $counter elements in $duration: $meanValue")
}
}
return Duration.between(startTime, Instant.now())
}
@ -36,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) {
@ -48,6 +45,7 @@ private fun runDirect(): Duration {
println("Direct sampler completed $counter elements in $duration: $meanValue")
}
}
return Duration.between(startTime, Instant.now())
}
@ -56,16 +54,9 @@ 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()}")
}
}
}

View File

@ -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<Double>.mean(): Chain<Double> = collectWithState(AveragingChainState(), { it.copy() }) { chain ->
private fun Chain<Double>.mean(): Chain<Double> = collectWithState(AveragingChainState(), { it.copy() }) { chain ->
val next = chain.next()
num++
value += next

View File

@ -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")
}

View File

@ -4,32 +4,30 @@ 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 {
var res: Double = 0.0
var res = 0.0
strides.indices().forEach { res = structure[it] }
} // warmup
val time1 = measureTimeMillis {
var res: Double = 0.0
var res = 0.0
strides.indices().forEach { res = structure[it] }
}
println("Structure reading finished in $time1 millis")
val time2 = measureTimeMillis {
var res: Double = 0.0
var res = 0.0
strides.indices().forEach { res = buffer[strides.offset(it)] }
}
println("Buffer reading finished in $time2 millis")
val time3 = measureTimeMillis {
var res: Double = 0.0
var res = 0.0
strides.indices().forEach { res = array[strides.offset(it)] }
}
println("Array reading finished in $time3 millis")

View File

@ -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 })

View File

@ -6,7 +6,7 @@ import kscience.kmath.dimensions.DMatrixContext
import kscience.kmath.dimensions.Dimension
import kscience.kmath.operations.RealField
fun DMatrixContext<Double, RealField>.simple() {
private fun DMatrixContext<Double, RealField>.simple() {
val m1 = produce<D2, D3> { i, j -> (i + j).toDouble() }
val m2 = produce<D3, D2> { i, j -> (i + j).toDouble() }
@ -14,12 +14,11 @@ fun DMatrixContext<Double, RealField>.simple() {
m1.transpose() + m2
}
object D5 : Dimension {
private object D5 : Dimension {
override val dim: UInt = 5u
}
fun DMatrixContext<Double, RealField>.custom() {
private fun DMatrixContext<Double, RealField>.custom() {
val m1 = produce<D2, D5> { i, j -> (i + j).toDouble() }
val m2 = produce<D5, D2> { i, j -> (i - j).toDouble() }
val m3 = produce<D2, D2> { i, j -> (i - j).toDouble() }

View File

@ -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<String, Double>
) : ExtendedField<DerivativeStructure> {
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<String, DerivativeStructure> = parameters.mapValues { (key, value) ->
DerivativeStructure(parameters.size, order, parameters.keys.indexOf(key), value)

View File

@ -4,7 +4,7 @@ import kscience.kmath.operations.*
internal class FunctionalUnaryOperation<T>(val context: Algebra<T>, val name: String, private val expr: Expression<T>) :
Expression<T> {
public override operator fun invoke(arguments: Map<String, T>): T =
override operator fun invoke(arguments: Map<String, T>): T =
context.unaryOperation(name, expr.invoke(arguments))
}
@ -14,17 +14,17 @@ internal class FunctionalBinaryOperation<T>(
val first: Expression<T>,
val second: Expression<T>
) : Expression<T> {
public override operator fun invoke(arguments: Map<String, T>): T =
override operator fun invoke(arguments: Map<String, T>): T =
context.binaryOperation(name, first.invoke(arguments), second.invoke(arguments))
}
internal class FunctionalVariableExpression<T>(val name: String, val default: T? = null) : Expression<T> {
public override operator fun invoke(arguments: Map<String, T>): T =
override operator fun invoke(arguments: Map<String, T>): T =
arguments[name] ?: default ?: error("Parameter not found: $name")
}
internal class FunctionalConstantExpression<T>(val value: T) : Expression<T> {
public override operator fun invoke(arguments: Map<String, T>): T = value
override operator fun invoke(arguments: Map<String, T>): T = value
}
internal class FunctionalConstProductExpression<T>(
@ -32,7 +32,7 @@ internal class FunctionalConstProductExpression<T>(
private val expr: Expression<T>,
val const: Number
) : Expression<T> {
public override operator fun invoke(arguments: Map<String, T>): T = context.multiply(expr.invoke(arguments), const)
override operator fun invoke(arguments: Map<String, T>): T = context.multiply(expr.invoke(arguments), const)
}
/**
@ -139,16 +139,27 @@ public open class FunctionalExpressionField<T, A>(algebra: A) :
public open class FunctionalExpressionExtendedField<T, A>(algebra: A) :
FunctionalExpressionField<T, A>(algebra),
ExtendedField<Expression<T>> where A : ExtendedField<T>, A : NumericAlgebra<T> {
public override fun sin(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.SIN_OPERATION, arg)
public override fun cos(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.COS_OPERATION, arg)
public override fun asin(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg)
public override fun acos(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg)
public override fun atan(arg: Expression<T>): Expression<T> = unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg)
public override fun sin(arg: Expression<T>): Expression<T> =
unaryOperation(TrigonometricOperations.SIN_OPERATION, arg)
public override fun cos(arg: Expression<T>): Expression<T> =
unaryOperation(TrigonometricOperations.COS_OPERATION, arg)
public override fun asin(arg: Expression<T>): Expression<T> =
unaryOperation(TrigonometricOperations.ASIN_OPERATION, arg)
public override fun acos(arg: Expression<T>): Expression<T> =
unaryOperation(TrigonometricOperations.ACOS_OPERATION, arg)
public override fun atan(arg: Expression<T>): Expression<T> =
unaryOperation(TrigonometricOperations.ATAN_OPERATION, arg)
public override fun power(arg: Expression<T>, pow: Number): Expression<T> =
binaryOperation(PowerOperations.POW_OPERATION, arg, number(pow))
public override fun exp(arg: Expression<T>): Expression<T> = unaryOperation(ExponentialOperations.EXP_OPERATION, arg)
public override fun exp(arg: Expression<T>): Expression<T> =
unaryOperation(ExponentialOperations.EXP_OPERATION, arg)
public override fun ln(arg: Expression<T>): Expression<T> = unaryOperation(ExponentialOperations.LN_OPERATION, arg)
public override fun unaryOperation(operation: String, arg: Expression<T>): Expression<T> =

View File

@ -24,7 +24,11 @@ public interface FeaturedMatrix<T : Any> : Matrix<T> {
public companion object
}
public inline fun Structure2D.Companion.real(rows: Int, columns: Int, initializer: (Int, Int) -> Double): Matrix<Double> =
public inline fun Structure2D.Companion.real(
rows: Int,
columns: Int,
initializer: (Int, Int) -> Double
): Matrix<Double> =
MatrixContext.real.produce(rows, columns, initializer)
/**

View File

@ -1,10 +1,7 @@
package kscience.kmath.misc
import kscience.kmath.linear.Point
import kscience.kmath.operations.ExtendedField
import kscience.kmath.operations.Field
import kscience.kmath.operations.invoke
import kscience.kmath.operations.sum
import kscience.kmath.operations.*
import kscience.kmath.structures.asBuffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
@ -17,23 +14,37 @@ import kotlin.contracts.contract
/**
* Differentiable variable with value and derivative of differentiation ([deriv]) result
* with respect to this variable.
*
* @param T the non-nullable type of value.
* @property value The value of this variable.
*/
public open class Variable<T : Any>(public val value: T)
/**
* Represents result of [deriv] call.
*
* @param T the non-nullable type of value.
* @param value the value of result.
* @property deriv The mapping of differentiated variables to their derivatives.
* @property context The field over [T].
*/
public class DerivationResult<T : Any>(
value: T,
public val deriv: Map<Variable<T>, T>,
public val context: Field<T>
) : Variable<T>(value) {
/**
* Returns derivative of [variable] or returns [Ring.zero] in [context].
*/
public fun deriv(variable: Variable<T>): T = deriv[variable] ?: context.zero
/**
* compute divergence
* Computes the divergence.
*/
public fun div(): T = context { sum(deriv.values) }
/**
* Compute a gradient for variables in given order
* Computes the gradient for variables in given order.
*/
public fun grad(vararg variables: Variable<T>): Point<T> {
check(variables.isNotEmpty()) { "Variable order is not provided for gradient construction" }
@ -53,6 +64,9 @@ public class DerivationResult<T : Any>(
* assertEquals(17.0, y.x) // the value of result (y)
* assertEquals(9.0, x.d) // dy/dx
* ```
*
* @param body the action in [AutoDiffField] context returning [Variable] to differentiate with respect to.
* @return the result of differentiation.
*/
public inline fun <T : Any, F : Field<T>> F.deriv(body: AutoDiffField<T, F>.() -> Variable<T>): DerivationResult<T> {
contract { callsInPlace(body, InvocationKind.EXACTLY_ONCE) }
@ -65,12 +79,15 @@ public inline fun <T : Any, F : Field<T>> F.deriv(body: AutoDiffField<T, F>.() -
}
}
/**
* Represents field in context of which functions can be derived.
*/
public abstract class AutoDiffField<T : Any, F : Field<T>> : Field<Variable<T>> {
public abstract val context: F
/**
* A variable accessing inner state of derivatives.
* Use this function in inner builders to avoid creating additional derivative bindings
* Use this value in inner builders to avoid creating additional derivative bindings.
*/
public abstract var Variable<T>.d: T
@ -87,6 +104,9 @@ public abstract class AutoDiffField<T : Any, F : Field<T>> : Field<Variable<T>>
*/
public abstract fun <R> derive(value: R, block: F.(R) -> Unit): R
/**
*
*/
public abstract fun variable(value: T): Variable<T>
public inline fun variable(block: F.() -> T): Variable<T> = variable(context.block())

View File

@ -299,7 +299,7 @@ public class BigInt internal constructor(
for (i in mag.indices) {
val cur: ULong = carry + mag[i].toULong() * x.toULong()
result[i] = (cur and BASE.toULong()).toUInt()
result[i] = (cur and BASE).toUInt()
carry = cur shr BASE_SIZE
}
result[resultLength - 1] = (carry and BASE).toUInt()
@ -316,7 +316,7 @@ public class BigInt internal constructor(
for (j in mag2.indices) {
val cur: ULong = result[i + j].toULong() + mag1[i].toULong() * mag2[j].toULong() + carry
result[i + j] = (cur and BASE.toULong()).toUInt()
result[i + j] = (cur and BASE).toUInt()
carry = cur shr BASE_SIZE
}

View File

@ -6,6 +6,7 @@ import kscience.kmath.memory.MemoryWriter
import kscience.kmath.structures.Buffer
import kscience.kmath.structures.MemoryBuffer
import kscience.kmath.structures.MutableBuffer
import kscience.kmath.structures.MutableMemoryBuffer
import kotlin.math.*
/**
@ -159,7 +160,7 @@ public object ComplexField : ExtendedField<Complex>, Norm<Complex, Complex> {
}
/**
* Represents complex number.
* Represents `double`-based complex number.
*
* @property re The real part.
* @property im The imaginary part.
@ -182,10 +183,10 @@ public data class Complex(val re: Double, val im: Double) : FieldElement<Complex
public companion object : MemorySpec<Complex> {
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)
@ -202,8 +203,16 @@ public data class Complex(val re: Double, val im: Double) : FieldElement<Complex
*/
public fun Number.toComplex(): Complex = Complex(this, 0.0)
/**
* 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 Buffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> =
MemoryBuffer.create(Complex, size, init)
public inline fun MutableBuffer.Companion.complex(size: Int, init: (Int) -> Complex): Buffer<Complex> =
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<Complex> =
MutableMemoryBuffer.create(Complex, size, init)

View File

@ -15,8 +15,9 @@ public class BoxingNDField<T, F : Field<T>>(
public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> =
bufferFactory(size, initializer)
public override fun check(vararg elements: NDBuffer<T>) {
check(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" }
public override fun check(vararg elements: NDBuffer<T>): Array<out NDBuffer<T>> {
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<T, F> =
@ -75,6 +76,6 @@ public inline fun <T : Any, F : Field<T>, R> F.nd(
vararg shape: Int,
action: NDField<T, F, *>.() -> R
): R {
val ndfield: BoxingNDField<T, F> = NDField.boxing(this, *shape, bufferFactory = bufferFactory)
val ndfield = NDField.boxing(this, *shape, bufferFactory = bufferFactory)
return ndfield.action()
}

View File

@ -14,8 +14,9 @@ public class BoxingNDRing<T, R : Ring<T>>(
public fun buildBuffer(size: Int, initializer: (Int) -> T): Buffer<T> = bufferFactory(size, initializer)
override fun check(vararg elements: NDBuffer<T>) {
require(elements.all { it.strides == strides }) { "Element strides are not the same as context strides" }
override fun check(vararg elements: NDBuffer<T>): Array<out NDBuffer<T>> {
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<T, R> =

View File

@ -5,8 +5,10 @@ import kscience.kmath.operations.*
public interface BufferedNDAlgebra<T, C> : NDAlgebra<T, C, NDBuffer<T>> {
public val strides: Strides
public override fun check(vararg elements: NDBuffer<T>): Unit =
require(elements.all { it.strides == strides }) { ("Strides mismatch") }
public override fun check(vararg elements: NDBuffer<T>): Array<out NDBuffer<T>> {
require(elements.all { it.strides == strides }) { "Strides mismatch" }
return elements
}
/**
* Convert any [NDStructure] to buffered structure using strides from this context.

View File

@ -46,35 +46,48 @@ public interface Buffer<T> {
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 <T> boxing(size: Int, initializer: (Int) -> T): Buffer<T> =
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 <T : Any> auto(type: KClass<T>, size: Int, crossinline initializer: (Int) -> T): Buffer<T> {
//TODO add resolution based on Annotation or companion resolution
return when (type) {
Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as Buffer<T>
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as Buffer<T>
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as Buffer<T>
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as Buffer<T>
public inline fun <T : Any> auto(type: KClass<T>, size: Int, initializer: (Int) -> T): Buffer<T> =
when (type) {
Double::class -> RealBuffer(size) { initializer(it) as Double } as Buffer<T>
Short::class -> ShortBuffer(size) { initializer(it) as Short } as Buffer<T>
Int::class -> IntBuffer(size) { initializer(it) as Int } as Buffer<T>
Long::class -> LongBuffer(size) { initializer(it) as Long } as Buffer<T>
Float::class -> FloatBuffer(size) { initializer(it) as Float } as Buffer<T>
Complex::class -> complex(size) { initializer(it) as Complex } as Buffer<T>
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 <reified T : Any> auto(size: Int, crossinline initializer: (Int) -> T): Buffer<T> =
public inline fun <reified T : Any> auto(size: Int, initializer: (Int) -> T): Buffer<T> =
auto(T::class, size, initializer)
}
}
@ -117,25 +130,40 @@ public interface MutableBuffer<T> : Buffer<T> {
public inline fun <T> boxing(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
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 <T : Any> auto(type: KClass<out T>, size: Int, initializer: (Int) -> T): MutableBuffer<T> =
when (type) {
Double::class -> RealBuffer(DoubleArray(size) { initializer(it) as Double }) as MutableBuffer<T>
Short::class -> ShortBuffer(ShortArray(size) { initializer(it) as Short }) as MutableBuffer<T>
Int::class -> IntBuffer(IntArray(size) { initializer(it) as Int }) as MutableBuffer<T>
Long::class -> LongBuffer(LongArray(size) { initializer(it) as Long }) as MutableBuffer<T>
Double::class -> RealBuffer(size) { initializer(it) as Double } as MutableBuffer<T>
Short::class -> ShortBuffer(size) { initializer(it) as Short } as MutableBuffer<T>
Int::class -> IntBuffer(size) { initializer(it) as Int } as MutableBuffer<T>
Float::class -> FloatBuffer(size) { initializer(it) as Float } as MutableBuffer<T>
Long::class -> LongBuffer(size) { initializer(it) as Long } as MutableBuffer<T>
Complex::class -> complex(size) { initializer(it) as Complex } as MutableBuffer<T>
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 <reified T : Any> auto(size: Int, initializer: (Int) -> T): MutableBuffer<T> =
auto(T::class, size, initializer)
public val real: MutableBufferFactory<Double> =
{ 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) }
}
}

View File

@ -48,7 +48,8 @@ public fun FlaggedBuffer<*>.isMissing(index: Int): Boolean = hasFlag(index, Valu
/**
* A real buffer which supports flags for each value like NaN or Missing
*/
public class FlaggedRealBuffer(public val values: DoubleArray, public val flags: ByteArray) : FlaggedBuffer<Double?>, Buffer<Double?> {
public class FlaggedRealBuffer(public val values: DoubleArray, public val flags: ByteArray) : FlaggedBuffer<Double?>,
Buffer<Double?> {
init {
require(values.size == flags.size) { "Values and flags must have the same dimensions" }
}

View File

@ -53,7 +53,7 @@ public class MutableMemoryBuffer<T : Any>(memory: Memory, spec: MemorySpec<T>) :
public inline fun <T : Any> create(
spec: MemorySpec<T>,
size: Int,
crossinline initializer: (Int) -> T
initializer: (Int) -> T
): MutableMemoryBuffer<T> = MutableMemoryBuffer(Memory.allocate(size * spec.objectSize), spec).also { buffer ->
(0 until size).forEach { buffer[it] = initializer(it) }
}

View File

@ -7,49 +7,77 @@ import kscience.kmath.operations.Space
import kotlin.native.concurrent.ThreadLocal
/**
* 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<T, C, N : NDStructure<T>> {
/**
* 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<out N> = elements
.map(NDStructure<T>::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<T, T>.invoke(structure: N): N = map(structure) { value -> this@invoke(value) }
@ -57,42 +85,107 @@ public interface NDAlgebra<T, C, N : NDStructure<T>> {
}
/**
* 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<T, S : Space<T>, N : NDStructure<T>> : Space<N>, NDAlgebra<T, S, N> {
/**
* 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<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T, R, N> {
/**
* 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
@ -103,17 +196,35 @@ public interface NDRing<T, R : Ring<T>, N : NDStructure<T>> : Ring<N>, NDSpace<T
*
* @param T the type of the element contained in ND structure.
* @param N the type of ND structure.
* @param F field of structure elements.
* @param F the type field of structure elements.
*/
public interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F, N> {
/**
* 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) }
@ThreadLocal
@ -121,12 +232,12 @@ public interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing
private val realNDFieldCache: MutableMap<IntArray, RealNDField> = 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 <T : Any, F : Field<T>> boxing(
field: F,

View File

@ -38,9 +38,8 @@ public interface NDStructure<T> {
*/
public fun elements(): Sequence<Pair<IntArray, T>>
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 {
/**
@ -50,13 +49,8 @@ public interface NDStructure<T> {
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] }

View File

@ -17,7 +17,7 @@ public interface Structure1D<T> : NDStructure<T>, Buffer<T> {
/**
* A 1D wrapper for nd-structure
*/
private inline class Structure1DWrapper<T>(public val structure: NDStructure<T>) : Structure1D<T> {
private inline class Structure1DWrapper<T>(val structure: NDStructure<T>) : Structure1D<T> {
override val shape: IntArray get() = structure.shape
override val size: Int get() = structure.shape[0]

View File

@ -21,7 +21,8 @@ internal class LazyDeferred<T>(val dispatcher: CoroutineDispatcher, val block: s
}
public class AsyncFlow<T> internal constructor(internal val deferredFlow: Flow<LazyDeferred<T>>) : Flow<T> {
override suspend fun collect(collector: FlowCollector<T>): Unit = deferredFlow.collect { collector.emit((it.await())) }
override suspend fun collect(collector: FlowCollector<T>): Unit =
deferredFlow.collect { collector.emit((it.await())) }
}
public fun <T, R> Flow<T>.async(

View File

@ -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 <D : Dimension> KClass<D>.dim(): UInt = Dimension.resolve(this).dim
public expect fun <D : Dimension> Dimension.Companion.resolve(type: KClass<D>): 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 <reified D : Dimension> 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
}

View File

@ -4,7 +4,7 @@ import kscience.kmath.operations.Space
public interface Vector
public interface GeometrySpace<V: Vector>: Space<V> {
public interface GeometrySpace<V : Vector> : Space<V> {
/**
* L2 distance
*/

View File

@ -10,9 +10,10 @@ import kscience.kmath.structures.RealBuffer
*/
public interface Bin<T : Any> : Domain<T> {
/**
* The value of this bin
* The value of this bin.
*/
public val value: Number
public val center: Point<T>
}

View File

@ -5,10 +5,7 @@ import kscience.kmath.histogram.fill
import kscience.kmath.histogram.put
import kscience.kmath.real.RealVector
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlin.test.*
internal class MultivariateHistogramTest {
@Test
@ -18,7 +15,7 @@ internal class MultivariateHistogramTest {
(-1.0..1.0)
)
histogram.put(0.55, 0.55)
val bin = histogram.find { it.value.toInt() > 0 }!!
val bin = histogram.find { it.value.toInt() > 0 } ?: fail()
assertTrue { bin.contains(RealVector(0.55, 0.55)) }
assertTrue { bin.contains(RealVector(0.6, 0.5)) }
assertFalse { bin.contains(RealVector(-0.55, 0.55)) }

View File

@ -62,7 +62,7 @@ public fun <T : Any> Sampler<T>.sampleBuffer(
//clear list from previous run
tmp.clear()
//Fill list
repeat(size){
repeat(size) {
tmp.add(chain.next())
}
//return new buffer with elements from tmp

View File

@ -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()

View File

@ -6,7 +6,7 @@ import kotlin.test.Test
class SamplerTest {
@Test
fun bufferSamplerTest(){
fun bufferSamplerTest() {
val sampler: Sampler<Double> =
BasicSampler { it.chain { nextDouble() } }
val data = sampler.sampleBuffer(RandomGenerator.default, 100)