Add mandatory MutableBufferFactory to Algebra #477

This commit is contained in:
Alexander Nozik 2022-07-16 16:27:11 +03:00
parent 68add4cb5f
commit 3eef778f60
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
25 changed files with 187 additions and 180 deletions

View File

@ -3,6 +3,7 @@
## [Unreleased]
### Added
- Autodiff for generic algebra elements in core!
- Algebra now has an obligatory `bufferFactory` (#477).
### Changed
- Kotlin 1.7

View File

@ -16,7 +16,6 @@ import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.multik.multikAlgebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.tensorflow.produceWithTF
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.tensorAlgebra
@ -84,7 +83,7 @@ internal class DotBenchmark {
}
@Benchmark
fun bufferedDot(blackhole: Blackhole) = with(DoubleField.linearSpace(Buffer.Companion::auto)) {
fun bufferedDot(blackhole: Blackhole) = with(DoubleField.linearSpace) {
blackhole.consume(matrix1 dot matrix2)
}

View File

@ -20,7 +20,6 @@ import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd.one
import space.kscience.kmath.nd4j.nd4j
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.tensors.core.DoubleTensor
import space.kscience.kmath.tensors.core.one
import space.kscience.kmath.tensors.core.tensorAlgebra
@ -28,12 +27,6 @@ import space.kscience.kmath.viktor.viktorAlgebra
@State(Scope.Benchmark)
internal class NDFieldBenchmark {
@Benchmark
fun autoFieldAdd(blackhole: Blackhole) = with(autoField) {
var res: StructureND<Double> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
@Benchmark
fun specializedFieldAdd(blackhole: Blackhole) = with(specializedField) {
@ -95,9 +88,8 @@ internal class NDFieldBenchmark {
private const val dim = 1000
private const val n = 100
private val shape = intArrayOf(dim, dim)
private val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto)
private val specializedField = DoubleField.ndAlgebra
private val genericField = BufferedFieldOpsND(DoubleField, Buffer.Companion::boxing)
private val genericField = BufferedFieldOpsND(DoubleField)
private val nd4jField = DoubleField.nd4j
private val multikField = DoubleField.multikAlgebra
private val viktorField = DoubleField.viktorAlgebra

View File

@ -10,25 +10,19 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.*
import space.kscience.kmath.nd.Shape
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd.one
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.viktor.ViktorFieldND
@State(Scope.Benchmark)
internal class ViktorBenchmark {
@Benchmark
fun automaticFieldAddition(blackhole: Blackhole) {
with(autoField) {
var res: StructureND<Double> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
}
}
@Benchmark
fun realFieldAddition(blackhole: Blackhole) {
with(realField) {
fun doubleFieldAddition(blackhole: Blackhole) {
with(doubleField) {
var res: StructureND<Double> = one(shape)
repeat(n) { res += 1.0 }
blackhole.consume(res)
@ -58,8 +52,7 @@ internal class ViktorBenchmark {
private val shape = Shape(dim, dim)
// automatically build context most suited for given type.
private val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto)
private val realField = DoubleField.ndAlgebra
private val doubleField = DoubleField.ndAlgebra
private val viktorField = ViktorFieldND(dim, dim)
}
}

View File

@ -10,19 +10,17 @@ import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope
import kotlinx.benchmark.State
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.nd.BufferedFieldOpsND
import space.kscience.kmath.nd.Shape
import space.kscience.kmath.nd.ndAlgebra
import space.kscience.kmath.nd.one
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.viktor.ViktorFieldND
@State(Scope.Benchmark)
internal class ViktorLogBenchmark {
@Benchmark
fun realFieldLog(blackhole: Blackhole) {
with(realField) {
with(doubleField) {
val fortyTwo = structureND(shape) { 42.0 }
var res = one(shape)
repeat(n) { res = ln(fortyTwo) }
@ -54,8 +52,7 @@ internal class ViktorLogBenchmark {
private val shape = Shape(dim, dim)
// automatically build context most suited for given type.
private val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto)
private val realField = DoubleField.ndAlgebra
private val doubleField = DoubleField.ndAlgebra
private val viktorField = ViktorFieldND(dim, dim)
}
}

View File

@ -7,7 +7,6 @@ package space.kscience.kmath.operations
import space.kscience.kmath.complex.Complex
import space.kscience.kmath.complex.algebra
import space.kscience.kmath.complex.bufferAlgebra
import space.kscience.kmath.complex.ndAlgebra
import space.kscience.kmath.nd.BufferND
import space.kscience.kmath.nd.StructureND

View File

@ -32,12 +32,10 @@ fun main() {
val shape = Shape(dim, dim)
// automatically build context most suited for given type.
val autoField = BufferedFieldOpsND(DoubleField, Buffer.Companion::auto)
// specialized nd-field for Double. It works as generic Double field as well.
val realField = DoubleField.ndAlgebra
//A generic boxing field. It should be used for objects, not primitives.
val boxingField = BufferedFieldOpsND(DoubleField, Buffer.Companion::boxing)
val doubleField = DoubleField.ndAlgebra
//A generic field. It should be used for objects, not primitives.
val genericField = BufferedFieldOpsND(DoubleField)
// Nd4j specialized field.
val nd4jField = DoubleField.nd4j
//viktor field
@ -46,14 +44,14 @@ fun main() {
val parallelField = DoubleField.ndStreaming(dim, dim)
measureAndPrint("Boxing addition") {
boxingField {
genericField {
var res: StructureND<Double> = one(shape)
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Specialized addition") {
realField {
doubleField {
var res: StructureND<Double> = one(shape)
repeat(n) { res += 1.0 }
}
@ -80,15 +78,8 @@ fun main() {
}
}
measureAndPrint("Automatic field addition") {
autoField {
var res: StructureND<Double> = one(shape)
repeat(n) { res += 1.0 }
}
}
measureAndPrint("Lazy addition") {
val res = realField.one(shape).mapAsync(GlobalScope) {
val res = doubleField.one(shape).mapAsync(GlobalScope) {
var c = 0.0
repeat(n) {
c += 1.0

View File

@ -10,10 +10,7 @@ import space.kscience.kmath.memory.MemorySpec
import space.kscience.kmath.memory.MemoryWriter
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MemoryBuffer
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.MutableMemoryBuffer
import space.kscience.kmath.structures.*
import kotlin.math.*
/**
@ -54,6 +51,9 @@ public object ComplexField :
Norm<Complex, Complex>,
NumbersAddOps<Complex>,
ScaleOperations<Complex> {
override val bufferFactory: MutableBufferFactory<Complex> = MutableBufferFactory { size, init ->
MutableMemoryBuffer.create(Complex, size, init)
}
override val zero: Complex = 0.0.toComplex()
override val one: Complex = 1.0.toComplex()

View File

@ -56,11 +56,6 @@ public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField
public companion object : ComplexFieldOpsND()
}
@UnstableKMathAPI
public val ComplexField.bufferAlgebra: BufferFieldOps<Complex, ComplexField>
get() = bufferAlgebra(Buffer.Companion::complex)
@OptIn(UnstableKMathAPI::class)
public class ComplexFieldND(override val shape: Shape) :
ComplexFieldOpsND(), FieldND<Complex, ComplexField>,

View File

@ -78,9 +78,9 @@ public val <T, A : Ring<T>> DS<T, A>.value: T get() = data[0]
@UnstableKMathAPI
public abstract class DSAlgebra<T, A : Ring<T>>(
public val algebra: A,
public val bufferFactory: MutableBufferFactory<T>,
public val order: Int,
bindings: Map<Symbol, T>,
public val valueBufferFactory: MutableBufferFactory<T> = algebra.bufferFactory,
) : ExpressionAlgebra<T, DS<T, A>>, SymbolIndexer {
/**
@ -90,6 +90,7 @@ public abstract class DSAlgebra<T, A : Ring<T>>(
*/
@PublishedApi
internal val compiler: DSCompiler<T, A> by lazy {
val numberOfVariables = bindings.size
// get the cached compilers
val cache: Array<Array<DSCompiler<T, A>?>>? = null
@ -115,7 +116,7 @@ public abstract class DSAlgebra<T, A : Ring<T>>(
newCache[p][o] = DSCompiler(
algebra,
bufferFactory,
valueBufferFactory,
p,
o,
valueCompiler,
@ -139,16 +140,13 @@ public abstract class DSAlgebra<T, A : Ring<T>>(
}
override val symbols: List<Symbol> = bindings.map { it.key }
public val numberOfVariables: Int get() = symbols.size
private fun bufferForVariable(index: Int, value: T): Buffer<T> {
val buffer = bufferFactory(compiler.size) { algebra.zero }
val buffer = valueBufferFactory(compiler.size) { algebra.zero }
buffer[0] = value
if (compiler.order > 0) {
// the derivative of the variable with respect to itself is 1.
val indexOfDerivative = compiler.getPartialDerivativeIndex(*IntArray(numberOfVariables).apply {
val indexOfDerivative = compiler.getPartialDerivativeIndex(*IntArray(symbols.size).apply {
set(index, 1)
})
@ -209,7 +207,7 @@ public abstract class DSAlgebra<T, A : Ring<T>>(
}
public override fun const(value: T): DS<T, A> {
val buffer = bufferFactory(compiler.size) { algebra.zero }
val buffer = valueBufferFactory(compiler.size) { algebra.zero }
buffer[0] = value
return DS(buffer)
@ -245,10 +243,10 @@ public abstract class DSAlgebra<T, A : Ring<T>>(
@UnstableKMathAPI
public open class DSRing<T, A>(
algebra: A,
bufferFactory: MutableBufferFactory<T>,
order: Int,
bindings: Map<Symbol, T>,
) : DSAlgebra<T, A>(algebra, bufferFactory, order, bindings),
valueBufferFactory: MutableBufferFactory<T>,
) : DSAlgebra<T, A>(algebra, order, bindings, valueBufferFactory),
Ring<DS<T, A>>, ScaleOperations<DS<T, A>>,
NumericAlgebra<DS<T, A>>,
NumbersAddOps<DS<T, A>> where A : Ring<T>, A : NumericAlgebra<T>, A : ScaleOperations<T> {
@ -263,14 +261,14 @@ public open class DSRing<T, A>(
*/
protected inline fun DS<T, A>.transformDataBuffer(block: A.(MutableBuffer<T>) -> Unit): DS<T, A> {
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
val newData = bufferFactory(compiler.size) { data[it] }
val newData = valueBufferFactory(compiler.size) { data[it] }
algebra.block(newData)
return DS(newData)
}
protected fun DS<T, A>.mapData(block: A.(T) -> T): DS<T, A> {
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
val newData: Buffer<T> = data.map(bufferFactory) {
val newData: Buffer<T> = data.map(valueBufferFactory) {
algebra.block(it)
}
return DS(newData)
@ -278,7 +276,7 @@ public open class DSRing<T, A>(
protected fun DS<T, A>.mapDataIndexed(block: (Int, T) -> T): DS<T, A> {
require(derivativeAlgebra == this@DSRing) { "All derivative operations should be done in the same algebra" }
val newData: Buffer<T> = data.mapIndexed(bufferFactory, block)
val newData: Buffer<T> = data.mapIndexed(valueBufferFactory, block)
return DS(newData)
}
@ -327,19 +325,19 @@ public open class DSRing<T, A>(
@UnstableKMathAPI
public class DerivativeStructureRingExpression<T, A>(
public val algebra: A,
public val bufferFactory: MutableBufferFactory<T>,
public val elementBufferFactory: MutableBufferFactory<T> = algebra.bufferFactory,
public val function: DSRing<T, A>.() -> DS<T, A>,
) : DifferentiableExpression<T> where A : Ring<T>, A : ScaleOperations<T>, A : NumericAlgebra<T> {
override operator fun invoke(arguments: Map<Symbol, T>): T =
DSRing(algebra, bufferFactory, 0, arguments).function().value
DSRing(algebra, 0, arguments, elementBufferFactory).function().value
override fun derivativeOrNull(symbols: List<Symbol>): Expression<T> = Expression { arguments ->
with(
DSRing(
algebra,
bufferFactory,
symbols.size,
arguments
arguments,
elementBufferFactory
)
) { function().derivative(symbols) }
}
@ -354,10 +352,10 @@ public class DerivativeStructureRingExpression<T, A>(
@UnstableKMathAPI
public class DSField<T, A : ExtendedField<T>>(
algebra: A,
bufferFactory: MutableBufferFactory<T>,
order: Int,
bindings: Map<Symbol, T>,
) : DSRing<T, A>(algebra, bufferFactory, order, bindings), ExtendedField<DS<T, A>> {
valueBufferFactory: MutableBufferFactory<T>,
) : DSRing<T, A>(algebra, order, bindings, valueBufferFactory), ExtendedField<DS<T, A>> {
override fun number(value: Number): DS<T, A> = const(algebra.number(value))
override fun divide(left: DS<T, A>, right: DS<T, A>): DS<T, A> = left.transformDataBuffer { result ->
@ -441,18 +439,18 @@ public class DSField<T, A : ExtendedField<T>>(
@UnstableKMathAPI
public class DSFieldExpression<T, A : ExtendedField<T>>(
public val algebra: A,
public val bufferFactory: MutableBufferFactory<T>,
private val valueBufferFactory: MutableBufferFactory<T> = algebra.bufferFactory,
public val function: DSField<T, A>.() -> DS<T, A>,
) : DifferentiableExpression<T> {
override operator fun invoke(arguments: Map<Symbol, T>): T =
DSField(algebra, bufferFactory, 0, arguments).function().value
DSField(algebra, 0, arguments, valueBufferFactory).function().value
override fun derivativeOrNull(symbols: List<Symbol>): Expression<T> = Expression { arguments ->
DSField(
algebra,
bufferFactory,
symbols.size,
arguments,
valueBufferFactory,
).run { function().derivative(symbols) }
}
}

View File

@ -11,13 +11,12 @@ import space.kscience.kmath.nd.as2D
import space.kscience.kmath.nd.asND
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.VirtualBuffer
import space.kscience.kmath.structures.indices
public class BufferedLinearSpace<T, out A : Ring<T>>(
private val bufferAlgebra: BufferAlgebra<T, A>
private val bufferAlgebra: BufferAlgebra<T, A>,
) : LinearSpace<T, A> {
override val elementAlgebra: A get() = bufferAlgebra.elementAlgebra
@ -91,5 +90,5 @@ public class BufferedLinearSpace<T, out A : Ring<T>>(
}
public fun <T, A : Ring<T>> A.linearSpace(bufferFactory: BufferFactory<T>): BufferedLinearSpace<T, A> =
BufferedLinearSpace(BufferRingOps(this, bufferFactory))
public val <T, A : Ring<T>> A.linearSpace: BufferedLinearSpace<T, A>
get() = BufferedLinearSpace(BufferRingOps(this))

View File

@ -11,12 +11,9 @@ import space.kscience.kmath.nd.Structure2D
import space.kscience.kmath.nd.StructureFeature
import space.kscience.kmath.nd.as1D
import space.kscience.kmath.operations.BufferRingOps
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.Ring
import space.kscience.kmath.operations.invoke
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.reflect.KClass
/**
@ -187,18 +184,9 @@ public interface LinearSpace<T, out A : Ring<T>> {
* A structured matrix with custom buffer
*/
public fun <T : Any, A : Ring<T>> buffered(
algebra: A,
bufferFactory: BufferFactory<T> = BufferFactory(Buffer.Companion::boxing),
): LinearSpace<T, A> = BufferedLinearSpace(BufferRingOps(algebra, bufferFactory))
algebra: A
): LinearSpace<T, A> = BufferedLinearSpace(BufferRingOps(algebra))
@Deprecated("use DoubleField.linearSpace")
public val double: LinearSpace<Double, DoubleField> = buffered(DoubleField, ::DoubleBuffer)
/**
* Automatic buffered matrix, unboxed if it is possible
*/
public inline fun <reified T : Any, A : Ring<T>> auto(ring: A): LinearSpace<T, A> =
buffered(ring, Buffer.Companion::auto)
}
}

View File

@ -10,7 +10,6 @@ package space.kscience.kmath.nd
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.BufferFactory
public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
public val indexerBuilder: (IntArray) -> ShapeIndexer
@ -60,7 +59,7 @@ public inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapInline(
return BufferND(
indexes,
bufferAlgebra.run {
bufferFactory(buffer.size) { elementAlgebra.transform(buffer[it]) }
elementBufferFactory(buffer.size) { elementAlgebra.transform(buffer[it]) }
}
)
}
@ -74,7 +73,7 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapIndexedInline(
return BufferND(
indexes,
bufferAlgebra.run {
bufferFactory(buffer.size) { elementAlgebra.transform(indexes.index(it), buffer[it]) }
elementBufferFactory(buffer.size) { elementAlgebra.transform(indexes.index(it), buffer[it]) }
}
)
}
@ -91,7 +90,7 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
return BufferND(
indexes,
bufferAlgebra.run {
bufferFactory(lbuffer.size) { elementAlgebra.block(lbuffer[it], rbuffer[it]) }
elementBufferFactory(lbuffer.size) { elementAlgebra.block(lbuffer[it], rbuffer[it]) }
}
)
}
@ -116,9 +115,8 @@ public open class BufferedFieldOpsND<T, out A : Field<T>>(
public constructor(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : this(BufferFieldOps(elementAlgebra, bufferFactory), indexerBuilder)
) : this(BufferFieldOps(elementAlgebra), indexerBuilder)
@OptIn(PerformancePitfall::class)
override fun scale(a: StructureND<T>, value: Double): StructureND<T> = a.map { it * value }

View File

@ -120,7 +120,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
*/
public fun <T> buffered(
strides: Strides,
bufferFactory: BufferFactory<T> = BufferFactory(Buffer.Companion::boxing),
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
initializer: (IntArray) -> T,
): BufferND<T> = BufferND(strides, bufferFactory(strides.linearSize) { i -> initializer(strides.index(i)) })
@ -140,7 +140,7 @@ public interface StructureND<out T> : Featured<StructureFeature>, WithShape {
public fun <T> buffered(
shape: IntArray,
bufferFactory: BufferFactory<T> = BufferFactory(Buffer.Companion::boxing),
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
initializer: (IntArray) -> T,
): BufferND<T> = buffered(DefaultStrides(shape), bufferFactory, initializer)

View File

@ -8,12 +8,7 @@ package space.kscience.kmath.operations
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.Ring.Companion.optimizedPower
/**
* Stub for DSL the [Algebra] is.
*/
@DslMarker
public annotation class KMathContext
import space.kscience.kmath.structures.MutableBufferFactory
/**
* Represents an algebraic structure.
@ -21,6 +16,12 @@ public annotation class KMathContext
* @param T the type of element of this structure.
*/
public interface Algebra<T> {
/**
* Provide a factory for buffers, associated with this [Algebra]
*/
public val bufferFactory: MutableBufferFactory<T> get() = MutableBufferFactory.boxing()
/**
* Wraps a raw string to [T] object. This method is designed for three purposes:
*

View File

@ -10,7 +10,6 @@ import space.kscience.kmath.nd.BufferedRingOpsND
import space.kscience.kmath.operations.BigInt.Companion.BASE
import space.kscience.kmath.operations.BigInt.Companion.BASE_SIZE
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.MutableBuffer
import kotlin.math.log2
import kotlin.math.max
import kotlin.math.min
@ -528,19 +527,11 @@ public fun String.parseBigInteger(): BigInt? {
public val BigInt.algebra: BigIntField get() = BigIntField
@Deprecated("Use BigInt::buffer")
public inline fun Buffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): Buffer<BigInt> =
boxing(size, initializer)
public inline fun BigInt.Companion.buffer(size: Int, initializer: (Int) -> BigInt): Buffer<BigInt> =
Buffer.boxing(size, initializer)
@Deprecated("Use BigInt::mutableBuffer")
public inline fun MutableBuffer.Companion.bigInt(size: Int, initializer: (Int) -> BigInt): MutableBuffer<BigInt> =
boxing(size, initializer)
public inline fun BigInt.mutableBuffer(size: Int, initializer: (Int) -> BigInt): Buffer<BigInt> =
public inline fun BigInt.Companion.mutableBuffer(size: Int, initializer: (Int) -> BigInt): Buffer<BigInt> =
Buffer.boxing(size, initializer)
public val BigIntField.nd: BufferedRingOpsND<BigInt, BigIntField>
get() = BufferedRingOpsND(BufferRingOps(BigIntField, BigInt::buffer))
get() = BufferedRingOpsND(BufferRingOps(BigIntField))

View File

@ -7,8 +7,6 @@ package space.kscience.kmath.operations
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.ShortBuffer
public interface WithSize {
public val size: Int
@ -19,11 +17,11 @@ public interface WithSize {
*/
public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
public val elementAlgebra: A
public val bufferFactory: BufferFactory<T>
public val elementBufferFactory: BufferFactory<T> get() = elementAlgebra.bufferFactory
public fun buffer(size: Int, vararg elements: T): Buffer<T> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
return bufferFactory(size) { elements[it] }
return elementBufferFactory(size) { elements[it] }
}
//TODO move to multi-receiver inline extension
@ -36,13 +34,13 @@ public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
override fun unaryOperationFunction(operation: String): (arg: Buffer<T>) -> Buffer<T> {
val operationFunction = elementAlgebra.unaryOperationFunction(operation)
return { arg -> bufferFactory(arg.size) { operationFunction(arg[it]) } }
return { arg -> elementBufferFactory(arg.size) { operationFunction(arg[it]) } }
}
override fun binaryOperationFunction(operation: String): (left: Buffer<T>, right: Buffer<T>) -> Buffer<T> {
val operationFunction = elementAlgebra.binaryOperationFunction(operation)
return { left, right ->
bufferFactory(left.size) { operationFunction(left[it], right[it]) }
elementBufferFactory(left.size) { operationFunction(left[it], right[it]) }
}
}
}
@ -53,7 +51,7 @@ public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
buffer: Buffer<T>,
crossinline block: A.(T) -> T,
): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(buffer[it]) }
): Buffer<T> = elementBufferFactory(buffer.size) { elementAlgebra.block(buffer[it]) }
/**
* Inline map
@ -61,7 +59,7 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
buffer: Buffer<T>,
crossinline block: A.(index: Int, arg: T) -> T,
): Buffer<T> = bufferFactory(buffer.size) { elementAlgebra.block(it, buffer[it]) }
): Buffer<T> = elementBufferFactory(buffer.size) { elementAlgebra.block(it, buffer[it]) }
/**
* Inline zip
@ -72,15 +70,15 @@ private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline(
crossinline block: A.(l: T, r: T) -> T,
): Buffer<T> {
require(l.size == r.size) { "Incompatible buffer sizes. left: ${l.size}, right: ${r.size}" }
return bufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) }
return elementBufferFactory(l.size) { elementAlgebra.block(l[it], r[it]) }
}
public fun <T> BufferAlgebra<T, *>.buffer(size: Int, initializer: (Int) -> T): Buffer<T> {
return bufferFactory(size, initializer)
return elementBufferFactory(size, initializer)
}
public fun <T, A> A.buffer(initializer: (Int) -> T): Buffer<T> where A : BufferAlgebra<T, *>, A : WithSize {
return bufferFactory(size, initializer)
return elementBufferFactory(size, initializer)
}
public fun <T, A : TrigonometricOperations<T>> BufferAlgebra<T, A>.sin(arg: Buffer<T>): Buffer<T> =
@ -131,7 +129,6 @@ public fun <T, A : PowerOperations<T>> BufferAlgebra<T, A>.pow(arg: Buffer<T>, p
public open class BufferRingOps<T, A : Ring<T>>(
override val elementAlgebra: A,
override val bufferFactory: BufferFactory<T>,
) : BufferAlgebra<T, A>, RingOps<Buffer<T>> {
override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
@ -146,13 +143,11 @@ public open class BufferRingOps<T, A : Ring<T>>(
}
public val ShortRing.bufferAlgebra: BufferRingOps<Short, ShortRing>
get() = BufferRingOps(ShortRing, ::ShortBuffer)
get() = BufferRingOps(ShortRing)
public open class BufferFieldOps<T, A : Field<T>>(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
) : BufferRingOps<T, A>(elementAlgebra, bufferFactory), BufferAlgebra<T, A>, FieldOps<Buffer<T>>,
ScaleOperations<Buffer<T>> {
) : BufferRingOps<T, A>(elementAlgebra), BufferAlgebra<T, A>, FieldOps<Buffer<T>>, ScaleOperations<Buffer<T>> {
// override fun add(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l + r }
// override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
@ -167,30 +162,26 @@ public open class BufferFieldOps<T, A : Field<T>>(
public class BufferField<T, A : Field<T>>(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
override val size: Int,
) : BufferFieldOps<T, A>(elementAlgebra, bufferFactory), Field<Buffer<T>>, WithSize {
) : BufferFieldOps<T, A>(elementAlgebra), Field<Buffer<T>>, WithSize {
override val zero: Buffer<T> = bufferFactory(size) { elementAlgebra.zero }
override val one: Buffer<T> = bufferFactory(size) { elementAlgebra.one }
override val zero: Buffer<T> = elementAlgebra.bufferFactory(size) { elementAlgebra.zero }
override val one: Buffer<T> = elementAlgebra.bufferFactory(size) { elementAlgebra.one }
}
/**
* Generate full buffer field from given buffer operations
*/
public fun <T, A : Field<T>> BufferFieldOps<T, A>.withSize(size: Int): BufferField<T, A> =
BufferField(elementAlgebra, bufferFactory, size)
BufferField(elementAlgebra, size)
//Double buffer specialization
public fun BufferField<Double, *>.buffer(vararg elements: Number): Buffer<Double> {
require(elements.size == size) { "Expected $size elements but found ${elements.size}" }
return bufferFactory(size) { elements[it].toDouble() }
return elementBufferFactory(size) { elements[it].toDouble() }
}
public fun <T, A : Field<T>> A.bufferAlgebra(bufferFactory: BufferFactory<T>): BufferFieldOps<T, A> =
BufferFieldOps(this, bufferFactory)
public val DoubleField.bufferAlgebra: BufferFieldOps<Double, DoubleField>
get() = BufferFieldOps(DoubleField, ::DoubleBuffer)
public val <T, A : Field<T>> A.bufferAlgebra: BufferFieldOps<T, A>
get() = BufferFieldOps(this)

View File

@ -7,8 +7,8 @@ package space.kscience.kmath.operations
import space.kscience.kmath.linear.Point
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.MutableBufferFactory
import space.kscience.kmath.structures.asBuffer
import kotlin.math.*
@ -19,7 +19,7 @@ public abstract class DoubleBufferOps : BufferAlgebra<Double, DoubleField>, Exte
Norm<Buffer<Double>, Double> {
override val elementAlgebra: DoubleField get() = DoubleField
override val bufferFactory: BufferFactory<Double> get() = BufferFactory(::DoubleBuffer)
override val elementBufferFactory: MutableBufferFactory<Double> get() = elementAlgebra.bufferFactory
override fun Buffer<Double>.map(block: DoubleField.(Double) -> Double): DoubleBuffer =
mapInline { DoubleField.block(it) }

View File

@ -88,7 +88,7 @@ public inline fun <T, R> Buffer<T>.mapIndexed(
*/
public inline fun <T, reified R : Any> Buffer<T>.mapIndexed(
crossinline block: (index: Int, value: T) -> R,
): Buffer<R> = BufferFactory<R>(Buffer.Companion::auto).invoke(size) { block(it, get(it)) }
): Buffer<R> = Buffer.auto(size) { block(it, get(it)) }
/**
* Fold given buffer according to [operation]
@ -105,7 +105,7 @@ public inline fun <T, R> Buffer<T>.fold(initial: R, operation: (acc: R, T) -> R)
@UnstableKMathAPI
public inline fun <T1, T2 : Any, reified R : Any> Buffer<T1>.zip(
other: Buffer<T2>,
bufferFactory: BufferFactory<R> = BufferFactory(Buffer.Companion::auto),
bufferFactory: BufferFactory<R> = BufferFactory.auto(),
crossinline transform: (T1, T2) -> R,
): Buffer<R> {
require(size == other.size) { "Buffer size mismatch in zip: expected $size but found ${other.size}" }

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.operations
import space.kscience.kmath.structures.*
import kotlin.math.pow as kpow
/**
@ -65,6 +66,8 @@ public interface ExtendedField<T> : ExtendedFieldOps<T>, Field<T>, PowerOperatio
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object DoubleField : ExtendedField<Double>, Norm<Double, Double>, ScaleOperations<Double> {
override val bufferFactory: MutableBufferFactory<Double> = MutableBufferFactory(::DoubleBuffer)
override inline val zero: Double get() = 0.0
override inline val one: Double get() = 1.0
@ -123,6 +126,8 @@ public val Double.Companion.algebra: DoubleField get() = DoubleField
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object FloatField : ExtendedField<Float>, Norm<Float, Float> {
override val bufferFactory: MutableBufferFactory<Float> = MutableBufferFactory(::FloatBuffer)
override inline val zero: Float get() = 0.0f
override inline val one: Float get() = 1.0f
@ -177,11 +182,10 @@ public val Float.Companion.algebra: FloatField get() = FloatField
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object IntRing : Ring<Int>, Norm<Int, Int>, NumericAlgebra<Int> {
override inline val zero: Int
get() = 0
override val bufferFactory: MutableBufferFactory<Int> = MutableBufferFactory(::IntBuffer)
override inline val one: Int
get() = 1
override inline val zero: Int get() = 0
override inline val one: Int get() = 1
override fun number(value: Number): Int = value.toInt()
override inline fun add(left: Int, right: Int): Int = left + right
@ -201,11 +205,10 @@ public val Int.Companion.algebra: IntRing get() = IntRing
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object ShortRing : Ring<Short>, Norm<Short, Short>, NumericAlgebra<Short> {
override inline val zero: Short
get() = 0
override val bufferFactory: MutableBufferFactory<Short> = MutableBufferFactory(::ShortBuffer)
override inline val one: Short
get() = 1
override inline val zero: Short get() = 0
override inline val one: Short get() = 1
override fun number(value: Number): Short = value.toShort()
override inline fun add(left: Short, right: Short): Short = (left + right).toShort()
@ -225,11 +228,10 @@ public val Short.Companion.algebra: ShortRing get() = ShortRing
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object ByteRing : Ring<Byte>, Norm<Byte, Byte>, NumericAlgebra<Byte> {
override inline val zero: Byte
get() = 0
override val bufferFactory: MutableBufferFactory<Byte> = MutableBufferFactory(::ByteBuffer)
override inline val one: Byte
get() = 1
override inline val zero: Byte get() = 0
override inline val one: Byte get() = 1
override fun number(value: Number): Byte = value.toByte()
override inline fun add(left: Byte, right: Byte): Byte = (left + right).toByte()
@ -249,11 +251,10 @@ public val Byte.Companion.algebra: ByteRing get() = ByteRing
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public object LongRing : Ring<Long>, Norm<Long, Long>, NumericAlgebra<Long> {
override inline val zero: Long
get() = 0L
override val bufferFactory: MutableBufferFactory<Long> = MutableBufferFactory(::LongBuffer)
override inline val one: Long
get() = 1L
override inline val zero: Long get() = 0L
override inline val one: Long get() = 1L
override fun number(value: Number): Long = value.toLong()
override inline fun add(left: Long, right: Long): Long = left + right

View File

@ -16,6 +16,14 @@ import kotlin.reflect.KClass
*/
public fun interface BufferFactory<T> {
public operator fun invoke(size: Int, builder: (Int) -> T): Buffer<T>
public companion object{
public inline fun <reified T : Any> auto(): BufferFactory<T> =
BufferFactory(Buffer.Companion::auto)
public fun <T> boxing(): BufferFactory<T> =
BufferFactory(Buffer.Companion::boxing)
}
}
/**
@ -25,6 +33,14 @@ public fun interface BufferFactory<T> {
*/
public fun interface MutableBufferFactory<T> : BufferFactory<T> {
override fun invoke(size: Int, builder: (Int) -> T): MutableBuffer<T>
public companion object {
public inline fun <reified T : Any> auto(): MutableBufferFactory<T> =
MutableBufferFactory(MutableBuffer.Companion::auto)
public fun <T> boxing(): MutableBufferFactory<T> =
MutableBufferFactory(MutableBuffer.Companion::boxing)
}
}
/**

View File

@ -0,0 +1,57 @@
/*
* Copyright 2018-2021 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.
*/
package space.kscience.kmath.structures
import kotlin.jvm.JvmInline
/**
* Specialized [MutableBuffer] implementation over [ByteArray].
*
* @property array the underlying array.
*/
@JvmInline
public value class ByteBuffer(public val array: ByteArray) : MutableBuffer<Byte> {
override val size: Int get() = array.size
override operator fun get(index: Int): Byte = array[index]
override operator fun set(index: Int, value: Byte) {
array[index] = value
}
override operator fun iterator(): ByteIterator = array.iterator()
override fun copy(): MutableBuffer<Byte> = ByteBuffer(array.copyOf())
}
/**
* Creates a new [ByteBuffer] with the specified [size], where each element is calculated by calling the specified
* [init] function.
*
* The function [init] is called for each array element sequentially starting from the first one.
* It should return the value for a buffer element given its index.
*/
public inline fun ByteBuffer(size: Int, init: (Int) -> Byte): ByteBuffer = ByteBuffer(ByteArray(size) { init(it) })
/**
* Returns a new [ByteBuffer] of given elements.
*/
public fun ByteBuffer(vararg bytes: Byte): ByteBuffer = ByteBuffer(bytes)
/**
* Returns a new [ByteArray] containing all the elements of this [Buffer].
*/
public fun Buffer<Byte>.toByteArray(): ByteArray = when (this) {
is ByteBuffer -> array.copyOf()
else -> ByteArray(size, ::get)
}
/**
* Returns [ByteBuffer] over this array.
*
* @receiver the array.
* @return the new buffer.
*/
public fun ByteArray.asBuffer(): ByteBuffer = ByteBuffer(this)

View File

@ -22,7 +22,7 @@ internal inline fun diff(
block: DSField<Double, DoubleField>.() -> Unit,
) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
DSField(DoubleField, ::DoubleBuffer, order, mapOf(*parameters)).block()
DSField(DoubleField, order, mapOf(*parameters), ::DoubleBuffer).block()
}
internal class DSTest {

View File

@ -21,14 +21,14 @@ public typealias HyperSquareBin<V> = DomainBin<Double, HyperSquareDomain, V>
/**
* Multivariate histogram space for hyper-square real-field bins.
* @param bufferFactory is an optional parameter used to optimize buffer production.
* @param valueBufferFactory is an optional parameter used to optimize buffer production.
*/
public class UniformHistogramGroupND<V : Any, A : Field<V>>(
override val valueAlgebraND: FieldOpsND<V, A>,
private val lower: Buffer<Double>,
private val upper: Buffer<Double>,
private val binNums: IntArray = IntArray(lower.size) { 20 },
private val bufferFactory: BufferFactory<V> = BufferFactory(Buffer.Companion::boxing),
private val valueBufferFactory: BufferFactory<V> = valueAlgebraND.elementAlgebra.bufferFactory,
) : HistogramGroupND<Double, HyperSquareDomain, V> {
init {
@ -94,7 +94,7 @@ public class UniformHistogramGroupND<V : Any, A : Field<V>>(
}
}
hBuilder.apply(builder)
val values: BufferND<V> = ndCounter.mapToBuffer(bufferFactory) { it.value }
val values: BufferND<V> = ndCounter.mapToBuffer(valueBufferFactory) { it.value }
return HistogramND(this, values)
}
@ -114,12 +114,12 @@ public class UniformHistogramGroupND<V : Any, A : Field<V>>(
public fun <V : Any, A : Field<V>> Histogram.Companion.uniformNDFromRanges(
valueAlgebraND: FieldOpsND<V, A>,
vararg ranges: ClosedFloatingPointRange<Double>,
bufferFactory: BufferFactory<V> = BufferFactory(Buffer.Companion::boxing),
bufferFactory: BufferFactory<V> = valueAlgebraND.elementAlgebra.bufferFactory,
): UniformHistogramGroupND<V, A> = UniformHistogramGroupND(
valueAlgebraND,
ranges.map(ClosedFloatingPointRange<Double>::start).asBuffer(),
ranges.map(ClosedFloatingPointRange<Double>::endInclusive).asBuffer(),
bufferFactory = bufferFactory
valueBufferFactory = bufferFactory
)
public fun Histogram.Companion.uniformDoubleNDFromRanges(
@ -140,7 +140,7 @@ public fun Histogram.Companion.uniformDoubleNDFromRanges(
public fun <V : Any, A : Field<V>> Histogram.Companion.uniformNDFromRanges(
valueAlgebraND: FieldOpsND<V, A>,
vararg ranges: Pair<ClosedFloatingPointRange<Double>, Int>,
bufferFactory: BufferFactory<V> = BufferFactory(Buffer.Companion::boxing),
bufferFactory: BufferFactory<V> = valueAlgebraND.elementAlgebra.bufferFactory,
): UniformHistogramGroupND<V, A> = UniformHistogramGroupND(
valueAlgebraND,
ListBuffer(
@ -154,7 +154,7 @@ public fun <V : Any, A : Field<V>> Histogram.Companion.uniformNDFromRanges(
.map(ClosedFloatingPointRange<Double>::endInclusive)
),
ranges.map(Pair<ClosedFloatingPointRange<Double>, Int>::second).toIntArray(),
bufferFactory = bufferFactory
valueBufferFactory = bufferFactory
)
public fun Histogram.Companion.uniformDoubleNDFromRanges(

View File

@ -35,7 +35,7 @@ public fun interface Sampler<out T : Any> {
public fun <T : Any> Sampler<T>.sampleBuffer(
generator: RandomGenerator,
size: Int,
bufferFactory: BufferFactory<T> = BufferFactory(Buffer.Companion::boxing),
bufferFactory: BufferFactory<T> = BufferFactory.boxing(),
): Chain<Buffer<T>> {
require(size > 1)
//creating temporary storage once