Merge branch 'dev' into feature/tensorflow

# Conflicts:
#	kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt
This commit is contained in:
Alexander Nozik 2021-11-10 11:52:11 +03:00
commit c583320051
38 changed files with 572 additions and 393 deletions

View File

@ -17,6 +17,7 @@
- `BigInt` operation performance improvement and fixes by @zhelenskiy (#328)
- Integration between `MST` and Symja `IExpr`
- Complex power
- Separate methods for UInt, Int and Number powers. NaN safety.
### Changed
- Exponential operations merged with hyperbolic functions

View File

@ -13,6 +13,7 @@ import space.kscience.kmath.commons.linear.CMLinearSpace
import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM
import space.kscience.kmath.linear.invoke
import space.kscience.kmath.linear.linearSpace
import space.kscience.kmath.multik.multikAlgebra
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.Buffer
import kotlin.random.Random
@ -58,6 +59,16 @@ internal class DotBenchmark {
blackhole.consume(matrix1 dot matrix2)
}
// @Benchmark
// fun tensorDot(blackhole: Blackhole) = with(Double.tensorAlgebra) {
// blackhole.consume(matrix1 dot matrix2)
// }
@Benchmark
fun multikDot(blackhole: Blackhole) = with(Double.multikAlgebra) {
blackhole.consume(matrix1 dot matrix2)
}
@Benchmark
fun bufferedDot(blackhole: Blackhole) = with(DoubleField.linearSpace(Buffer.Companion::auto)) {
blackhole.consume(matrix1 dot matrix2)

View File

@ -163,7 +163,7 @@ class NeuralNetwork(private val layers: List<Layer>) {
for ((xBatch, yBatch) in iterBatch(xTrain, yTrain)) {
train(xBatch, yBatch)
}
println("Accuracy:${accuracy(yTrain, predict(xTrain).argMax(1, true))}")
println("Accuracy:${accuracy(yTrain, predict(xTrain).argMax(1, true).asDouble())}")
}
}
@ -230,7 +230,7 @@ fun main() = BroadcastDoubleTensorAlgebra {
val prediction = model.predict(xTest)
// process raw prediction via argMax
val predictionLabels = prediction.argMax(1, true)
val predictionLabels = prediction.argMax(1, true).asDouble()
// find out accuracy
val acc = accuracy(yTest, predictionLabels)

View File

@ -18,7 +18,7 @@ import kotlin.contracts.contract
*/
@OptIn(UnstableKMathAPI::class)
public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField>(ComplexField.bufferAlgebra),
ScaleOperations<StructureND<Complex>>, ExtendedFieldOps<StructureND<Complex>> {
ScaleOperations<StructureND<Complex>>, ExtendedFieldOps<StructureND<Complex>>, PowerOperations<StructureND<Complex>> {
override fun StructureND<Complex>.toBufferND(): BufferND<Complex> = when (this) {
is BufferND -> this
@ -33,9 +33,6 @@ public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField
override fun scale(a: StructureND<Complex>, value: Double): BufferND<Complex> =
mapInline(a.toBufferND()) { it * value }
override fun power(arg: StructureND<Complex>, pow: Number): BufferND<Complex> =
mapInline(arg.toBufferND()) { power(it, pow) }
override fun exp(arg: StructureND<Complex>): BufferND<Complex> = mapInline(arg.toBufferND()) { exp(it) }
override fun ln(arg: StructureND<Complex>): BufferND<Complex> = mapInline(arg.toBufferND()) { ln(it) }
@ -53,6 +50,9 @@ public sealed class ComplexFieldOpsND : BufferedFieldOpsND<Complex, ComplexField
override fun acosh(arg: StructureND<Complex>): BufferND<Complex> = mapInline(arg.toBufferND()) { acosh(it) }
override fun atanh(arg: StructureND<Complex>): BufferND<Complex> = mapInline(arg.toBufferND()) { atanh(it) }
override fun power(arg: StructureND<Complex>, pow: Number): StructureND<Complex> =
mapInline(arg.toBufferND()) { power(it,pow) }
public companion object : ComplexFieldOpsND()
}
@ -63,7 +63,8 @@ public val ComplexField.bufferAlgebra: BufferFieldOps<Complex, ComplexField>
@OptIn(UnstableKMathAPI::class)
public class ComplexFieldND(override val shape: Shape) :
ComplexFieldOpsND(), FieldND<Complex, ComplexField>, NumbersAddOps<StructureND<Complex>> {
ComplexFieldOpsND(), FieldND<Complex, ComplexField>,
NumbersAddOps<StructureND<Complex>> {
override fun number(value: Number): BufferND<Complex> {
val d = value.toDouble() // minimize conversions

View File

@ -252,7 +252,7 @@ public class SimpleAutoDiffExpression<T : Any, F : Field<T>>(
* Generate [AutoDiffProcessor] for [SimpleAutoDiffExpression]
*/
public fun <T : Any, F : Field<T>> simpleAutoDiff(
field: F
field: F,
): AutoDiffProcessor<T, AutoDiffValue<T>, SimpleAutoDiffField<T, F>> =
AutoDiffProcessor { function ->
SimpleAutoDiffExpression(field, function)
@ -272,8 +272,8 @@ public fun <T : Any, F : ExtendedField<T>> SimpleAutoDiffField<T, F>.sqrt(x: Aut
public fun <T : Any, F : ExtendedField<T>> SimpleAutoDiffField<T, F>.pow(
x: AutoDiffValue<T>,
y: Double,
): AutoDiffValue<T> = derive(const { power(x.value, y) }) { z ->
x.d += z.d * y * power(x.value, y - 1)
): AutoDiffValue<T> = derive(const { x.value.pow(y)}) { z ->
x.d += z.d * y * x.value.pow(y - 1)
}
public fun <T : Any, F : ExtendedField<T>> SimpleAutoDiffField<T, F>.pow(

View File

@ -35,7 +35,7 @@ public interface WithShape {
* @param T the type of ND-structure element.
* @param C the type of the element context.
*/
public interface AlgebraND<T, out C : Algebra<T>> {
public interface AlgebraND<T, out C : Algebra<T>>: Algebra<StructureND<T>> {
/**
* The algebra over elements of ND structure.
*/

View File

@ -53,21 +53,28 @@ public interface BufferAlgebraND<T, out A : Algebra<T>> : AlgebraND<T, A> {
public inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapInline(
arg: BufferND<T>,
crossinline transform: A.(T) -> T
crossinline transform: A.(T) -> T,
): BufferND<T> {
val indexes = arg.indices
return BufferND(indexes, bufferAlgebra.mapInline(arg.buffer, transform))
val buffer = arg.buffer
return BufferND(
indexes,
bufferAlgebra.run {
bufferFactory(buffer.size) { elementAlgebra.transform(buffer[it]) }
}
)
}
internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapIndexedInline(
arg: BufferND<T>,
crossinline transform: A.(index: IntArray, arg: T) -> T
crossinline transform: A.(index: IntArray, arg: T) -> T,
): BufferND<T> {
val indexes = arg.indices
val buffer = arg.buffer
return BufferND(
indexes,
bufferAlgebra.mapIndexedInline(arg.buffer) { offset, value ->
transform(indexes.index(offset), value)
bufferAlgebra.run {
bufferFactory(buffer.size) { elementAlgebra.transform(indexes.index(it), buffer[it]) }
}
)
}
@ -75,35 +82,42 @@ internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.mapIndexedInline(
internal inline fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.zipInline(
l: BufferND<T>,
r: BufferND<T>,
crossinline block: A.(l: T, r: T) -> T
crossinline block: A.(l: T, r: T) -> T,
): BufferND<T> {
require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
val indexes = l.indices
return BufferND(indexes, bufferAlgebra.zipInline(l.buffer, r.buffer, block))
val lbuffer = l.buffer
val rbuffer = r.buffer
return BufferND(
indexes,
bufferAlgebra.run {
bufferFactory(lbuffer.size) { elementAlgebra.block(lbuffer[it], rbuffer[it]) }
}
)
}
@OptIn(PerformancePitfall::class)
public open class BufferedGroupNDOps<T, out A : Group<T>>(
override val bufferAlgebra: BufferAlgebra<T, A>,
override val indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder
override val indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : GroupOpsND<T, A>, BufferAlgebraND<T, A> {
override fun StructureND<T>.unaryMinus(): StructureND<T> = map { -it }
}
public open class BufferedRingOpsND<T, out A : Ring<T>>(
bufferAlgebra: BufferAlgebra<T, A>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : BufferedGroupNDOps<T, A>(bufferAlgebra, indexerBuilder), RingOpsND<T, A>
public open class BufferedFieldOpsND<T, out A : Field<T>>(
bufferAlgebra: BufferAlgebra<T, A>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : BufferedRingOpsND<T, A>(bufferAlgebra, indexerBuilder), FieldOpsND<T, A> {
public constructor(
elementAlgebra: A,
bufferFactory: BufferFactory<T>,
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder
indexerBuilder: (IntArray) -> ShapeIndexer = BufferAlgebraND.defaultIndexerBuilder,
) : this(BufferFieldOps(elementAlgebra, bufferFactory), indexerBuilder)
@OptIn(PerformancePitfall::class)
@ -117,11 +131,11 @@ public val <T, A : Field<T>> BufferAlgebra<T, A>.nd: BufferedFieldOpsND<T, A> ge
public fun <T, A : Algebra<T>> BufferAlgebraND<T, A>.structureND(
vararg shape: Int,
initializer: A.(IntArray) -> T
initializer: A.(IntArray) -> T,
): BufferND<T> = structureND(shape, initializer)
public fun <T, EA : Algebra<T>, A> A.structureND(
initializer: EA.(IntArray) -> T
initializer: EA.(IntArray) -> T,
): BufferND<T> where A : BufferAlgebraND<T, EA>, A : WithShape = structureND(shape, initializer)
//// group factories

View File

@ -5,7 +5,6 @@
package space.kscience.kmath.nd
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.MutableBuffer
@ -27,11 +26,6 @@ public open class BufferND<out T>(
override val shape: IntArray get() = indices.shape
@PerformancePitfall
override fun elements(): Sequence<Pair<IntArray, T>> = indices.asSequence().map {
it to this[it]
}
override fun toString(): String = StructureND.toString(this)
}

View File

@ -11,7 +11,7 @@ import space.kscience.kmath.operations.*
import space.kscience.kmath.structures.DoubleBuffer
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.math.pow
import kotlin.math.pow as kpow
public class DoubleBufferND(
indexes: ShapeIndexer,
@ -30,9 +30,9 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
}
}
private inline fun mapInline(
protected inline fun mapInline(
arg: DoubleBufferND,
transform: (Double) -> Double
transform: (Double) -> Double,
): DoubleBufferND {
val indexes = arg.indices
val array = arg.buffer.array
@ -42,7 +42,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
private inline fun zipInline(
l: DoubleBufferND,
r: DoubleBufferND,
block: (l: Double, r: Double) -> Double
block: (l: Double, r: Double) -> Double,
): DoubleBufferND {
require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" }
val indexes = l.indices
@ -60,7 +60,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
override fun zip(
left: StructureND<Double>,
right: StructureND<Double>,
transform: DoubleField.(Double, Double) -> Double
transform: DoubleField.(Double, Double) -> Double,
): BufferND<Double> = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) }
override fun structureND(shape: Shape, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND {
@ -81,8 +81,8 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
override fun StructureND<Double>.unaryMinus(): DoubleBufferND = mapInline(toBufferND()) { -it }
override fun StructureND<Double>.div(other: StructureND<Double>): DoubleBufferND =
zipInline(toBufferND(), other.toBufferND()) { l, r -> l / r }
override fun StructureND<Double>.div(arg: StructureND<Double>): DoubleBufferND =
zipInline(toBufferND(), arg.toBufferND()) { l, r -> l / r }
override fun divide(left: StructureND<Double>, right: StructureND<Double>): DoubleBufferND =
zipInline(left.toBufferND(), right.toBufferND()) { l: Double, r: Double -> l / r }
@ -101,8 +101,8 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
override fun StructureND<Double>.minus(arg: StructureND<Double>): DoubleBufferND =
zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l - r }
override fun StructureND<Double>.times(other: StructureND<Double>): DoubleBufferND =
zipInline(toBufferND(), other.toBufferND()) { l: Double, r: Double -> l * r }
override fun StructureND<Double>.times(arg: StructureND<Double>): DoubleBufferND =
zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l * r }
override fun StructureND<Double>.times(k: Number): DoubleBufferND =
mapInline(toBufferND()) { it * k.toDouble() }
@ -123,9 +123,6 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
override fun scale(a: StructureND<Double>, value: Double): DoubleBufferND =
mapInline(a.toBufferND()) { it * value }
override fun power(arg: StructureND<Double>, pow: Number): DoubleBufferND =
mapInline(arg.toBufferND()) { it.pow(pow.toDouble()) }
override fun exp(arg: StructureND<Double>): DoubleBufferND =
mapInline(arg.toBufferND()) { kotlin.math.exp(it) }
@ -173,7 +170,38 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND<Double, DoubleField>(D
@OptIn(UnstableKMathAPI::class)
public class DoubleFieldND(override val shape: Shape) :
DoubleFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>> {
DoubleFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>>,
ExtendedField<StructureND<Double>> {
override fun power(arg: StructureND<Double>, pow: UInt): DoubleBufferND = mapInline(arg.toBufferND()) {
it.kpow(pow.toInt())
}
override fun power(arg: StructureND<Double>, pow: Int): DoubleBufferND = mapInline(arg.toBufferND()) {
it.kpow(pow)
}
override fun power(arg: StructureND<Double>, pow: Number): DoubleBufferND = if(pow.isInteger()){
power(arg, pow.toInt())
} else {
val dpow = pow.toDouble()
mapInline(arg.toBufferND()) {
if (it < 0) throw IllegalArgumentException("Can't raise negative $it to a fractional power")
else it.kpow(dpow)
}
}
override fun sinh(arg: StructureND<Double>): DoubleBufferND = super<DoubleFieldOpsND>.sinh(arg)
override fun cosh(arg: StructureND<Double>): DoubleBufferND = super<DoubleFieldOpsND>.cosh(arg)
override fun tanh(arg: StructureND<Double>): DoubleBufferND = super<DoubleFieldOpsND>.tan(arg)
override fun asinh(arg: StructureND<Double>): DoubleBufferND = super<DoubleFieldOpsND>.asinh(arg)
override fun acosh(arg: StructureND<Double>): DoubleBufferND = super<DoubleFieldOpsND>.acosh(arg)
override fun atanh(arg: StructureND<Double>): DoubleBufferND = super<DoubleFieldOpsND>.atanh(arg)
override fun number(value: Number): DoubleBufferND {
val d = value.toDouble() // minimize conversions

View File

@ -6,6 +6,8 @@
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.
@ -99,6 +101,14 @@ public interface Algebra<T> {
*/
public fun binaryOperation(operation: String, left: T, right: T): T =
binaryOperationFunction(operation)(left, right)
/**
* Export an algebra element, so it could be accessed even after algebra scope is closed.
* This method must be used on algebras where data is stored externally or any local algebra state is used.
* By default (if not overridden), exports the object itself.
*/
@UnstableKMathAPI
public fun export(arg: T): T = arg
}
public fun <T> Algebra<T>.bindSymbolOrNull(symbol: Symbol): T? = bindSymbolOrNull(symbol.identity)
@ -162,6 +172,7 @@ public interface GroupOps<T> : Algebra<T> {
* @return the difference.
*/
public operator fun T.minus(arg: T): T = add(this, -arg)
// Dynamic dispatch of operations
override fun unaryOperationFunction(operation: String): (arg: T) -> T = when (operation) {
PLUS_OPERATION -> { arg -> +arg }
@ -247,6 +258,40 @@ public interface Ring<T> : Group<T>, RingOps<T> {
* The neutral element of multiplication
*/
public val one: T
/**
* Raises [arg] to the integer power [pow].
*/
public fun power(arg: T, pow: UInt): T = optimizedPower(arg, pow)
public companion object{
/**
* Raises [arg] to the non-negative integer power [exponent].
*
* Special case: 0 ^ 0 is 1.
*
* @receiver the algebra to provide multiplication.
* @param arg the base.
* @param exponent the exponent.
* @return the base raised to the power.
* @author Evgeniy Zhelenskiy
*/
internal fun <T> Ring<T>.optimizedPower(arg: T, exponent: UInt): T = when {
arg == zero && exponent > 0U -> zero
arg == one -> arg
arg == -one -> powWithoutOptimization(arg, exponent % 2U)
else -> powWithoutOptimization(arg, exponent)
}
private fun <T> Ring<T>.powWithoutOptimization(base: T, exponent: UInt): T = when (exponent) {
0U -> one
1U -> base
else -> {
val pre = powWithoutOptimization(base, exponent shr 1).let { it * it }
if (exponent and 1U == 0U) pre else pre * base
}
}
}
}
/**
@ -270,10 +315,10 @@ public interface FieldOps<T> : RingOps<T> {
* Division of two elements.
*
* @receiver the dividend.
* @param other the divisor.
* @param arg the divisor.
* @return the quotient.
*/
public operator fun T.div(other: T): T = divide(this, other)
public operator fun T.div(arg: T): T = divide(this, arg)
override fun binaryOperationFunction(operation: String): (left: T, right: T) -> T = when (operation) {
DIV_OPERATION -> ::divide
@ -297,4 +342,24 @@ public interface FieldOps<T> : RingOps<T> {
*/
public interface Field<T> : Ring<T>, FieldOps<T>, ScaleOperations<T>, NumericAlgebra<T> {
override fun number(value: Number): T = scale(one, value.toDouble())
public fun power(arg: T, pow: Int): T = optimizedPower(arg, pow)
public companion object{
/**
* Raises [arg] to the integer power [exponent].
*
* Special case: 0 ^ 0 is 1.
*
* @receiver the algebra to provide multiplication and division.
* @param arg the base.
* @param exponent the exponent.
* @return the base raised to the power.
* @author Iaroslav Postovalov, Evgeniy Zhelenskiy
*/
private fun <T> Field<T>.optimizedPower(arg: T, exponent: Int): T = when {
exponent < 0 -> one / (this as Ring<T>).optimizedPower(arg, if (exponent == Int.MIN_VALUE) Int.MAX_VALUE.toUInt().inc() else (-exponent).toUInt())
else -> (this as Ring<T>).optimizedPower(arg, exponent.toUInt())
}
}
}

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
@ -34,11 +35,13 @@ public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
public fun Buffer<T>.zip(other: Buffer<T>, block: A.(left: T, right: T) -> T): Buffer<T> =
zipInline(this, other, block)
@UnstableKMathAPI
override fun unaryOperationFunction(operation: String): (arg: Buffer<T>) -> Buffer<T> {
val operationFunction = elementAlgebra.unaryOperationFunction(operation)
return { arg -> bufferFactory(arg.size) { operationFunction(arg[it]) } }
}
@UnstableKMathAPI
override fun binaryOperationFunction(operation: String): (left: Buffer<T>, right: Buffer<T>) -> Buffer<T> {
val operationFunction = elementAlgebra.binaryOperationFunction(operation)
return { left, right ->
@ -50,7 +53,7 @@ public interface BufferAlgebra<T, out A : Algebra<T>> : Algebra<Buffer<T>> {
/**
* Inline map
*/
public inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
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]) }
@ -58,7 +61,7 @@ public inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapInline(
/**
* Inline map
*/
public inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
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]) }
@ -66,7 +69,7 @@ public inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.mapIndexedInline(
/**
* Inline zip
*/
public inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline(
private inline fun <T, A : Algebra<T>> BufferAlgebra<T, A>.zipInline(
l: Buffer<T>,
r: Buffer<T>,
crossinline block: A.(l: T, r: T) -> T
@ -126,7 +129,7 @@ public fun <T, A : ExponentialOperations<T>> BufferAlgebra<T, A>.atanh(arg: Buff
mapInline(arg) { atanh(it) }
public fun <T, A : PowerOperations<T>> BufferAlgebra<T, A>.pow(arg: Buffer<T>, pow: Number): Buffer<T> =
mapInline(arg) { power(it, pow) }
mapInline(arg) {it.pow(pow) }
public open class BufferRingOps<T, A: Ring<T>>(
@ -138,9 +141,11 @@ public open class BufferRingOps<T, A: Ring<T>>(
override fun multiply(left: Buffer<T>, right: Buffer<T>): Buffer<T> = zipInline(left, right) { l, r -> l * r }
override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it }
@UnstableKMathAPI
override fun unaryOperationFunction(operation: String): (arg: Buffer<T>) -> Buffer<T> =
super<BufferAlgebra>.unaryOperationFunction(operation)
@UnstableKMathAPI
override fun binaryOperationFunction(operation: String): (left: Buffer<T>, right: Buffer<T>) -> Buffer<T> =
super<BufferAlgebra>.binaryOperationFunction(operation)
}
@ -160,6 +165,7 @@ public open class BufferFieldOps<T, A : Field<T>>(
override fun scale(a: Buffer<T>, value: Double): Buffer<T> = a.map { scale(it, value) }
override fun Buffer<T>.unaryMinus(): Buffer<T> = map { -it }
@UnstableKMathAPI
override fun binaryOperationFunction(operation: String): (left: Buffer<T>, right: Buffer<T>) -> Buffer<T> =
super<BufferRingOps>.binaryOperationFunction(operation)
}

View File

@ -5,6 +5,8 @@
package space.kscience.kmath.operations
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.operations.DoubleField.pow
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.DoubleBuffer
@ -29,6 +31,19 @@ public class DoubleBufferField(public val size: Int) : ExtendedField<Buffer<Doub
override fun atanh(arg: Buffer<Double>): DoubleBuffer = super<DoubleBufferOps>.atanh(arg)
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer = if (pow.isInteger()) {
arg.mapInline { it.pow(pow.toInt()) }
} else {
arg.mapInline {
if(it<0) throw IllegalArgumentException("Negative argument $it could not be raised to the fractional power")
it.pow(pow.toDouble())
}
}
@UnstableKMathAPI
override fun unaryOperationFunction(operation: String): (arg: Buffer<Double>) -> Buffer<Double> =
super<ExtendedField>.unaryOperationFunction(operation)
// override fun number(value: Number): Buffer<Double> = DoubleBuffer(size) { value.toDouble() }
//
// override fun Buffer<Double>.unaryMinus(): Buffer<Double> = DoubleBufferOperations.run {

View File

@ -6,21 +6,35 @@
package space.kscience.kmath.operations
import space.kscience.kmath.linear.Point
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.structures.Buffer
import space.kscience.kmath.structures.BufferFactory
import space.kscience.kmath.structures.DoubleBuffer
import space.kscience.kmath.structures.asBuffer
import kotlin.math.*
/**
* [ExtendedFieldOps] over [DoubleBuffer].
*/
public abstract class DoubleBufferOps : ExtendedFieldOps<Buffer<Double>>, Norm<Buffer<Double>, Double> {
public abstract class DoubleBufferOps : BufferAlgebra<Double, DoubleField>, ExtendedFieldOps<Buffer<Double>>,
Norm<Buffer<Double>, Double> {
override fun Buffer<Double>.unaryMinus(): DoubleBuffer = if (this is DoubleBuffer) {
DoubleBuffer(size) { -array[it] }
} else {
DoubleBuffer(size) { -get(it) }
}
override val elementAlgebra: DoubleField get() = DoubleField
override val bufferFactory: BufferFactory<Double> get() = ::DoubleBuffer
override fun Buffer<Double>.map(block: DoubleField.(Double) -> Double): DoubleBuffer =
mapInline { DoubleField.block(it) }
@UnstableKMathAPI
override fun unaryOperationFunction(operation: String): (arg: Buffer<Double>) -> Buffer<Double> =
super<ExtendedFieldOps>.unaryOperationFunction(operation)
@UnstableKMathAPI
override fun binaryOperationFunction(operation: String): (left: Buffer<Double>, right: Buffer<Double>) -> Buffer<Double> =
super<ExtendedFieldOps>.binaryOperationFunction(operation)
override fun Buffer<Double>.unaryMinus(): DoubleBuffer = mapInline { -it }
override fun add(left: Buffer<Double>, right: Buffer<Double>): DoubleBuffer {
require(right.size == left.size) {
@ -34,18 +48,18 @@ public abstract class DoubleBufferOps : ExtendedFieldOps<Buffer<Double>>, Norm<B
} else DoubleBuffer(DoubleArray(left.size) { left[it] + right[it] })
}
override fun Buffer<Double>.plus(other: Buffer<Double>): DoubleBuffer = add(this, other)
override fun Buffer<Double>.plus(arg: Buffer<Double>): DoubleBuffer = add(this, arg)
override fun Buffer<Double>.minus(other: Buffer<Double>): DoubleBuffer {
require(other.size == this.size) {
"The size of the first buffer ${this.size} should be the same as for second one: ${other.size} "
override fun Buffer<Double>.minus(arg: Buffer<Double>): DoubleBuffer {
require(arg.size == this.size) {
"The size of the first buffer ${this.size} should be the same as for second one: ${arg.size} "
}
return if (this is DoubleBuffer && other is DoubleBuffer) {
return if (this is DoubleBuffer && arg is DoubleBuffer) {
val aArray = this.array
val bArray = other.array
val bArray = arg.array
DoubleBuffer(DoubleArray(this.size) { aArray[it] - bArray[it] })
} else DoubleBuffer(DoubleArray(this.size) { this[it] - other[it] })
} else DoubleBuffer(DoubleArray(this.size) { this[it] - arg[it] })
}
//
@ -76,8 +90,7 @@ public abstract class DoubleBufferOps : ExtendedFieldOps<Buffer<Double>>, Norm<B
val aArray = left.array
val bArray = right.array
DoubleBuffer(DoubleArray(left.size) { aArray[it] * bArray[it] })
} else
DoubleBuffer(DoubleArray(left.size) { left[it] * right[it] })
} else DoubleBuffer(DoubleArray(left.size) { left[it] * right[it] })
}
override fun divide(left: Buffer<Double>, right: Buffer<Double>): DoubleBuffer {
@ -92,101 +105,46 @@ public abstract class DoubleBufferOps : ExtendedFieldOps<Buffer<Double>>, Norm<B
} else DoubleBuffer(DoubleArray(left.size) { left[it] / right[it] })
}
override fun sin(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { sin(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { sin(arg[it]) })
override fun sin(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::sin)
override fun cos(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { cos(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { cos(arg[it]) })
override fun cos(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::cos)
override fun tan(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { tan(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { tan(arg[it]) })
override fun tan(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::tan)
override fun asin(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { asin(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { asin(arg[it]) })
override fun asin(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::asin)
override fun acos(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { acos(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { acos(arg[it]) })
override fun acos(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::acos)
override fun atan(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { atan(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { atan(arg[it]) })
override fun atan(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::atan)
override fun sinh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { sinh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { sinh(arg[it]) })
override fun sinh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::sinh)
override fun cosh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { cosh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { cosh(arg[it]) })
override fun cosh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::cosh)
override fun tanh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { tanh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { tanh(arg[it]) })
override fun tanh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::tanh)
override fun asinh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { asinh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { asinh(arg[it]) })
override fun asinh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::asinh)
override fun acosh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { acosh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { acosh(arg[it]) })
override fun acosh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::acosh)
override fun atanh(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { atanh(array[it]) })
} else
DoubleBuffer(DoubleArray(arg.size) { atanh(arg[it]) })
override fun atanh(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::atanh)
override fun power(arg: Buffer<Double>, pow: Number): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { array[it].pow(pow.toDouble()) })
} else
DoubleBuffer(DoubleArray(arg.size) { arg[it].pow(pow.toDouble()) })
override fun exp(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::exp)
override fun exp(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { exp(array[it]) })
} else DoubleBuffer(DoubleArray(arg.size) { exp(arg[it]) })
override fun ln(arg: Buffer<Double>): DoubleBuffer = if (arg is DoubleBuffer) {
val array = arg.array
DoubleBuffer(DoubleArray(arg.size) { ln(array[it]) })
} else {
DoubleBuffer(DoubleArray(arg.size) { ln(arg[it]) })
}
override fun ln(arg: Buffer<Double>): DoubleBuffer = arg.mapInline(::ln)
override fun norm(arg: Buffer<Double>): Double = DoubleL2Norm.norm(arg)
override fun scale(a: Buffer<Double>, value: Double): DoubleBuffer = if (a is DoubleBuffer) {
val aArray = a.array
DoubleBuffer(DoubleArray(a.size) { aArray[it] * value })
} else DoubleBuffer(DoubleArray(a.size) { a[it] * value })
override fun scale(a: Buffer<Double>, value: Double): DoubleBuffer = a.mapInline { it * value }
public companion object : DoubleBufferOps()
public companion object : DoubleBufferOps() {
public inline fun Buffer<Double>.mapInline(block: (Double) -> Double): DoubleBuffer =
if (this is DoubleBuffer) {
DoubleArray(size) { block(array[it]) }.asBuffer()
} else {
DoubleArray(size) { block(get(it)) }.asBuffer()
}
}
}
public object DoubleL2Norm : Norm<Point<Double>, Double> {

View File

@ -74,14 +74,21 @@ public interface TrigonometricOperations<T> : Algebra<T> {
}
}
/**
* Check if number is an integer from platform point of view
*/
public expect fun Number.isInteger(): Boolean
/**
* A context extension to include power operations based on exponentiation.
*
* @param T the type of element of this structure.
*/
public interface PowerOperations<T> : Algebra<T> {
public interface PowerOperations<T> : FieldOps<T> {
/**
* Raises [arg] to the power [pow].
* Raises [arg] to a power if possible (negative number could not be raised to a fractional power).
* Throws [IllegalArgumentException] if not possible.
*/
public fun power(arg: T, pow: Number): T
@ -108,6 +115,7 @@ public interface PowerOperations<T> : Algebra<T> {
}
}
/**
* A container for operations related to `exp` and `ln` functions.
*

View File

@ -96,46 +96,3 @@ public fun <T, S> Iterable<T>.averageWith(space: S): T where S : Ring<T>, S : Sc
public fun <T, S> Sequence<T>.averageWith(space: S): T where S : Ring<T>, S : ScaleOperations<T> =
space.average(this)
/**
* Raises [arg] to the non-negative integer power [exponent].
*
* Special case: 0 ^ 0 is 1.
*
* @receiver the algebra to provide multiplication.
* @param arg the base.
* @param exponent the exponent.
* @return the base raised to the power.
* @author Evgeniy Zhelenskiy
*/
public fun <T> Ring<T>.power(arg: T, exponent: UInt): T = when {
arg == zero && exponent > 0U -> zero
arg == one -> arg
arg == -one -> powWithoutOptimization(arg, exponent % 2U)
else -> powWithoutOptimization(arg, exponent)
}
private fun <T> Ring<T>.powWithoutOptimization(base: T, exponent: UInt): T = when (exponent) {
0U -> one
1U -> base
else -> {
val pre = powWithoutOptimization(base, exponent shr 1).let { it * it }
if (exponent and 1U == 0U) pre else pre * base
}
}
/**
* Raises [arg] to the integer power [exponent].
*
* Special case: 0 ^ 0 is 1.
*
* @receiver the algebra to provide multiplication and division.
* @param arg the base.
* @param exponent the exponent.
* @return the base raised to the power.
* @author Iaroslav Postovalov, Evgeniy Zhelenskiy
*/
public fun <T> Field<T>.power(arg: T, exponent: Int): T = when {
exponent < 0 -> one / (this as Ring<T>).power(arg, if (exponent == Int.MIN_VALUE) Int.MAX_VALUE.toUInt().inc() else (-exponent).toUInt())
else -> (this as Ring<T>).power(arg, exponent.toUInt())
}

View File

@ -13,7 +13,6 @@ import kotlin.math.pow as kpow
public interface ExtendedFieldOps<T> :
FieldOps<T>,
TrigonometricOperations<T>,
PowerOperations<T>,
ExponentialOperations<T>,
ScaleOperations<T> {
override fun tan(arg: T): T = sin(arg) / cos(arg)
@ -26,7 +25,6 @@ public interface ExtendedFieldOps<T> :
TrigonometricOperations.ACOS_OPERATION -> ::acos
TrigonometricOperations.ASIN_OPERATION -> ::asin
TrigonometricOperations.ATAN_OPERATION -> ::atan
PowerOperations.SQRT_OPERATION -> ::sqrt
ExponentialOperations.EXP_OPERATION -> ::exp
ExponentialOperations.LN_OPERATION -> ::ln
ExponentialOperations.COSH_OPERATION -> ::cosh
@ -42,7 +40,7 @@ public interface ExtendedFieldOps<T> :
/**
* Advanced Number-like field that implements basic operations.
*/
public interface ExtendedField<T> : ExtendedFieldOps<T>, Field<T>, NumericAlgebra<T>{
public interface ExtendedField<T> : ExtendedFieldOps<T>, Field<T>, PowerOperations<T>, NumericAlgebra<T> {
override fun sinh(arg: T): T = (exp(arg) - exp(-arg)) / 2.0
override fun cosh(arg: T): T = (exp(arg) + exp(-arg)) / 2.0
override fun tanh(arg: T): T = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg))
@ -50,6 +48,11 @@ public interface ExtendedField<T> : ExtendedFieldOps<T>, Field<T>, NumericAlgebr
override fun acosh(arg: T): T = ln(arg + sqrt((arg - one) * (arg + one)))
override fun atanh(arg: T): T = (ln(arg + one) - ln(one - arg)) / 2.0
override fun unaryOperationFunction(operation: String): (arg: T) -> T {
return if (operation == PowerOperations.SQRT_OPERATION) ::sqrt
else super<ExtendedFieldOps>.unaryOperationFunction(operation)
}
override fun rightSideNumberOperationFunction(operation: String): (left: T, right: Number) -> T =
when (operation) {
PowerOperations.POW_OPERATION -> ::power
@ -69,7 +72,7 @@ public object DoubleField : ExtendedField<Double>, Norm<Double, Double>, ScaleOp
override fun binaryOperationFunction(operation: String): (left: Double, right: Double) -> Double =
when (operation) {
PowerOperations.POW_OPERATION -> ::power
PowerOperations.POW_OPERATION -> { l, r -> l.kpow(r) }
else -> super<ExtendedField>.binaryOperationFunction(operation)
}
@ -94,18 +97,23 @@ public object DoubleField : ExtendedField<Double>, Norm<Double, Double>, ScaleOp
override inline fun acosh(arg: Double): Double = kotlin.math.acosh(arg)
override inline fun atanh(arg: Double): Double = kotlin.math.atanh(arg)
override inline fun sqrt(arg: Double): Double = kotlin.math.sqrt(arg)
override inline fun power(arg: Double, pow: Number): Double = arg.kpow(pow.toDouble())
override fun sqrt(arg: Double): Double = kotlin.math.sqrt(arg)
override fun power(arg: Double, pow: Number): Double = when {
pow.isInteger() -> arg.kpow(pow.toInt())
arg < 0 -> throw IllegalArgumentException("Can't raise negative $arg to a fractional power $pow")
else -> arg.kpow(pow.toDouble())
}
override inline fun exp(arg: Double): Double = kotlin.math.exp(arg)
override inline fun ln(arg: Double): Double = kotlin.math.ln(arg)
override inline fun norm(arg: Double): Double = abs(arg)
override inline fun Double.unaryMinus(): Double = -this
override inline fun Double.plus(other: Double): Double = this + other
override inline fun Double.minus(other: Double): Double = this - other
override inline fun Double.times(other: Double): Double = this * other
override inline fun Double.div(other: Double): Double = this / other
override inline fun Double.plus(arg: Double): Double = this + arg
override inline fun Double.minus(arg: Double): Double = this - arg
override inline fun Double.times(arg: Double): Double = this * arg
override inline fun Double.div(arg: Double): Double = this / arg
}
public val Double.Companion.algebra: DoubleField get() = DoubleField
@ -122,7 +130,7 @@ public object FloatField : ExtendedField<Float>, Norm<Float, Float> {
override fun binaryOperationFunction(operation: String): (left: Float, right: Float) -> Float =
when (operation) {
PowerOperations.POW_OPERATION -> ::power
PowerOperations.POW_OPERATION -> { l, r -> l.kpow(r) }
else -> super.binaryOperationFunction(operation)
}
@ -149,16 +157,17 @@ public object FloatField : ExtendedField<Float>, Norm<Float, Float> {
override inline fun sqrt(arg: Float): Float = kotlin.math.sqrt(arg)
override inline fun power(arg: Float, pow: Number): Float = arg.kpow(pow.toFloat())
override inline fun exp(arg: Float): Float = kotlin.math.exp(arg)
override inline fun ln(arg: Float): Float = kotlin.math.ln(arg)
override inline fun norm(arg: Float): Float = abs(arg)
override inline fun Float.unaryMinus(): Float = -this
override inline fun Float.plus(other: Float): Float = this + other
override inline fun Float.minus(other: Float): Float = this - other
override inline fun Float.times(other: Float): Float = this * other
override inline fun Float.div(other: Float): Float = this / other
override inline fun Float.plus(arg: Float): Float = this + arg
override inline fun Float.minus(arg: Float): Float = this - arg
override inline fun Float.times(arg: Float): Float = this * arg
override inline fun Float.div(arg: Float): Float = this / arg
}
public val Float.Companion.algebra: FloatField get() = FloatField
@ -180,9 +189,9 @@ public object IntRing : Ring<Int>, Norm<Int, Int>, NumericAlgebra<Int> {
override inline fun norm(arg: Int): Int = abs(arg)
override inline fun Int.unaryMinus(): Int = -this
override inline fun Int.plus(other: Int): Int = this + other
override inline fun Int.minus(other: Int): Int = this - other
override inline fun Int.times(other: Int): Int = this * other
override inline fun Int.plus(arg: Int): Int = this + arg
override inline fun Int.minus(arg: Int): Int = this - arg
override inline fun Int.times(arg: Int): Int = this * arg
}
public val Int.Companion.algebra: IntRing get() = IntRing
@ -204,9 +213,9 @@ public object ShortRing : Ring<Short>, Norm<Short, Short>, NumericAlgebra<Short>
override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort()
override inline fun Short.unaryMinus(): Short = (-this).toShort()
override inline fun Short.plus(other: Short): Short = (this + other).toShort()
override inline fun Short.minus(other: Short): Short = (this - other).toShort()
override inline fun Short.times(other: Short): Short = (this * other).toShort()
override inline fun Short.plus(arg: Short): Short = (this + arg).toShort()
override inline fun Short.minus(arg: Short): Short = (this - arg).toShort()
override inline fun Short.times(arg: Short): Short = (this * arg).toShort()
}
public val Short.Companion.algebra: ShortRing get() = ShortRing
@ -230,7 +239,7 @@ public object ByteRing : Ring<Byte>, Norm<Byte, Byte>, NumericAlgebra<Byte> {
override inline fun Byte.unaryMinus(): Byte = (-this).toByte()
override inline fun Byte.plus(arg: Byte): Byte = (this + arg).toByte()
override inline fun Byte.minus(arg: Byte): Byte = (this - arg).toByte()
override inline fun Byte.times(other: Byte): Byte = (this * other).toByte()
override inline fun Byte.times(arg: Byte): Byte = (this * arg).toByte()
}
public val Byte.Companion.algebra: ByteRing get() = ByteRing
@ -252,9 +261,9 @@ public object LongRing : Ring<Long>, Norm<Long, Long>, NumericAlgebra<Long> {
override fun norm(arg: Long): Long = abs(arg)
override inline fun Long.unaryMinus(): Long = (-this)
override inline fun Long.plus(other: Long): Long = (this + other)
override inline fun Long.minus(other: Long): Long = (this - other)
override inline fun Long.times(other: Long): Long = (this * other)
override inline fun Long.plus(arg: Long): Long = (this + arg)
override inline fun Long.minus(arg: Long): Long = (this - arg)
override inline fun Long.times(arg: Long): Long = (this * arg)
}
public val Long.Companion.algebra: LongRing get() = LongRing

View File

@ -0,0 +1,6 @@
package space.kscience.kmath.operations
/**
* Check if number is an integer
*/
public actual fun Number.isInteger(): Boolean = js("Number").isInteger(this) as Boolean

View File

@ -0,0 +1,6 @@
package space.kscience.kmath.operations
/**
* Check if number is an integer
*/
public actual fun Number.isInteger(): Boolean = (this is Int) || (this is Long) || (this is Short) || (this.toDouble() % 1 == 0.0)

View File

@ -0,0 +1,6 @@
package space.kscience.kmath.operations
/**
* Check if number is an integer
*/
public actual fun Number.isInteger(): Boolean = (this is Int) || (this is Long) || (this is Short) || (this.toDouble() % 1 == 0.0)

View File

@ -59,8 +59,8 @@ public object JafamaDoubleField : ExtendedField<Double>, Norm<Double, Double>, S
override inline fun Double.unaryMinus(): Double = -this
override inline fun Double.plus(arg: Double): Double = this + arg
override inline fun Double.minus(arg: Double): Double = this - arg
override inline fun Double.times(other: Double): Double = this * other
override inline fun Double.div(other: Double): Double = this / other
override inline fun Double.times(arg: Double): Double = this * arg
override inline fun Double.div(arg: Double): Double = this / arg
}
/**
@ -108,8 +108,8 @@ public object StrictJafamaDoubleField : ExtendedField<Double>, Norm<Double, Doub
override inline fun norm(arg: Double): Double = StrictFastMath.abs(arg)
override inline fun Double.unaryMinus(): Double = -this
override inline fun Double.plus(other: Double): Double = this + other
override inline fun Double.minus(other: Double): Double = this - other
override inline fun Double.times(other: Double): Double = this * other
override inline fun Double.div(other: Double): Double = this / other
override inline fun Double.plus(arg: Double): Double = this + arg
override inline fun Double.minus(arg: Double): Double = this - arg
override inline fun Double.times(arg: Double): Double = this * arg
override inline fun Double.div(arg: Double): Double = this / arg
}

View File

@ -62,6 +62,6 @@ internal class AdaptingTests {
.parseMath()
.compileToExpression(DoubleField)
assertEquals(actualDerivative(x to 0.1), expectedDerivative(x to 0.1))
assertEquals(actualDerivative(x to -0.1), expectedDerivative(x to -0.1))
}
}

View File

@ -0,0 +1,49 @@
package space.kscience.kmath.multik
import org.jetbrains.kotlinx.multik.ndarray.data.DataType
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.ExponentialOperations
import space.kscience.kmath.operations.TrigonometricOperations
public object MultikDoubleAlgebra : MultikDivisionTensorAlgebra<Double, DoubleField>(),
TrigonometricOperations<StructureND<Double>>, ExponentialOperations<StructureND<Double>> {
override val elementAlgebra: DoubleField get() = DoubleField
override val type: DataType get() = DataType.DoubleDataType
override fun sin(arg: StructureND<Double>): MultikTensor<Double> = multikMath.mathEx.sin(arg.asMultik().array).wrap()
override fun cos(arg: StructureND<Double>): MultikTensor<Double> = multikMath.mathEx.cos(arg.asMultik().array).wrap()
override fun tan(arg: StructureND<Double>): MultikTensor<Double> = sin(arg) / cos(arg)
override fun asin(arg: StructureND<Double>): MultikTensor<Double> = arg.map { asin(it) }
override fun acos(arg: StructureND<Double>): MultikTensor<Double> = arg.map { acos(it) }
override fun atan(arg: StructureND<Double>): MultikTensor<Double> = arg.map { atan(it) }
override fun exp(arg: StructureND<Double>): MultikTensor<Double> = multikMath.mathEx.exp(arg.asMultik().array).wrap()
override fun ln(arg: StructureND<Double>): MultikTensor<Double> = multikMath.mathEx.log(arg.asMultik().array).wrap()
override fun sinh(arg: StructureND<Double>): MultikTensor<Double> = (exp(arg) - exp(-arg)) / 2.0
override fun cosh(arg: StructureND<Double>): MultikTensor<Double> = (exp(arg) + exp(-arg)) / 2.0
override fun tanh(arg: StructureND<Double>): MultikTensor<Double> {
val expPlus = exp(arg)
val expMinus = exp(-arg)
return (expPlus - expMinus) / (expPlus + expMinus)
}
override fun asinh(arg: StructureND<Double>): MultikTensor<Double> = arg.map { asinh(it) }
override fun acosh(arg: StructureND<Double>): MultikTensor<Double> = arg.map { acosh(it) }
override fun atanh(arg: StructureND<Double>): MultikTensor<Double> = arg.map { atanh(it) }
}
public val Double.Companion.multikAlgebra: MultikTensorAlgebra<Double, DoubleField> get() = MultikDoubleAlgebra
public val DoubleField.multikAlgebra: MultikTensorAlgebra<Double, DoubleField> get() = MultikDoubleAlgebra

View File

@ -7,11 +7,9 @@
package space.kscience.kmath.multik
import org.jetbrains.kotlinx.multik.api.Multik
import org.jetbrains.kotlinx.multik.api.linalg.dot
import org.jetbrains.kotlinx.multik.api.mk
import org.jetbrains.kotlinx.multik.api.ndarrayOf
import org.jetbrains.kotlinx.multik.api.zeros
import org.jetbrains.kotlinx.multik.api.*
import org.jetbrains.kotlinx.multik.api.linalg.LinAlg
import org.jetbrains.kotlinx.multik.api.math.Math
import org.jetbrains.kotlinx.multik.ndarray.data.*
import org.jetbrains.kotlinx.multik.ndarray.operations.*
import space.kscience.kmath.misc.PerformancePitfall
@ -52,10 +50,16 @@ private fun <T, D : Dimension> MultiArray<T, D>.asD2Array(): D2Array<T> {
else throw ClassCastException("Cannot cast MultiArray to NDArray.")
}
public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A> where T : Number, T : Comparable<T> {
public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
where T : Number, T : Comparable<T> {
public abstract val type: DataType
protected val multikMath: Math = mk.math
protected val multikLinAl: LinAlg = mk.linalg
protected val multikStat: Statistics = mk.stat
override fun structureND(shape: Shape, initializer: A.(IntArray) -> T): MultikTensor<T> {
val strides = DefaultStrides(shape)
val memoryView = initMemoryView<T>(strides.linearSize, type)
@ -65,6 +69,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
return MultikTensor(NDArray(memoryView, shape = shape, dim = DN(shape.size)))
}
@OptIn(PerformancePitfall::class)
override fun StructureND<T>.map(transform: A.(T) -> T): MultikTensor<T> = if (this is MultikTensor) {
val data = initMemoryView<T>(array.size, type)
var count = 0
@ -76,6 +81,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
}
}
@OptIn(PerformancePitfall::class)
override fun StructureND<T>.mapIndexed(transform: A.(index: IntArray, T) -> T): MultikTensor<T> =
if (this is MultikTensor) {
val array = asMultik().array
@ -96,6 +102,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
}
}
@OptIn(PerformancePitfall::class)
override fun zip(left: StructureND<T>, right: StructureND<T>, transform: A.(T, T) -> T): MultikTensor<T> {
require(left.shape.contentEquals(right.shape)) { "ND array shape mismatch" } //TODO replace by ShapeMismatchException
val leftArray = left.asMultik().array
@ -208,9 +215,9 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
override fun StructureND<T>.unaryMinus(): MultikTensor<T> =
asMultik().array.unaryMinus().wrap()
override fun StructureND<T>.get(i: Int): MultikTensor<T> = asMultik().array.mutableView(i).wrap()
override fun Tensor<T>.get(i: Int): MultikTensor<T> = asMultik().array.mutableView(i).wrap()
override fun StructureND<T>.transpose(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap()
override fun Tensor<T>.transpose(i: Int, j: Int): MultikTensor<T> = asMultik().array.transpose(i, j).wrap()
override fun Tensor<T>.view(shape: IntArray): MultikTensor<T> {
require(shape.all { it > 0 })
@ -236,12 +243,12 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
override fun StructureND<T>.dot(other: StructureND<T>): MultikTensor<T> =
if (this.shape.size == 1 && other.shape.size == 1) {
Multik.ndarrayOf(
asMultik().array.asD1Array() dot other.asMultik().array.asD1Array()
).asDNArray().wrap()
multikLinAl.linAlgEx.dotVV(asMultik().array.asD1Array(), other.asMultik().array.asD1Array())
).wrap()
} else if (this.shape.size == 2 && other.shape.size == 2) {
(asMultik().array.asD2Array() dot other.asMultik().array.asD2Array()).asDNArray().wrap()
multikLinAl.linAlgEx.dotMM(asMultik().array.asD2Array(), other.asMultik().array.asD2Array()).wrap()
} else if (this.shape.size == 2 && other.shape.size == 1) {
(asMultik().array.asD2Array() dot other.asMultik().array.asD1Array()).asDNArray().wrap()
multikLinAl.linAlgEx.dotMV(asMultik().array.asD2Array(), other.asMultik().array.asD1Array()).wrap()
} else {
TODO("Not implemented for broadcasting")
}
@ -270,7 +277,7 @@ public abstract class MultikTensorAlgebra<T, A : Ring<T>> : TensorAlgebra<T, A>
TODO("Not yet implemented")
}
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<T> {
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<Int> {
TODO("Not yet implemented")
}
}
@ -294,23 +301,15 @@ public abstract class MultikDivisionTensorAlgebra<T, A : Field<T>>
}
}
override fun Tensor<T>.divAssign(other: StructureND<T>) {
override fun Tensor<T>.divAssign(arg: StructureND<T>) {
if (this is MultikTensor) {
array.divAssign(other.asMultik().array)
array.divAssign(arg.asMultik().array)
} else {
mapInPlace { index, t -> elementAlgebra.divide(t, other[index]) }
mapInPlace { index, t -> elementAlgebra.divide(t, arg[index]) }
}
}
}
public object MultikDoubleAlgebra : MultikDivisionTensorAlgebra<Double, DoubleField>() {
override val elementAlgebra: DoubleField get() = DoubleField
override val type: DataType get() = DataType.DoubleDataType
}
public val Double.Companion.multikAlgebra: MultikTensorAlgebra<Double, DoubleField> get() = MultikDoubleAlgebra
public val DoubleField.multikAlgebra: MultikTensorAlgebra<Double, DoubleField> get() = MultikDoubleAlgebra
public object MultikFloatAlgebra : MultikDivisionTensorAlgebra<Float, FloatField>() {
override val elementAlgebra: FloatField get() = FloatField
override val type: DataType get() = DataType.FloatDataType

View File

@ -155,7 +155,7 @@ public sealed interface Nd4jArrayField<T, out F : Field<T>> : FieldOpsND<T, F>,
* Represents intersection of [ExtendedField] and [Field] over [Nd4jArrayStructure].
*/
public sealed interface Nd4jArrayExtendedFieldOps<T, out F : ExtendedField<T>> :
ExtendedFieldOps<StructureND<T>>, Nd4jArrayField<T, F> {
ExtendedFieldOps<StructureND<T>>, Nd4jArrayField<T, F>, PowerOperations<StructureND<T>> {
override fun sin(arg: StructureND<T>): StructureND<T> = Transforms.sin(arg.ndArray).wrap()
override fun cos(arg: StructureND<T>): StructureND<T> = Transforms.cos(arg.ndArray).wrap()

View File

@ -92,8 +92,8 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
}
override fun StructureND<T>.unaryMinus(): Nd4jArrayStructure<T> = ndArray.neg().wrap()
override fun StructureND<T>.get(i: Int): Nd4jArrayStructure<T> = ndArray.slice(i.toLong()).wrap()
override fun StructureND<T>.transpose(i: Int, j: Int): Nd4jArrayStructure<T> = ndArray.swapAxes(i, j).wrap()
override fun Tensor<T>.get(i: Int): Nd4jArrayStructure<T> = ndArray.slice(i.toLong()).wrap()
override fun Tensor<T>.transpose(i: Int, j: Int): Nd4jArrayStructure<T> = ndArray.swapAxes(i, j).wrap()
override fun StructureND<T>.dot(other: StructureND<T>): Nd4jArrayStructure<T> = ndArray.mmul(other.ndArray).wrap()
override fun StructureND<T>.min(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
@ -108,8 +108,8 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
override fun Tensor<T>.view(shape: IntArray): Nd4jArrayStructure<T> = ndArray.reshape(shape).wrap()
override fun Tensor<T>.viewAs(other: StructureND<T>): Nd4jArrayStructure<T> = view(other.shape)
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
ndBase.get().argmax(ndArray, keepDim, dim).wrap()
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<Int> =
ndBase.get().argmax(ndArray, keepDim, dim).asIntStructure()
override fun StructureND<T>.mean(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =
ndArray.mean(keepDim, dim).wrap()
@ -148,8 +148,8 @@ public sealed interface Nd4jTensorAlgebra<T : Number, A : Field<T>> : AnalyticTe
ndArray.divi(value)
}
override fun Tensor<T>.divAssign(other: StructureND<T>) {
ndArray.divi(other.ndArray)
override fun Tensor<T>.divAssign(arg: StructureND<T>) {
ndArray.divi(arg.ndArray)
}
override fun StructureND<T>.variance(dim: Int, keepDim: Boolean): Nd4jArrayStructure<T> =

View File

@ -6,6 +6,7 @@
package space.kscience.kmath.tensors.api
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.ExtendedFieldOps
import space.kscience.kmath.operations.Field
@ -14,7 +15,8 @@ import space.kscience.kmath.operations.Field
*
* @param T the type of items closed under analytic functions in the tensors.
*/
public interface AnalyticTensorAlgebra<T, A : Field<T>> : TensorPartialDivisionAlgebra<T, A> {
public interface AnalyticTensorAlgebra<T, A : Field<T>> :
TensorPartialDivisionAlgebra<T, A>, ExtendedFieldOps<StructureND<T>> {
/**
* @return the mean of all elements in the input tensor.
@ -121,4 +123,27 @@ public interface AnalyticTensorAlgebra<T, A : Field<T>> : TensorPartialDivisionA
//For information: https://pytorch.org/docs/stable/generated/torch.floor.html#torch.floor
public fun StructureND<T>.floor(): Tensor<T>
override fun sin(arg: StructureND<T>): StructureND<T> = arg.sin()
override fun cos(arg: StructureND<T>): StructureND<T> = arg.cos()
override fun asin(arg: StructureND<T>): StructureND<T> = arg.asin()
override fun acos(arg: StructureND<T>): StructureND<T> = arg.acos()
override fun atan(arg: StructureND<T>): StructureND<T> = arg.atan()
override fun exp(arg: StructureND<T>): StructureND<T> = arg.exp()
override fun ln(arg: StructureND<T>): StructureND<T> = arg.ln()
override fun sinh(arg: StructureND<T>): StructureND<T> = arg.sinh()
override fun cosh(arg: StructureND<T>): StructureND<T> = arg.cosh()
override fun asinh(arg: StructureND<T>): StructureND<T> = arg.asinh()
override fun acosh(arg: StructureND<T>): StructureND<T> = arg.acosh()
override fun atanh(arg: StructureND<T>): StructureND<T> = arg.atanh()
}

View File

@ -166,7 +166,7 @@ public interface TensorAlgebra<T, A : Ring<T>> : RingOpsND<T, A> {
* @param i index of the extractable tensor
* @return subtensor of the original tensor with index [i]
*/
public operator fun StructureND<T>.get(i: Int): Tensor<T>
public operator fun Tensor<T>.get(i: Int): Tensor<T>
/**
* Returns a tensor that is a transposed version of this tensor. The given dimensions [i] and [j] are swapped.
@ -176,7 +176,7 @@ public interface TensorAlgebra<T, A : Ring<T>> : RingOpsND<T, A> {
* @param j the second dimension to be transposed
* @return transposed tensor
*/
public fun StructureND<T>.transpose(i: Int = -2, j: Int = -1): Tensor<T>
public fun Tensor<T>.transpose(i: Int = -2, j: Int = -1): Tensor<T>
/**
* Returns a new tensor with the same data as the self tensor but of a different shape.
@ -324,7 +324,7 @@ public interface TensorAlgebra<T, A : Ring<T>> : RingOpsND<T, A> {
* @param keepDim whether the output tensor has [dim] retained or not.
* @return the index of maximum value of each row of the input tensor in the given dimension [dim].
*/
public fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<T>
public fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<Int>
override fun add(left: StructureND<T>, right: StructureND<T>): Tensor<T> = left + right

View File

@ -53,9 +53,9 @@ public interface TensorPartialDivisionAlgebra<T, A : Field<T>> : TensorAlgebra<T
public operator fun Tensor<T>.divAssign(value: T)
/**
* Each element of this tensor is divided by each element of the [other] tensor.
* Each element of this tensor is divided by each element of the [arg] tensor.
*
* @param other tensor to be divided by.
* @param arg tensor to be divided by.
*/
public operator fun Tensor<T>.divAssign(other: StructureND<T>)
public operator fun Tensor<T>.divAssign(arg: StructureND<T>)
}

View File

@ -5,6 +5,7 @@
package space.kscience.kmath.tensors.core
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.tensors.api.Tensor
@ -17,6 +18,8 @@ import space.kscience.kmath.tensors.core.internal.tensor
* Basic linear algebra operations implemented with broadcasting.
* For more information: https://pytorch.org/docs/stable/notes/broadcasting.html
*/
@PerformancePitfall
public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
override fun StructureND<Double>.plus(arg: StructureND<Double>): DoubleTensor {
@ -74,8 +77,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
}
}
override fun StructureND<Double>.div(other: StructureND<Double>): DoubleTensor {
val broadcast = broadcastTensors(tensor, other.tensor)
override fun StructureND<Double>.div(arg: StructureND<Double>): DoubleTensor {
val broadcast = broadcastTensors(tensor, arg.tensor)
val newThis = broadcast[0]
val newOther = broadcast[1]
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
@ -85,8 +88,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
return DoubleTensor(newThis.shape, resBuffer)
}
override fun Tensor<Double>.divAssign(other: StructureND<Double>) {
val newOther = broadcastTo(other.tensor, tensor.shape)
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
val newOther = broadcastTo(arg.tensor, tensor.shape)
for (i in 0 until tensor.indices.linearSize) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
newOther.mutableBuffer.array()[tensor.bufferStart + i]
@ -99,5 +102,6 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
* Compute a value using broadcast double tensor algebra
*/
@UnstableKMathAPI
@PerformancePitfall
public fun <R> DoubleTensorAlgebra.withBroadcast(block: BroadcastDoubleTensorAlgebra.() -> R): R =
BroadcastDoubleTensorAlgebra.block()

View File

@ -9,7 +9,6 @@ import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Strides
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.tensors.api.Tensor
import space.kscience.kmath.tensors.core.internal.TensorLinearStructure
/**
* Represents [Tensor] over a [MutableBuffer] intended to be used through [DoubleTensor] and [IntTensor]

View File

@ -3,13 +3,18 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
@file:OptIn(PerformancePitfall::class)
package space.kscience.kmath.tensors.core
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.MutableStructure2D
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.as1D
import space.kscience.kmath.nd.as2D
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.structures.MutableBuffer
import space.kscience.kmath.structures.indices
import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra
import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra
@ -38,6 +43,7 @@ public open class DoubleTensorAlgebra :
* @param transform the function to be applied to each element of the tensor.
* @return the resulting tensor after applying the function.
*/
@PerformancePitfall
@Suppress("OVERRIDE_BY_INLINE")
final override inline fun StructureND<Double>.map(transform: DoubleField.(Double) -> Double): DoubleTensor {
val tensor = this.tensor
@ -51,6 +57,7 @@ public open class DoubleTensorAlgebra :
)
}
@PerformancePitfall
@Suppress("OVERRIDE_BY_INLINE")
final override inline fun StructureND<Double>.mapIndexed(transform: DoubleField.(index: IntArray, Double) -> Double): DoubleTensor {
val tensor = this.tensor
@ -64,10 +71,11 @@ public open class DoubleTensorAlgebra :
)
}
@PerformancePitfall
override fun zip(
left: StructureND<Double>,
right: StructureND<Double>,
transform: DoubleField.(Double, Double) -> Double
transform: DoubleField.(Double, Double) -> Double,
): DoubleTensor {
require(left.shape.contentEquals(right.shape)) {
"The shapes in zip are not equal: left - ${left.shape}, right - ${right.shape}"
@ -115,7 +123,7 @@ public open class DoubleTensorAlgebra :
TensorLinearStructure(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray()
)
override operator fun StructureND<Double>.get(i: Int): DoubleTensor {
override operator fun Tensor<Double>.get(i: Int): DoubleTensor {
val lastShape = tensor.shape.drop(1).toIntArray()
val newShape = if (lastShape.isNotEmpty()) lastShape else intArrayOf(1)
val newStart = newShape.reduce(Int::times) * i + tensor.bufferStart
@ -314,11 +322,11 @@ public open class DoubleTensorAlgebra :
return DoubleTensor(shape, resBuffer)
}
override fun StructureND<Double>.div(other: StructureND<Double>): DoubleTensor {
checkShapesCompatible(tensor, other)
override fun StructureND<Double>.div(arg: StructureND<Double>): DoubleTensor {
checkShapesCompatible(tensor, arg)
val resBuffer = DoubleArray(tensor.numElements) { i ->
tensor.mutableBuffer.array()[other.tensor.bufferStart + i] /
other.tensor.mutableBuffer.array()[other.tensor.bufferStart + i]
tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] /
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
}
return DoubleTensor(tensor.shape, resBuffer)
}
@ -329,11 +337,11 @@ public open class DoubleTensorAlgebra :
}
}
override fun Tensor<Double>.divAssign(other: StructureND<Double>) {
checkShapesCompatible(tensor, other)
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
checkShapesCompatible(tensor, arg)
for (i in 0 until tensor.numElements) {
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
other.tensor.mutableBuffer.array()[tensor.bufferStart + i]
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
}
}
@ -344,7 +352,7 @@ public open class DoubleTensorAlgebra :
return DoubleTensor(tensor.shape, resBuffer)
}
override fun StructureND<Double>.transpose(i: Int, j: Int): DoubleTensor {
override fun Tensor<Double>.transpose(i: Int, j: Int): DoubleTensor {
val ii = tensor.minusIndex(i)
val jj = tensor.minusIndex(j)
checkTranspose(tensor.dimension, ii, jj)
@ -376,6 +384,7 @@ public open class DoubleTensorAlgebra :
override fun Tensor<Double>.viewAs(other: StructureND<Double>): DoubleTensor =
tensor.view(other.shape)
@PerformancePitfall
override infix fun StructureND<Double>.dot(other: StructureND<Double>): DoubleTensor {
if (tensor.shape.size == 1 && other.shape.size == 1) {
return DoubleTensor(intArrayOf(1), doubleArrayOf(tensor.times(other).tensor.mutableBuffer.array().sum()))
@ -413,14 +422,11 @@ public open class DoubleTensorAlgebra :
for ((res, ab) in resTensor.matrixSequence().zip(newThis.matrixSequence().zip(newOther.matrixSequence()))) {
val (a, b) = ab
dotHelper(a.as2D(), b.as2D(), res.as2D(), l, m1, n)
dotTo(a.as2D(), b.as2D(), res.as2D(), l, m1, n)
}
return if (penultimateDim) {
resTensor.view(
resTensor.shape.dropLast(2).toIntArray() +
intArrayOf(resTensor.shape.last())
)
resTensor.view(resTensor.shape.dropLast(2).toIntArray() + intArrayOf(resTensor.shape.last()))
} else if (lastDim) {
resTensor.view(resTensor.shape.dropLast(1).toIntArray())
} else {
@ -432,7 +438,7 @@ public open class DoubleTensorAlgebra :
diagonalEntries: Tensor<Double>,
offset: Int,
dim1: Int,
dim2: Int
dim2: Int,
): DoubleTensor {
val n = diagonalEntries.shape.size
val d1 = minusIndexFrom(n + 1, dim1)
@ -568,14 +574,14 @@ public open class DoubleTensorAlgebra :
*/
public fun Tensor<Double>.rowsByIndices(indices: IntArray): DoubleTensor = stack(indices.map { this[it] })
internal inline fun StructureND<Double>.fold(foldFunction: (DoubleArray) -> Double): Double =
private inline fun StructureND<Double>.fold(foldFunction: (DoubleArray) -> Double): Double =
foldFunction(tensor.copyArray())
internal inline fun StructureND<Double>.foldDim(
foldFunction: (DoubleArray) -> Double,
private inline fun <reified R : Any> StructureND<Double>.foldDim(
dim: Int,
keepDim: Boolean,
): DoubleTensor {
foldFunction: (DoubleArray) -> R,
): BufferedTensor<R> {
check(dim < dimension) { "Dimension $dim out of range $dimension" }
val resShape = if (keepDim) {
shape.take(dim).toIntArray() + intArrayOf(1) + shape.takeLast(dimension - dim - 1).toIntArray()
@ -583,80 +589,75 @@ public open class DoubleTensorAlgebra :
shape.take(dim).toIntArray() + shape.takeLast(dimension - dim - 1).toIntArray()
}
val resNumElements = resShape.reduce(Int::times)
val resTensor = DoubleTensor(resShape, DoubleArray(resNumElements) { 0.0 }, 0)
for (index in resTensor.indices.asSequence()) {
val init = foldFunction(DoubleArray(1) { 0.0 })
val resTensor = BufferedTensor(resShape,
MutableBuffer.auto(resNumElements) { init }, 0)
for (index in resTensor.indices) {
val prefix = index.take(dim).toIntArray()
val suffix = index.takeLast(dimension - dim - 1).toIntArray()
resTensor[index] = foldFunction(DoubleArray(shape[dim]) { i ->
tensor[prefix + intArrayOf(i) + suffix]
})
}
return resTensor
}
override fun StructureND<Double>.sum(): Double = tensor.fold { it.sum() }
override fun StructureND<Double>.sum(dim: Int, keepDim: Boolean): DoubleTensor =
foldDim({ x -> x.sum() }, dim, keepDim)
foldDim(dim, keepDim) { x -> x.sum() }.toDoubleTensor()
override fun StructureND<Double>.min(): Double = this.fold { it.minOrNull()!! }
override fun StructureND<Double>.min(dim: Int, keepDim: Boolean): DoubleTensor =
foldDim({ x -> x.minOrNull()!! }, dim, keepDim)
foldDim(dim, keepDim) { x -> x.minOrNull()!! }.toDoubleTensor()
override fun StructureND<Double>.max(): Double = this.fold { it.maxOrNull()!! }
override fun StructureND<Double>.max(dim: Int, keepDim: Boolean): DoubleTensor =
foldDim({ x -> x.maxOrNull()!! }, dim, keepDim)
foldDim(dim, keepDim) { x -> x.maxOrNull()!! }.toDoubleTensor()
override fun StructureND<Double>.argMax(dim: Int, keepDim: Boolean): DoubleTensor =
foldDim({ x ->
x.withIndex().maxByOrNull { it.value }?.index!!.toDouble()
}, dim, keepDim)
override fun StructureND<Double>.argMax(dim: Int, keepDim: Boolean): IntTensor =
foldDim(dim, keepDim) { x ->
x.withIndex().maxByOrNull { it.value }?.index!!
}.toIntTensor()
override fun StructureND<Double>.mean(): Double = this.fold { it.sum() / tensor.numElements }
override fun StructureND<Double>.mean(dim: Int, keepDim: Boolean): DoubleTensor =
foldDim(
{ arr ->
override fun StructureND<Double>.mean(dim: Int, keepDim: Boolean): DoubleTensor = foldDim(dim, keepDim) { arr ->
check(dim < dimension) { "Dimension $dim out of range $dimension" }
arr.sum() / shape[dim]
},
dim,
keepDim
)
}.toDoubleTensor()
override fun StructureND<Double>.std(): Double = this.fold { arr ->
override fun StructureND<Double>.std(): Double = fold { arr ->
val mean = arr.sum() / tensor.numElements
sqrt(arr.sumOf { (it - mean) * (it - mean) } / (tensor.numElements - 1))
}
override fun StructureND<Double>.std(dim: Int, keepDim: Boolean): DoubleTensor = foldDim(
{ arr ->
dim,
keepDim
) { arr ->
check(dim < dimension) { "Dimension $dim out of range $dimension" }
val mean = arr.sum() / shape[dim]
sqrt(arr.sumOf { (it - mean) * (it - mean) } / (shape[dim] - 1))
},
dim,
keepDim
)
}.toDoubleTensor()
override fun StructureND<Double>.variance(): Double = this.fold { arr ->
override fun StructureND<Double>.variance(): Double = fold { arr ->
val mean = arr.sum() / tensor.numElements
arr.sumOf { (it - mean) * (it - mean) } / (tensor.numElements - 1)
}
override fun StructureND<Double>.variance(dim: Int, keepDim: Boolean): DoubleTensor = foldDim(
{ arr ->
dim,
keepDim
) { arr ->
check(dim < dimension) { "Dimension $dim out of range $dimension" }
val mean = arr.sum() / shape[dim]
arr.sumOf { (it - mean) * (it - mean) } / (shape[dim] - 1)
},
dim,
keepDim
)
}.toDoubleTensor()
private fun cov(x: DoubleTensor, y: DoubleTensor): Double {
val n = x.shape[0]

View File

@ -6,6 +6,7 @@
package space.kscience.kmath.tensors.core
import space.kscience.kmath.structures.IntBuffer
import space.kscience.kmath.tensors.core.internal.array
/**
* Default [BufferedTensor] implementation for [Int] values
@ -14,4 +15,7 @@ public class IntTensor internal constructor(
shape: IntArray,
buffer: IntArray,
offset: Int = 0
) : BufferedTensor<Int>(shape, IntBuffer(buffer), offset)
) : BufferedTensor<Int>(shape, IntBuffer(buffer), offset){
public fun asDouble() : DoubleTensor =
DoubleTensor(shape, mutableBuffer.array().map{ it.toDouble()}.toDoubleArray(), bufferStart)
}

View File

@ -0,0 +1,74 @@
/*
* 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.tensors.core
import space.kscience.kmath.nd.Strides
import kotlin.math.max
/**
* This [Strides] implementation follows the last dimension first convention
* For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html
*
* @param shape the shape of the tensor.
*/
public class TensorLinearStructure(override val shape: IntArray) : Strides() {
override val strides: IntArray
get() = stridesFromShape(shape)
override fun index(offset: Int): IntArray =
indexFromOffset(offset, strides, shape.size)
override val linearSize: Int
get() = shape.reduce(Int::times)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as TensorLinearStructure
if (!shape.contentEquals(other.shape)) return false
return true
}
override fun hashCode(): Int {
return shape.contentHashCode()
}
public companion object {
public fun stridesFromShape(shape: IntArray): IntArray {
val nDim = shape.size
val res = IntArray(nDim)
if (nDim == 0)
return res
var current = nDim - 1
res[current] = 1
while (current > 0) {
res[current - 1] = max(1, shape[current]) * res[current]
current--
}
return res
}
public fun indexFromOffset(offset: Int, strides: IntArray, nDim: Int): IntArray {
val res = IntArray(nDim)
var current = offset
var strideIndex = 0
while (strideIndex < nDim) {
res[strideIndex] = (current / strides[strideIndex])
current %= strides[strideIndex]
strideIndex++
}
return res
}
}
}

View File

@ -1,71 +0,0 @@
/*
* 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 file.
*/
package space.kscience.kmath.tensors.core.internal
import space.kscience.kmath.nd.Strides
import kotlin.math.max
internal fun stridesFromShape(shape: IntArray): IntArray {
val nDim = shape.size
val res = IntArray(nDim)
if (nDim == 0)
return res
var current = nDim - 1
res[current] = 1
while (current > 0) {
res[current - 1] = max(1, shape[current]) * res[current]
current--
}
return res
}
internal fun indexFromOffset(offset: Int, strides: IntArray, nDim: Int): IntArray {
val res = IntArray(nDim)
var current = offset
var strideIndex = 0
while (strideIndex < nDim) {
res[strideIndex] = (current / strides[strideIndex])
current %= strides[strideIndex]
strideIndex++
}
return res
}
/**
* This [Strides] implementation follows the last dimension first convention
* For more information: https://numpy.org/doc/stable/reference/generated/numpy.ndarray.strides.html
*
* @param shape the shape of the tensor.
*/
internal class TensorLinearStructure(override val shape: IntArray) : Strides() {
override val strides: IntArray
get() = stridesFromShape(shape)
override fun index(offset: Int): IntArray =
indexFromOffset(offset, strides, shape.size)
override val linearSize: Int
get() = shape.reduce(Int::times)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as TensorLinearStructure
if (!shape.contentEquals(other.shape)) return false
return true
}
override fun hashCode(): Int {
return shape.contentHashCode()
}
}

View File

@ -53,7 +53,7 @@ internal val <T> BufferedTensor<T>.matrices: VirtualBuffer<BufferedTensor<T>>
internal fun <T> BufferedTensor<T>.matrixSequence(): Sequence<BufferedTensor<T>> = matrices.asSequence()
internal fun dotHelper(
internal fun dotTo(
a: MutableStructure2D<Double>,
b: MutableStructure2D<Double>,
res: MutableStructure2D<Double>,

View File

@ -12,6 +12,7 @@ import space.kscience.kmath.tensors.api.Tensor
import space.kscience.kmath.tensors.core.BufferedTensor
import space.kscience.kmath.tensors.core.DoubleTensor
import space.kscience.kmath.tensors.core.IntTensor
import space.kscience.kmath.tensors.core.TensorLinearStructure
internal fun BufferedTensor<Int>.asTensor(): IntTensor =
IntTensor(this.shape, this.mutableBuffer.array(), this.bufferStart)

View File

@ -3,8 +3,11 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
@file:OptIn(PerformancePitfall::class)
package space.kscience.kmath.tensors.core
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.Shape
import kotlin.jvm.JvmName

View File

@ -6,17 +6,20 @@
package space.kscience.kmath.viktor
import org.jetbrains.bio.viktor.F64Array
import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.*
import space.kscience.kmath.operations.DoubleField
import space.kscience.kmath.operations.ExtendedFieldOps
import space.kscience.kmath.operations.NumbersAddOps
import space.kscience.kmath.operations.PowerOperations
@OptIn(UnstableKMathAPI::class)
@Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE")
public open class ViktorFieldOpsND :
FieldOpsND<Double, DoubleField>,
ExtendedFieldOps<StructureND<Double>> {
ExtendedFieldOps<StructureND<Double>>,
PowerOperations<StructureND<Double>> {
public val StructureND<Double>.f64Buffer: F64Array
get() = when (this) {
@ -35,6 +38,7 @@ public open class ViktorFieldOpsND :
override fun StructureND<Double>.unaryMinus(): StructureND<Double> = -1 * this
@PerformancePitfall
override fun StructureND<Double>.map(transform: DoubleField.(Double) -> Double): ViktorStructureND =
F64Array(*shape).apply {
DefaultStrides(shape).asSequence().forEach { index ->
@ -42,6 +46,7 @@ public open class ViktorFieldOpsND :
}
}.asStructure()
@PerformancePitfall
override fun StructureND<Double>.mapIndexed(
transform: DoubleField.(index: IntArray, Double) -> Double,
): ViktorStructureND = F64Array(*shape).apply {
@ -50,6 +55,7 @@ public open class ViktorFieldOpsND :
}
}.asStructure()
@PerformancePitfall
override fun zip(
left: StructureND<Double>,
right: StructureND<Double>,
@ -110,7 +116,7 @@ public open class ViktorFieldOpsND :
public val DoubleField.viktorAlgebra: ViktorFieldOpsND get() = ViktorFieldOpsND
public open class ViktorFieldND(
override val shape: Shape
override val shape: Shape,
) : ViktorFieldOpsND(), FieldND<Double, DoubleField>, NumbersAddOps<StructureND<Double>> {
override val zero: ViktorStructureND by lazy { F64Array.full(init = 0.0, shape = shape).asStructure() }
override val one: ViktorStructureND by lazy { F64Array.full(init = 1.0, shape = shape).asStructure() }