Merge pull request #1 from margarita0303/feature/tensors-performance
Feature/tensors performance Adding SVDGolubKahan
This commit is contained in:
commit
87022d771b
@ -84,6 +84,11 @@ benchmark {
|
|||||||
iterationTimeUnit = "ms"
|
iterationTimeUnit = "ms"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configurations.register("svd") {
|
||||||
|
commonConfiguration()
|
||||||
|
include("SVDBenchmark")
|
||||||
|
}
|
||||||
|
|
||||||
configurations.register("buffer") {
|
configurations.register("buffer") {
|
||||||
commonConfiguration()
|
commonConfiguration()
|
||||||
include("BufferBenchmark")
|
include("BufferBenchmark")
|
||||||
|
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* 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.benchmarks
|
||||||
|
import kotlinx.benchmark.Benchmark
|
||||||
|
import kotlinx.benchmark.Blackhole
|
||||||
|
import kotlinx.benchmark.Scope
|
||||||
|
import kotlinx.benchmark.State
|
||||||
|
import org.ejml.UtilEjml.assertTrue
|
||||||
|
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.diagonalEmbedding
|
||||||
|
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot
|
||||||
|
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.eq
|
||||||
|
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.svdGolubKahan
|
||||||
|
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transpose
|
||||||
|
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||||
|
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.svdPowerMethod
|
||||||
|
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
class SVDBenchmark {
|
||||||
|
companion object {
|
||||||
|
val tensorSmall = DoubleTensorAlgebra.randomNormal(intArrayOf(5, 5), 0)
|
||||||
|
val tensorMedium = DoubleTensorAlgebra.randomNormal(intArrayOf(10, 10), 0)
|
||||||
|
val tensorLarge = DoubleTensorAlgebra.randomNormal(intArrayOf(50, 50), 0)
|
||||||
|
val tensorVeryLarge = DoubleTensorAlgebra.randomNormal(intArrayOf(100, 100), 0)
|
||||||
|
val epsilon = 1e-9
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdPowerMethodSmall(blackhole: Blackhole) {
|
||||||
|
val svd = tensorSmall.svdPowerMethod()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorSmall, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorSmall.svdPowerMethod()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdPowerMethodMedium(blackhole: Blackhole) {
|
||||||
|
val svd = tensorMedium.svdPowerMethod()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorMedium, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorMedium.svdPowerMethod()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdPowerMethodLarge(blackhole: Blackhole) {
|
||||||
|
val svd = tensorLarge.svdPowerMethod()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorLarge, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorLarge.svdPowerMethod()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdPowerMethodVeryLarge(blackhole: Blackhole) {
|
||||||
|
val svd = tensorVeryLarge.svdPowerMethod()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorVeryLarge, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorVeryLarge.svdPowerMethod()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdGolubKahanSmall(blackhole: Blackhole) {
|
||||||
|
val svd = tensorSmall.svdGolubKahan()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorSmall, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorSmall.svdGolubKahan()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdGolubKahanMedium(blackhole: Blackhole) {
|
||||||
|
val svd = tensorMedium.svdGolubKahan()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorMedium, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorMedium.svdGolubKahan()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdGolubKahanLarge(blackhole: Blackhole) {
|
||||||
|
val svd = tensorLarge.svdGolubKahan()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorLarge, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorLarge.svdGolubKahan()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun svdGolubKahanVeryLarge(blackhole: Blackhole) {
|
||||||
|
val svd = tensorVeryLarge.svdGolubKahan()
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
assertTrue(tensorSVD.eq(tensorVeryLarge, epsilon))
|
||||||
|
blackhole.consume(
|
||||||
|
tensorVeryLarge.svdGolubKahan()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
val newThis = broadcast[0]
|
val newThis = broadcast[0]
|
||||||
val newOther = broadcast[1]
|
val newOther = broadcast[1]
|
||||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||||
newThis.mutableBuffer.array()[i] + newOther.mutableBuffer.array()[i]
|
newThis.mutableBuffer[i] + newOther.mutableBuffer[i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(newThis.shape, resBuffer)
|
return DoubleTensor(newThis.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -35,8 +35,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
|
||||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||||
for (i in 0 until tensor.indices.linearSize) {
|
for (i in 0 until tensor.indices.linearSize) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] +=
|
tensor.mutableBuffer[tensor.bufferStart + i] +=
|
||||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
newOther.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
val newThis = broadcast[0]
|
val newThis = broadcast[0]
|
||||||
val newOther = broadcast[1]
|
val newOther = broadcast[1]
|
||||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||||
newThis.mutableBuffer.array()[i] - newOther.mutableBuffer.array()[i]
|
newThis.mutableBuffer[i] - newOther.mutableBuffer[i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(newThis.shape, resBuffer)
|
return DoubleTensor(newThis.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -53,8 +53,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
|
||||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||||
for (i in 0 until tensor.indices.linearSize) {
|
for (i in 0 until tensor.indices.linearSize) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] -=
|
tensor.mutableBuffer[tensor.bufferStart + i] -=
|
||||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
newOther.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,8 +63,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
val newThis = broadcast[0]
|
val newThis = broadcast[0]
|
||||||
val newOther = broadcast[1]
|
val newOther = broadcast[1]
|
||||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||||
newThis.mutableBuffer.array()[newThis.bufferStart + i] *
|
newThis.mutableBuffer[newThis.bufferStart + i] *
|
||||||
newOther.mutableBuffer.array()[newOther.bufferStart + i]
|
newOther.mutableBuffer[newOther.bufferStart + i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(newThis.shape, resBuffer)
|
return DoubleTensor(newThis.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -72,8 +72,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
|
||||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||||
for (i in 0 until tensor.indices.linearSize) {
|
for (i in 0 until tensor.indices.linearSize) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] *=
|
tensor.mutableBuffer[tensor.bufferStart + i] *=
|
||||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
newOther.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,8 +82,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
val newThis = broadcast[0]
|
val newThis = broadcast[0]
|
||||||
val newOther = broadcast[1]
|
val newOther = broadcast[1]
|
||||||
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
val resBuffer = DoubleArray(newThis.indices.linearSize) { i ->
|
||||||
newThis.mutableBuffer.array()[newOther.bufferStart + i] /
|
newThis.mutableBuffer[newOther.bufferStart + i] /
|
||||||
newOther.mutableBuffer.array()[newOther.bufferStart + i]
|
newOther.mutableBuffer[newOther.bufferStart + i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(newThis.shape, resBuffer)
|
return DoubleTensor(newThis.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -91,8 +91,8 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() {
|
|||||||
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
|
||||||
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
val newOther = broadcastTo(arg.tensor, tensor.shape)
|
||||||
for (i in 0 until tensor.indices.linearSize) {
|
for (i in 0 until tensor.indices.linearSize) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
|
tensor.mutableBuffer[tensor.bufferStart + i] /=
|
||||||
newOther.mutableBuffer.array()[tensor.bufferStart + i]
|
newOther.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ public open class DoubleTensorAlgebra :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<Double>.valueOrNull(): Double? = if (tensor.shape contentEquals intArrayOf(1))
|
override fun StructureND<Double>.valueOrNull(): Double? = if (tensor.shape contentEquals intArrayOf(1))
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart] else null
|
tensor.mutableBuffer[tensor.bufferStart] else null
|
||||||
|
|
||||||
override fun StructureND<Double>.value(): Double = valueOrNull()
|
override fun StructureND<Double>.value(): Double = valueOrNull()
|
||||||
?: throw IllegalArgumentException("The tensor shape is $shape, but value method is allowed only for shape [1]")
|
?: throw IllegalArgumentException("The tensor shape is $shape, but value method is allowed only for shape [1]")
|
||||||
@ -208,7 +208,7 @@ public open class DoubleTensorAlgebra :
|
|||||||
|
|
||||||
override fun Double.plus(arg: StructureND<Double>): DoubleTensor {
|
override fun Double.plus(arg: StructureND<Double>): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
||||||
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] + this
|
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i] + this
|
||||||
}
|
}
|
||||||
return DoubleTensor(arg.shape, resBuffer)
|
return DoubleTensor(arg.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -218,35 +218,35 @@ public open class DoubleTensorAlgebra :
|
|||||||
override fun StructureND<Double>.plus(arg: StructureND<Double>): DoubleTensor {
|
override fun StructureND<Double>.plus(arg: StructureND<Double>): DoubleTensor {
|
||||||
checkShapesCompatible(tensor, arg.tensor)
|
checkShapesCompatible(tensor, arg.tensor)
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[i] + arg.tensor.mutableBuffer.array()[i]
|
tensor.mutableBuffer[i] + arg.tensor.mutableBuffer[i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(tensor.shape, resBuffer)
|
return DoubleTensor(tensor.shape, resBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.plusAssign(value: Double) {
|
override fun Tensor<Double>.plusAssign(value: Double) {
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] += value
|
tensor.mutableBuffer[tensor.bufferStart + i] += value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.plusAssign(arg: StructureND<Double>) {
|
||||||
checkShapesCompatible(tensor, arg.tensor)
|
checkShapesCompatible(tensor, arg.tensor)
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] +=
|
tensor.mutableBuffer[tensor.bufferStart + i] +=
|
||||||
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
|
arg.tensor.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Double.minus(arg: StructureND<Double>): DoubleTensor {
|
override fun Double.minus(arg: StructureND<Double>): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
||||||
this - arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
|
this - arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(arg.shape, resBuffer)
|
return DoubleTensor(arg.shape, resBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<Double>.minus(arg: Double): DoubleTensor {
|
override fun StructureND<Double>.minus(arg: Double): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] - arg
|
tensor.mutableBuffer[tensor.bufferStart + i] - arg
|
||||||
}
|
}
|
||||||
return DoubleTensor(tensor.shape, resBuffer)
|
return DoubleTensor(tensor.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -254,28 +254,28 @@ public open class DoubleTensorAlgebra :
|
|||||||
override fun StructureND<Double>.minus(arg: StructureND<Double>): DoubleTensor {
|
override fun StructureND<Double>.minus(arg: StructureND<Double>): DoubleTensor {
|
||||||
checkShapesCompatible(tensor, arg)
|
checkShapesCompatible(tensor, arg)
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[i] - arg.tensor.mutableBuffer.array()[i]
|
tensor.mutableBuffer[i] - arg.tensor.mutableBuffer[i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(tensor.shape, resBuffer)
|
return DoubleTensor(tensor.shape, resBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.minusAssign(value: Double) {
|
override fun Tensor<Double>.minusAssign(value: Double) {
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] -= value
|
tensor.mutableBuffer[tensor.bufferStart + i] -= value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.minusAssign(arg: StructureND<Double>) {
|
||||||
checkShapesCompatible(tensor, arg)
|
checkShapesCompatible(tensor, arg)
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] -=
|
tensor.mutableBuffer[tensor.bufferStart + i] -=
|
||||||
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
|
arg.tensor.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Double.times(arg: StructureND<Double>): DoubleTensor {
|
override fun Double.times(arg: StructureND<Double>): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
||||||
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] * this
|
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i] * this
|
||||||
}
|
}
|
||||||
return DoubleTensor(arg.shape, resBuffer)
|
return DoubleTensor(arg.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -285,36 +285,36 @@ public open class DoubleTensorAlgebra :
|
|||||||
override fun StructureND<Double>.times(arg: StructureND<Double>): DoubleTensor {
|
override fun StructureND<Double>.times(arg: StructureND<Double>): DoubleTensor {
|
||||||
checkShapesCompatible(tensor, arg)
|
checkShapesCompatible(tensor, arg)
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] *
|
tensor.mutableBuffer[tensor.bufferStart + i] *
|
||||||
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
|
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(tensor.shape, resBuffer)
|
return DoubleTensor(tensor.shape, resBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.timesAssign(value: Double) {
|
override fun Tensor<Double>.timesAssign(value: Double) {
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] *= value
|
tensor.mutableBuffer[tensor.bufferStart + i] *= value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.timesAssign(arg: StructureND<Double>) {
|
||||||
checkShapesCompatible(tensor, arg)
|
checkShapesCompatible(tensor, arg)
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] *=
|
tensor.mutableBuffer[tensor.bufferStart + i] *=
|
||||||
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
|
arg.tensor.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Double.div(arg: StructureND<Double>): DoubleTensor {
|
override fun Double.div(arg: StructureND<Double>): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
val resBuffer = DoubleArray(arg.tensor.numElements) { i ->
|
||||||
this / arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
|
this / arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(arg.shape, resBuffer)
|
return DoubleTensor(arg.shape, resBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<Double>.div(arg: Double): DoubleTensor {
|
override fun StructureND<Double>.div(arg: Double): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] / arg
|
tensor.mutableBuffer[tensor.bufferStart + i] / arg
|
||||||
}
|
}
|
||||||
return DoubleTensor(shape, resBuffer)
|
return DoubleTensor(shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -322,29 +322,29 @@ public open class DoubleTensorAlgebra :
|
|||||||
override fun StructureND<Double>.div(arg: StructureND<Double>): DoubleTensor {
|
override fun StructureND<Double>.div(arg: StructureND<Double>): DoubleTensor {
|
||||||
checkShapesCompatible(tensor, arg)
|
checkShapesCompatible(tensor, arg)
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[arg.tensor.bufferStart + i] /
|
tensor.mutableBuffer[arg.tensor.bufferStart + i] /
|
||||||
arg.tensor.mutableBuffer.array()[arg.tensor.bufferStart + i]
|
arg.tensor.mutableBuffer[arg.tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
return DoubleTensor(tensor.shape, resBuffer)
|
return DoubleTensor(tensor.shape, resBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.divAssign(value: Double) {
|
override fun Tensor<Double>.divAssign(value: Double) {
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] /= value
|
tensor.mutableBuffer[tensor.bufferStart + i] /= value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
|
override fun Tensor<Double>.divAssign(arg: StructureND<Double>) {
|
||||||
checkShapesCompatible(tensor, arg)
|
checkShapesCompatible(tensor, arg)
|
||||||
for (i in 0 until tensor.numElements) {
|
for (i in 0 until tensor.numElements) {
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i] /=
|
tensor.mutableBuffer[tensor.bufferStart + i] /=
|
||||||
arg.tensor.mutableBuffer.array()[tensor.bufferStart + i]
|
arg.tensor.mutableBuffer[tensor.bufferStart + i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<Double>.unaryMinus(): DoubleTensor {
|
override fun StructureND<Double>.unaryMinus(): DoubleTensor {
|
||||||
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
val resBuffer = DoubleArray(tensor.numElements) { i ->
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + i].unaryMinus()
|
tensor.mutableBuffer[tensor.bufferStart + i].unaryMinus()
|
||||||
}
|
}
|
||||||
return DoubleTensor(tensor.shape, resBuffer)
|
return DoubleTensor(tensor.shape, resBuffer)
|
||||||
}
|
}
|
||||||
@ -367,8 +367,8 @@ public open class DoubleTensorAlgebra :
|
|||||||
newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] }
|
newMultiIndex[ii] = newMultiIndex[jj].also { newMultiIndex[jj] = newMultiIndex[ii] }
|
||||||
|
|
||||||
val linearIndex = resTensor.indices.offset(newMultiIndex)
|
val linearIndex = resTensor.indices.offset(newMultiIndex)
|
||||||
resTensor.mutableBuffer.array()[linearIndex] =
|
resTensor.mutableBuffer[linearIndex] =
|
||||||
tensor.mutableBuffer.array()[tensor.bufferStart + offset]
|
tensor.mutableBuffer[tensor.bufferStart + offset]
|
||||||
}
|
}
|
||||||
return resTensor
|
return resTensor
|
||||||
}
|
}
|
||||||
@ -833,8 +833,38 @@ public open class DoubleTensorAlgebra :
|
|||||||
return qTensor to rTensor
|
return qTensor to rTensor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun StructureND<Double>.svd(): Triple<DoubleTensor, DoubleTensor, DoubleTensor> =
|
override fun StructureND<Double>.svd(): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
|
||||||
svd(epsilon = 1e-10)
|
return this.svdGolubKahan()
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun StructureND<Double>.svdGolubKahan(iterations: Int = 30, epsilon: Double = 1e-10): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
|
||||||
|
val size = tensor.dimension
|
||||||
|
val commonShape = tensor.shape.sliceArray(0 until size - 2)
|
||||||
|
val (n, m) = tensor.shape.sliceArray(size - 2 until size)
|
||||||
|
val uTensor = zeros(commonShape + intArrayOf(n, m))
|
||||||
|
val sTensor = zeros(commonShape + intArrayOf(m))
|
||||||
|
val vTensor = zeros(commonShape + intArrayOf(m, m))
|
||||||
|
|
||||||
|
val matrices = tensor.matrices
|
||||||
|
val uTensors = uTensor.matrices
|
||||||
|
val sTensorVectors = sTensor.vectors
|
||||||
|
val vTensors = vTensor.matrices
|
||||||
|
|
||||||
|
for (index in matrices.indices) {
|
||||||
|
val matrix = matrices[index]
|
||||||
|
val matrixSize = matrix.shape.reduce { acc, i -> acc * i }
|
||||||
|
val curMatrix = DoubleTensor(
|
||||||
|
matrix.shape,
|
||||||
|
matrix.mutableBuffer.array()
|
||||||
|
.slice(matrix.bufferStart until matrix.bufferStart + matrixSize)
|
||||||
|
.toDoubleArray()
|
||||||
|
)
|
||||||
|
curMatrix.as2D().svdGolubKahanHelper(uTensors[index].as2D(), sTensorVectors[index], vTensors[index].as2D(),
|
||||||
|
iterations, epsilon)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Triple(uTensor, sTensor, vTensor)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singular Value Decomposition.
|
* Singular Value Decomposition.
|
||||||
@ -849,7 +879,7 @@ public open class DoubleTensorAlgebra :
|
|||||||
* i.e., the precision with which the cosine approaches 1 in an iterative algorithm.
|
* i.e., the precision with which the cosine approaches 1 in an iterative algorithm.
|
||||||
* @return a triple `Triple(U, S, V)`.
|
* @return a triple `Triple(U, S, V)`.
|
||||||
*/
|
*/
|
||||||
public fun StructureND<Double>.svd(epsilon: Double): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
|
public fun StructureND<Double>.svdPowerMethod(epsilon: Double = 1e-10): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
|
||||||
val size = tensor.dimension
|
val size = tensor.dimension
|
||||||
val commonShape = tensor.shape.sliceArray(0 until size - 2)
|
val commonShape = tensor.shape.sliceArray(0 until size - 2)
|
||||||
val (n, m) = tensor.shape.sliceArray(size - 2 until size)
|
val (n, m) = tensor.shape.sliceArray(size - 2 until size)
|
||||||
@ -876,7 +906,7 @@ public open class DoubleTensorAlgebra :
|
|||||||
.slice(matrix.bufferStart until matrix.bufferStart + matrixSize)
|
.slice(matrix.bufferStart until matrix.bufferStart + matrixSize)
|
||||||
.toDoubleArray()
|
.toDoubleArray()
|
||||||
)
|
)
|
||||||
svdHelper(curMatrix, usv, m, n, epsilon)
|
svdPowerMethodHelper(curMatrix, usv, m, n, epsilon)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Triple(uTensor.transpose(), sTensor, vTensor.transpose())
|
return Triple(uTensor.transpose(), sTensor, vTensor.transpose())
|
||||||
@ -907,7 +937,7 @@ public open class DoubleTensorAlgebra :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val (u, s, v) = tensor.svd(epsilon)
|
val (u, s, v) = tensor.svd()
|
||||||
val shp = s.shape + intArrayOf(1)
|
val shp = s.shape + intArrayOf(1)
|
||||||
val utv = u.transpose() dot v
|
val utv = u.transpose() dot v
|
||||||
val n = s.shape.last()
|
val n = s.shape.last()
|
||||||
@ -928,18 +958,21 @@ public open class DoubleTensorAlgebra :
|
|||||||
|
|
||||||
var eigenvalueStart = 0
|
var eigenvalueStart = 0
|
||||||
var eigenvectorStart = 0
|
var eigenvectorStart = 0
|
||||||
|
val eigenvaluesBuffer = eigenvalues.mutableBuffer
|
||||||
|
val eigenvectorsBuffer = eigenvectors.mutableBuffer
|
||||||
|
|
||||||
for (matrix in tensor.matrixSequence()) {
|
for (matrix in tensor.matrixSequence()) {
|
||||||
val matrix2D = matrix.as2D()
|
val matrix2D = matrix.as2D()
|
||||||
val (d, v) = matrix2D.jacobiHelper(maxIteration, epsilon)
|
val (d, v) = matrix2D.jacobiHelper(maxIteration, epsilon)
|
||||||
|
|
||||||
for (i in 0 until matrix2D.rowNum) {
|
for (i in 0 until matrix2D.rowNum) {
|
||||||
for (j in 0 until matrix2D.colNum) {
|
for (j in 0 until matrix2D.colNum) {
|
||||||
eigenvectors.mutableBuffer.array()[eigenvectorStart + i * matrix2D.rowNum + j] = v[i, j]
|
eigenvectorsBuffer[eigenvectorStart + i * matrix2D.rowNum + j] = v[i, j]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until matrix2D.rowNum) {
|
for (i in 0 until matrix2D.rowNum) {
|
||||||
eigenvalues.mutableBuffer.array()[eigenvalueStart + i] = d[i]
|
eigenvaluesBuffer[eigenvalueStart + i] = d[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
eigenvalueStart += this.shape.last()
|
eigenvalueStart += this.shape.last()
|
||||||
@ -962,11 +995,11 @@ public open class DoubleTensorAlgebra :
|
|||||||
|
|
||||||
// assume that buffered tensor is square matrix
|
// assume that buffered tensor is square matrix
|
||||||
operator fun BufferedTensor<Double>.get(i: Int, j: Int): Double {
|
operator fun BufferedTensor<Double>.get(i: Int, j: Int): Double {
|
||||||
return this.mutableBuffer.array()[bufferStart + i * this.shape[0] + j]
|
return this.mutableBuffer[bufferStart + i * this.shape[0] + j]
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun BufferedTensor<Double>.set(i: Int, j: Int, value: Double) {
|
operator fun BufferedTensor<Double>.set(i: Int, j: Int, value: Double) {
|
||||||
this.mutableBuffer.array()[bufferStart + i * this.shape[0] + j] = value
|
this.mutableBuffer[bufferStart + i * this.shape[0] + j] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun maxOffDiagonal(matrix: BufferedTensor<Double>): Double {
|
fun maxOffDiagonal(matrix: BufferedTensor<Double>): Double {
|
||||||
|
@ -17,6 +17,7 @@ import space.kscience.kmath.tensors.core.DoubleTensor
|
|||||||
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
|
||||||
import space.kscience.kmath.tensors.core.IntTensor
|
import space.kscience.kmath.tensors.core.IntTensor
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
@ -299,7 +300,7 @@ internal fun DoubleTensorAlgebra.svd1d(a: DoubleTensor, epsilon: Double = 1e-10)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun DoubleTensorAlgebra.svdHelper(
|
internal fun DoubleTensorAlgebra.svdPowerMethodHelper(
|
||||||
matrix: DoubleTensor,
|
matrix: DoubleTensor,
|
||||||
USV: Triple<BufferedTensor<Double>, BufferedTensor<Double>, BufferedTensor<Double>>,
|
USV: Triple<BufferedTensor<Double>, BufferedTensor<Double>, BufferedTensor<Double>>,
|
||||||
m: Int, n: Int, epsilon: Double,
|
m: Int, n: Int, epsilon: Double,
|
||||||
@ -307,6 +308,14 @@ internal fun DoubleTensorAlgebra.svdHelper(
|
|||||||
val res = ArrayList<Triple<Double, DoubleTensor, DoubleTensor>>(0)
|
val res = ArrayList<Triple<Double, DoubleTensor, DoubleTensor>>(0)
|
||||||
val (matrixU, matrixS, matrixV) = USV
|
val (matrixU, matrixS, matrixV) = USV
|
||||||
|
|
||||||
|
val matrixUStart = matrixU.bufferStart
|
||||||
|
val matrixSStart = matrixS.bufferStart
|
||||||
|
val matrixVStart = matrixV.bufferStart
|
||||||
|
|
||||||
|
val matrixUBuffer = matrixU.mutableBuffer
|
||||||
|
val matrixSBuffer = matrixS.mutableBuffer
|
||||||
|
val matrixVBuffer = matrixV.mutableBuffer
|
||||||
|
|
||||||
for (k in 0 until min(n, m)) {
|
for (k in 0 until min(n, m)) {
|
||||||
var a = matrix.copy()
|
var a = matrix.copy()
|
||||||
for ((singularValue, u, v) in res.slice(0 until k)) {
|
for ((singularValue, u, v) in res.slice(0 until k)) {
|
||||||
@ -340,12 +349,307 @@ internal fun DoubleTensorAlgebra.svdHelper(
|
|||||||
val uBuffer = res.map { it.second }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray()
|
val uBuffer = res.map { it.second }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray()
|
||||||
val vBuffer = res.map { it.third }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray()
|
val vBuffer = res.map { it.third }.flatMap { it.mutableBuffer.array().toList() }.toDoubleArray()
|
||||||
for (i in uBuffer.indices) {
|
for (i in uBuffer.indices) {
|
||||||
matrixU.mutableBuffer.array()[matrixU.bufferStart + i] = uBuffer[i]
|
matrixUBuffer[matrixUStart + i] = uBuffer[i]
|
||||||
}
|
}
|
||||||
for (i in s.indices) {
|
for (i in s.indices) {
|
||||||
matrixS.mutableBuffer.array()[matrixS.bufferStart + i] = s[i]
|
matrixSBuffer[matrixSStart + i] = s[i]
|
||||||
}
|
}
|
||||||
for (i in vBuffer.indices) {
|
for (i in vBuffer.indices) {
|
||||||
matrixV.mutableBuffer.array()[matrixV.bufferStart + i] = vBuffer[i]
|
matrixVBuffer[matrixVStart + i] = vBuffer[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pythag(a: Double, b: Double): Double {
|
||||||
|
val at: Double = abs(a)
|
||||||
|
val bt: Double = abs(b)
|
||||||
|
val ct: Double
|
||||||
|
val result: Double
|
||||||
|
if (at > bt) {
|
||||||
|
ct = bt / at
|
||||||
|
result = at * sqrt(1.0 + ct * ct)
|
||||||
|
} else if (bt > 0.0) {
|
||||||
|
ct = at / bt
|
||||||
|
result = bt * sqrt(1.0 + ct * ct)
|
||||||
|
} else result = 0.0
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SIGN(a: Double, b: Double): Double {
|
||||||
|
if (b >= 0.0)
|
||||||
|
return abs(a)
|
||||||
|
else
|
||||||
|
return -abs(a)
|
||||||
|
}
|
||||||
|
internal fun MutableStructure2D<Double>.svdGolubKahanHelper(u: MutableStructure2D<Double>, w: BufferedTensor<Double>,
|
||||||
|
v: MutableStructure2D<Double>, iterations: Int, epsilon: Double) {
|
||||||
|
val shape = this.shape
|
||||||
|
val m = shape.component1()
|
||||||
|
val n = shape.component2()
|
||||||
|
var f = 0.0
|
||||||
|
val rv1 = DoubleArray(n)
|
||||||
|
var s = 0.0
|
||||||
|
var scale = 0.0
|
||||||
|
var anorm = 0.0
|
||||||
|
var g = 0.0
|
||||||
|
var l = 0
|
||||||
|
|
||||||
|
val wStart = w.bufferStart
|
||||||
|
val wBuffer = w.mutableBuffer
|
||||||
|
|
||||||
|
for (i in 0 until n) {
|
||||||
|
/* left-hand reduction */
|
||||||
|
l = i + 1
|
||||||
|
rv1[i] = scale * g
|
||||||
|
g = 0.0
|
||||||
|
s = 0.0
|
||||||
|
scale = 0.0
|
||||||
|
if (i < m) {
|
||||||
|
for (k in i until m) {
|
||||||
|
scale += abs(this[k, i]);
|
||||||
|
}
|
||||||
|
if (abs(scale) > epsilon) {
|
||||||
|
for (k in i until m) {
|
||||||
|
this[k, i] = (this[k, i] / scale)
|
||||||
|
s += this[k, i] * this[k, i]
|
||||||
|
}
|
||||||
|
f = this[i, i]
|
||||||
|
if (f >= 0) {
|
||||||
|
g = (-1) * abs(sqrt(s))
|
||||||
|
} else {
|
||||||
|
g = abs(sqrt(s))
|
||||||
|
}
|
||||||
|
val h = f * g - s
|
||||||
|
this[i, i] = f - g
|
||||||
|
if (i != n - 1) {
|
||||||
|
for (j in l until n) {
|
||||||
|
s = 0.0
|
||||||
|
for (k in i until m) {
|
||||||
|
s += this[k, i] * this[k, j]
|
||||||
|
}
|
||||||
|
f = s / h
|
||||||
|
for (k in i until m) {
|
||||||
|
this[k, j] += f * this[k, i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (k in i until m) {
|
||||||
|
this[k, i] = this[k, i] * scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wBuffer[wStart + i] = scale * g
|
||||||
|
/* right-hand reduction */
|
||||||
|
g = 0.0
|
||||||
|
s = 0.0
|
||||||
|
scale = 0.0
|
||||||
|
if (i < m && i != n - 1) {
|
||||||
|
for (k in l until n) {
|
||||||
|
scale += abs(this[i, k])
|
||||||
|
}
|
||||||
|
if (abs(scale) > epsilon) {
|
||||||
|
for (k in l until n) {
|
||||||
|
this[i, k] = this[i, k] / scale
|
||||||
|
s += this[i, k] * this[i, k]
|
||||||
|
}
|
||||||
|
f = this[i, l]
|
||||||
|
if (f >= 0) {
|
||||||
|
g = (-1) * abs(sqrt(s))
|
||||||
|
} else {
|
||||||
|
g = abs(sqrt(s))
|
||||||
|
}
|
||||||
|
val h = f * g - s
|
||||||
|
this[i, l] = f - g
|
||||||
|
for (k in l until n) {
|
||||||
|
rv1[k] = this[i, k] / h
|
||||||
|
}
|
||||||
|
if (i != m - 1) {
|
||||||
|
for (j in l until m) {
|
||||||
|
s = 0.0
|
||||||
|
for (k in l until n) {
|
||||||
|
s += this[j, k] * this[i, k]
|
||||||
|
}
|
||||||
|
for (k in l until n) {
|
||||||
|
this[j, k] += s * rv1[k]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (k in l until n) {
|
||||||
|
this[i, k] = this[i, k] * scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anorm = max(anorm, (abs(wBuffer[wStart + i]) + abs(rv1[i])));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in n - 1 downTo 0) {
|
||||||
|
if (i < n - 1) {
|
||||||
|
if (abs(g) > epsilon) {
|
||||||
|
for (j in l until n) {
|
||||||
|
v[j, i] = (this[i, j] / this[i, l]) / g
|
||||||
|
}
|
||||||
|
for (j in l until n) {
|
||||||
|
s = 0.0
|
||||||
|
for (k in l until n)
|
||||||
|
s += this[i, k] * v[k, j]
|
||||||
|
for (k in l until n)
|
||||||
|
v[k, j] += s * v[k, i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (j in l until n) {
|
||||||
|
v[i, j] = 0.0
|
||||||
|
v[j, i] = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v[i, i] = 1.0
|
||||||
|
g = rv1[i]
|
||||||
|
l = i
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in min(n, m) - 1 downTo 0) {
|
||||||
|
l = i + 1
|
||||||
|
g = wBuffer[wStart + i]
|
||||||
|
for (j in l until n) {
|
||||||
|
this[i, j] = 0.0
|
||||||
|
}
|
||||||
|
if (abs(g) > epsilon) {
|
||||||
|
g = 1.0 / g
|
||||||
|
for (j in l until n) {
|
||||||
|
s = 0.0
|
||||||
|
for (k in l until m) {
|
||||||
|
s += this[k, i] * this[k, j]
|
||||||
|
}
|
||||||
|
f = (s / this[i, i]) * g
|
||||||
|
for (k in i until m) {
|
||||||
|
this[k, j] += f * this[k, i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (j in i until m) {
|
||||||
|
this[j, i] *= g
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (j in i until m) {
|
||||||
|
this[j, i] = 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this[i, i] += 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
var flag = 0
|
||||||
|
var nm = 0
|
||||||
|
var c = 0.0
|
||||||
|
var h = 0.0
|
||||||
|
var y = 0.0
|
||||||
|
var z = 0.0
|
||||||
|
var x = 0.0
|
||||||
|
for (k in n - 1 downTo 0) {
|
||||||
|
for (its in 1 until iterations) {
|
||||||
|
flag = 1
|
||||||
|
for (newl in k downTo 0) {
|
||||||
|
nm = newl - 1
|
||||||
|
if (abs(rv1[newl]) + anorm == anorm) {
|
||||||
|
flag = 0
|
||||||
|
l = newl
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (abs(wBuffer[wStart + nm]) + anorm == anorm) {
|
||||||
|
l = newl
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag != 0) {
|
||||||
|
c = 0.0
|
||||||
|
s = 1.0
|
||||||
|
for (i in l until k + 1) {
|
||||||
|
f = s * rv1[i]
|
||||||
|
rv1[i] = c * rv1[i]
|
||||||
|
if (abs(f) + anorm == anorm) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
g = wBuffer[wStart + i]
|
||||||
|
h = pythag(f, g)
|
||||||
|
wBuffer[wStart + i] = h
|
||||||
|
h = 1.0 / h
|
||||||
|
c = g * h
|
||||||
|
s = (-f) * h
|
||||||
|
for (j in 0 until m) {
|
||||||
|
y = this[j, nm]
|
||||||
|
z = this[j, i]
|
||||||
|
this[j, nm] = y * c + z * s
|
||||||
|
this[j, i] = z * c - y * s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z = wBuffer[wStart + k]
|
||||||
|
if (l == k) {
|
||||||
|
if (z < 0.0) {
|
||||||
|
wBuffer[wStart + k] = -z
|
||||||
|
for (j in 0 until n)
|
||||||
|
v[j, k] = -v[j, k]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
x = wBuffer[wStart + l]
|
||||||
|
nm = k - 1
|
||||||
|
y = wBuffer[wStart + nm]
|
||||||
|
g = rv1[nm]
|
||||||
|
h = rv1[k]
|
||||||
|
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y)
|
||||||
|
g = pythag(f, 1.0)
|
||||||
|
f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x
|
||||||
|
c = 1.0
|
||||||
|
s = 1.0
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
for (j in l until nm + 1) {
|
||||||
|
i = j + 1
|
||||||
|
g = rv1[i]
|
||||||
|
y = wBuffer[wStart + i]
|
||||||
|
h = s * g
|
||||||
|
g = c * g
|
||||||
|
z = pythag(f, h)
|
||||||
|
rv1[j] = z
|
||||||
|
c = f / z
|
||||||
|
s = h / z
|
||||||
|
f = x * c + g * s
|
||||||
|
g = g * c - x * s
|
||||||
|
h = y * s
|
||||||
|
y *= c
|
||||||
|
|
||||||
|
for (jj in 0 until n) {
|
||||||
|
x = v[jj, j];
|
||||||
|
z = v[jj, i];
|
||||||
|
v[jj, j] = x * c + z * s;
|
||||||
|
v[jj, i] = z * c - x * s;
|
||||||
|
}
|
||||||
|
z = pythag(f, h)
|
||||||
|
wBuffer[wStart + j] = z
|
||||||
|
if (abs(z) > epsilon) {
|
||||||
|
z = 1.0 / z
|
||||||
|
c = f * z
|
||||||
|
s = h * z
|
||||||
|
}
|
||||||
|
f = c * g + s * y
|
||||||
|
x = c * y - s * g
|
||||||
|
for (jj in 0 until m) {
|
||||||
|
y = this[jj, j]
|
||||||
|
z = this[jj, i]
|
||||||
|
this[jj, j] = y * c + z * s
|
||||||
|
this[jj, i] = z * c - y * s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rv1[l] = 0.0
|
||||||
|
rv1[k] = f
|
||||||
|
wBuffer[wStart + k] = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until n) {
|
||||||
|
for (j in 0 until m) {
|
||||||
|
u[j, i] = this[j, i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,110 +12,250 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
internal class TestDoubleAnalyticTensorAlgebra {
|
internal class TestDoubleAnalyticTensorAlgebra {
|
||||||
|
|
||||||
val shape = intArrayOf(2, 1, 3, 2)
|
val shapeWithNegative = intArrayOf(4)
|
||||||
val buffer = doubleArrayOf(
|
val bufferWithNegative = doubleArrayOf(9.3348, -7.5889, -1.2005, 1.1584)
|
||||||
27.1, 20.0, 19.84,
|
val tensorWithNegative = DoubleTensor(shapeWithNegative, bufferWithNegative)
|
||||||
23.123, 3.0, 2.0,
|
|
||||||
|
|
||||||
3.23, 133.7, 25.3,
|
val shape1 = intArrayOf(4)
|
||||||
100.3, 11.0, 12.012
|
val buffer1 = doubleArrayOf(1.3348, 1.5889, 1.2005, 1.1584)
|
||||||
)
|
val tensor1 = DoubleTensor(shape1, buffer1)
|
||||||
val tensor = DoubleTensor(shape, buffer)
|
|
||||||
|
val shape2 = intArrayOf(2, 2)
|
||||||
|
val buffer2 = doubleArrayOf(1.0, 9.456, 3.0, 4.0)
|
||||||
|
val tensor2 = DoubleTensor(shape2, buffer2)
|
||||||
|
|
||||||
|
val shape3 = intArrayOf(2, 3, 2)
|
||||||
|
val buffer3 = doubleArrayOf(1.0, 9.456, 7.0, 2.123, 1.0, 9.456, 30.8888, 6.0, 1.0, 9.456, 3.0, 4.99)
|
||||||
|
val tensor3 = DoubleTensor(shape3, buffer3)
|
||||||
|
|
||||||
|
val shape4 = intArrayOf(2, 1, 3, 2)
|
||||||
|
val buffer4 = doubleArrayOf(27.1, 20.0, 19.84, 23.123, 3.0, 2.0, 3.23, 133.7, 25.3, 100.3, 11.0, 12.012)
|
||||||
|
val tensor4 = DoubleTensor(shape4, buffer4)
|
||||||
|
|
||||||
|
val bufferWithNegativeMod1 = bufferWithNegative.map { x -> x % 1 }.toDoubleArray()
|
||||||
|
val tensorWithNegativeMod1 = DoubleTensor(shapeWithNegative, bufferWithNegativeMod1)
|
||||||
|
|
||||||
|
val buffer1Mod1 = buffer1.map { x -> x % 1 }.toDoubleArray()
|
||||||
|
val tensor1Mod1 = DoubleTensor(shape1, buffer1Mod1)
|
||||||
|
|
||||||
|
val buffer2Mod1 = buffer2.map { x -> x % 1 }.toDoubleArray()
|
||||||
|
val tensor2Mod1 = DoubleTensor(shape2, buffer2Mod1)
|
||||||
|
|
||||||
|
val buffer3Mod1 = buffer3.map { x -> x % 1 }.toDoubleArray()
|
||||||
|
val tensor3Mod1 = DoubleTensor(shape3, buffer3Mod1)
|
||||||
|
|
||||||
|
val buffer4Mod1 = buffer4.map { x -> x % 1 }.toDoubleArray()
|
||||||
|
val tensor4Mod1 = DoubleTensor(shape4, buffer4Mod1)
|
||||||
|
|
||||||
fun DoubleArray.fmap(transform: (Double) -> Double): DoubleArray {
|
fun DoubleArray.fmap(transform: (Double) -> Double): DoubleArray {
|
||||||
return this.map(transform).toDoubleArray()
|
return this.map(transform).toDoubleArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun expectedTensor(transform: (Double) -> Double): DoubleTensor {
|
fun expectedTensorWithNegative(transform: (Double) -> Double): DoubleTensor {
|
||||||
return DoubleTensor(shape, buffer.fmap(transform))
|
return DoubleTensor(shapeWithNegative, bufferWithNegative.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor1(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape1, buffer1.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor2(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape2, buffer2.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor3(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape3, buffer3.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor4(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape4, buffer4.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensorWithNegativeMod1(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shapeWithNegative, bufferWithNegativeMod1.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor1Mod1(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape1, buffer1Mod1.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor2Mod1(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape2, buffer2Mod1.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor3Mod1(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape3, buffer3Mod1.fmap(transform))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectedTensor4Mod1(transform: (Double) -> Double): DoubleTensor {
|
||||||
|
return DoubleTensor(shape4, buffer4Mod1.fmap(transform))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testExp() = DoubleTensorAlgebra {
|
fun testExp() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.exp() eq expectedTensor(::exp) }
|
assertTrue { tensorWithNegative.exp() eq expectedTensorWithNegative(::exp) }
|
||||||
|
assertTrue { tensor1.exp() eq expectedTensor1(::exp) }
|
||||||
|
assertTrue { tensor2.exp() eq expectedTensor2(::exp) }
|
||||||
|
assertTrue { tensor3.exp() eq expectedTensor3(::exp) }
|
||||||
|
assertTrue { tensor4.exp() eq expectedTensor4(::exp) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLog() = DoubleTensorAlgebra {
|
fun testLog() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.ln() eq expectedTensor(::ln) }
|
assertTrue { tensor1.ln() eq expectedTensor1(::ln) }
|
||||||
|
assertTrue { tensor2.ln() eq expectedTensor2(::ln) }
|
||||||
|
assertTrue { tensor3.ln() eq expectedTensor3(::ln) }
|
||||||
|
assertTrue { tensor4.ln() eq expectedTensor4(::ln) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSqrt() = DoubleTensorAlgebra {
|
fun testSqrt() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.sqrt() eq expectedTensor(::sqrt) }
|
assertTrue { tensor1.sqrt() eq expectedTensor1(::sqrt) }
|
||||||
|
assertTrue { tensor2.sqrt() eq expectedTensor2(::sqrt) }
|
||||||
|
assertTrue { tensor3.sqrt() eq expectedTensor3(::sqrt) }
|
||||||
|
assertTrue { tensor4.sqrt() eq expectedTensor4(::sqrt) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCos() = DoubleTensorAlgebra {
|
fun testCos() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.cos() eq expectedTensor(::cos) }
|
assertTrue { tensorWithNegative.cos() eq expectedTensorWithNegative(::cos) }
|
||||||
|
assertTrue { tensor1.cos() eq expectedTensor1(::cos) }
|
||||||
|
assertTrue { tensor2.cos() eq expectedTensor2(::cos) }
|
||||||
|
assertTrue { tensor3.cos() eq expectedTensor3(::cos) }
|
||||||
|
assertTrue { tensor4.cos() eq expectedTensor4(::cos) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAcos() = DoubleTensorAlgebra {
|
||||||
|
assertTrue { tensorWithNegativeMod1.acos() eq expectedTensorWithNegativeMod1(::acos) }
|
||||||
|
assertTrue { tensor1Mod1.acos() eq expectedTensor1Mod1(::acos) }
|
||||||
|
assertTrue { tensor2Mod1.acos() eq expectedTensor2Mod1(::acos) }
|
||||||
|
assertTrue { tensor3Mod1.acos() eq expectedTensor3Mod1(::acos) }
|
||||||
|
assertTrue { tensor4Mod1.acos() eq expectedTensor4Mod1(::acos) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCosh() = DoubleTensorAlgebra {
|
fun testCosh() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.cosh() eq expectedTensor(::cosh) }
|
assertTrue { tensorWithNegative.cosh() eq expectedTensorWithNegative(::cosh) }
|
||||||
|
assertTrue { tensor1.cosh() eq expectedTensor1(::cosh) }
|
||||||
|
assertTrue { tensor2.cosh() eq expectedTensor2(::cosh) }
|
||||||
|
assertTrue { tensor3.cosh() eq expectedTensor3(::cosh) }
|
||||||
|
assertTrue { tensor4.cosh() eq expectedTensor4(::cosh) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAcosh() = DoubleTensorAlgebra {
|
fun testAcosh() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.acosh() eq expectedTensor(::acosh) }
|
assertTrue { tensor1.acosh() eq expectedTensor1(::acosh) }
|
||||||
|
assertTrue { tensor2.acosh() eq expectedTensor2(::acosh) }
|
||||||
|
assertTrue { tensor3.acosh() eq expectedTensor3(::acosh) }
|
||||||
|
assertTrue { tensor4.acosh() eq expectedTensor4(::acosh) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSin() = DoubleTensorAlgebra {
|
fun testSin() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.sin() eq expectedTensor(::sin) }
|
assertTrue { tensorWithNegative.sin() eq expectedTensorWithNegative(::sin) }
|
||||||
|
assertTrue { tensor1.sin() eq expectedTensor1(::sin) }
|
||||||
|
assertTrue { tensor2.sin() eq expectedTensor2(::sin) }
|
||||||
|
assertTrue { tensor3.sin() eq expectedTensor3(::sin) }
|
||||||
|
assertTrue { tensor4.sin() eq expectedTensor4(::sin) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAsin() = DoubleTensorAlgebra {
|
||||||
|
assertTrue { tensorWithNegativeMod1.asin() eq expectedTensorWithNegativeMod1(::asin) }
|
||||||
|
assertTrue { tensor1Mod1.asin() eq expectedTensor1Mod1(::asin) }
|
||||||
|
assertTrue { tensor2Mod1.asin() eq expectedTensor2Mod1(::asin) }
|
||||||
|
assertTrue { tensor3Mod1.asin() eq expectedTensor3Mod1(::asin) }
|
||||||
|
assertTrue { tensor4Mod1.asin() eq expectedTensor4Mod1(::asin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSinh() = DoubleTensorAlgebra {
|
fun testSinh() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.sinh() eq expectedTensor(::sinh) }
|
assertTrue { tensorWithNegative.sinh() eq expectedTensorWithNegative(::sinh) }
|
||||||
|
assertTrue { tensor1.sinh() eq expectedTensor1(::sinh) }
|
||||||
|
assertTrue { tensor2.sinh() eq expectedTensor2(::sinh) }
|
||||||
|
assertTrue { tensor3.sinh() eq expectedTensor3(::sinh) }
|
||||||
|
assertTrue { tensor4.sinh() eq expectedTensor4(::sinh) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAsinh() = DoubleTensorAlgebra {
|
fun testAsinh() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.asinh() eq expectedTensor(::asinh) }
|
assertTrue { tensorWithNegative.asinh() eq expectedTensorWithNegative(::asinh) }
|
||||||
|
assertTrue { tensor1.asinh() eq expectedTensor1(::asinh) }
|
||||||
|
assertTrue { tensor2.asinh() eq expectedTensor2(::asinh) }
|
||||||
|
assertTrue { tensor3.asinh() eq expectedTensor3(::asinh) }
|
||||||
|
assertTrue { tensor4.asinh() eq expectedTensor4(::asinh) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testTan() = DoubleTensorAlgebra {
|
fun testTan() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.tan() eq expectedTensor(::tan) }
|
assertTrue { tensorWithNegative.tan() eq expectedTensorWithNegative(::tan) }
|
||||||
|
assertTrue { tensor1.tan() eq expectedTensor1(::tan) }
|
||||||
|
assertTrue { tensor2.tan() eq expectedTensor2(::tan) }
|
||||||
|
assertTrue { tensor3.tan() eq expectedTensor3(::tan) }
|
||||||
|
assertTrue { tensor4.tan() eq expectedTensor4(::tan) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAtan() = DoubleTensorAlgebra {
|
fun testAtan() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.atan() eq expectedTensor(::atan) }
|
assertTrue { tensorWithNegative.atan() eq expectedTensorWithNegative(::atan) }
|
||||||
|
assertTrue { tensor1.atan() eq expectedTensor1(::atan) }
|
||||||
|
assertTrue { tensor2.atan() eq expectedTensor2(::atan) }
|
||||||
|
assertTrue { tensor3.atan() eq expectedTensor3(::atan) }
|
||||||
|
assertTrue { tensor4.atan() eq expectedTensor4(::atan) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testTanh() = DoubleTensorAlgebra {
|
fun testTanh() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.tanh() eq expectedTensor(::tanh) }
|
assertTrue { tensorWithNegative.tanh() eq expectedTensorWithNegative(::tanh) }
|
||||||
|
assertTrue { tensor1.tanh() eq expectedTensor1(::tanh) }
|
||||||
|
assertTrue { tensor2.tanh() eq expectedTensor2(::tanh) }
|
||||||
|
assertTrue { tensor3.tanh() eq expectedTensor3(::tanh) }
|
||||||
|
assertTrue { tensor4.tanh() eq expectedTensor4(::tanh) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAtanh() = DoubleTensorAlgebra {
|
||||||
|
assertTrue { tensorWithNegativeMod1.atanh() eq expectedTensorWithNegativeMod1(::atanh) }
|
||||||
|
assertTrue { tensor1Mod1.atanh() eq expectedTensor1Mod1(::atanh) }
|
||||||
|
assertTrue { tensor2Mod1.atanh() eq expectedTensor2Mod1(::atanh) }
|
||||||
|
assertTrue { tensor3Mod1.atanh() eq expectedTensor3Mod1(::atanh) }
|
||||||
|
assertTrue { tensor4Mod1.atanh() eq expectedTensor4Mod1(::atanh) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testCeil() = DoubleTensorAlgebra {
|
fun testCeil() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.ceil() eq expectedTensor(::ceil) }
|
assertTrue { tensorWithNegative.ceil() eq expectedTensorWithNegative(::ceil) }
|
||||||
|
assertTrue { tensor1.ceil() eq expectedTensor1(::ceil) }
|
||||||
|
assertTrue { tensor2.ceil() eq expectedTensor2(::ceil) }
|
||||||
|
assertTrue { tensor3.ceil() eq expectedTensor3(::ceil) }
|
||||||
|
assertTrue { tensor4.ceil() eq expectedTensor4(::ceil) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testFloor() = DoubleTensorAlgebra {
|
fun testFloor() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor.floor() eq expectedTensor(::floor) }
|
assertTrue { tensorWithNegative.floor() eq expectedTensorWithNegative(::floor) }
|
||||||
|
assertTrue { tensor1.floor() eq expectedTensor1(::floor) }
|
||||||
|
assertTrue { tensor2.floor() eq expectedTensor2(::floor) }
|
||||||
|
assertTrue { tensor3.floor() eq expectedTensor3(::floor) }
|
||||||
|
assertTrue { tensor4.floor() eq expectedTensor4(::floor) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val shape2 = intArrayOf(2, 2)
|
val shape5 = intArrayOf(2, 2)
|
||||||
val buffer2 = doubleArrayOf(
|
val buffer5 = doubleArrayOf(
|
||||||
1.0, 2.0,
|
1.0, 2.0,
|
||||||
-3.0, 4.0
|
-3.0, 4.0
|
||||||
)
|
)
|
||||||
val tensor2 = DoubleTensor(shape2, buffer2)
|
val tensor5 = DoubleTensor(shape5, buffer5)
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMin() = DoubleTensorAlgebra {
|
fun testMin() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor2.min() == -3.0 }
|
assertTrue { tensor5.min() == -3.0 }
|
||||||
assertTrue { tensor2.min(0, true) eq fromArray(
|
assertTrue { tensor5.min(0, true) eq fromArray(
|
||||||
intArrayOf(1, 2),
|
intArrayOf(1, 2),
|
||||||
doubleArrayOf(-3.0, 2.0)
|
doubleArrayOf(-3.0, 2.0)
|
||||||
)}
|
)}
|
||||||
assertTrue { tensor2.min(1, false) eq fromArray(
|
assertTrue { tensor5.min(1, false) eq fromArray(
|
||||||
intArrayOf(2),
|
intArrayOf(2),
|
||||||
doubleArrayOf(1.0, -3.0)
|
doubleArrayOf(1.0, -3.0)
|
||||||
)}
|
)}
|
||||||
@ -123,12 +263,12 @@ internal class TestDoubleAnalyticTensorAlgebra {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMax() = DoubleTensorAlgebra {
|
fun testMax() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor2.max() == 4.0 }
|
assertTrue { tensor5.max() == 4.0 }
|
||||||
assertTrue { tensor2.max(0, true) eq fromArray(
|
assertTrue { tensor5.max(0, true) eq fromArray(
|
||||||
intArrayOf(1, 2),
|
intArrayOf(1, 2),
|
||||||
doubleArrayOf(1.0, 4.0)
|
doubleArrayOf(1.0, 4.0)
|
||||||
)}
|
)}
|
||||||
assertTrue { tensor2.max(1, false) eq fromArray(
|
assertTrue { tensor5.max(1, false) eq fromArray(
|
||||||
intArrayOf(2),
|
intArrayOf(2),
|
||||||
doubleArrayOf(2.0, 4.0)
|
doubleArrayOf(2.0, 4.0)
|
||||||
)}
|
)}
|
||||||
@ -136,12 +276,12 @@ internal class TestDoubleAnalyticTensorAlgebra {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSum() = DoubleTensorAlgebra {
|
fun testSum() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor2.sum() == 4.0 }
|
assertTrue { tensor5.sum() == 4.0 }
|
||||||
assertTrue { tensor2.sum(0, true) eq fromArray(
|
assertTrue { tensor5.sum(0, true) eq fromArray(
|
||||||
intArrayOf(1, 2),
|
intArrayOf(1, 2),
|
||||||
doubleArrayOf(-2.0, 6.0)
|
doubleArrayOf(-2.0, 6.0)
|
||||||
)}
|
)}
|
||||||
assertTrue { tensor2.sum(1, false) eq fromArray(
|
assertTrue { tensor5.sum(1, false) eq fromArray(
|
||||||
intArrayOf(2),
|
intArrayOf(2),
|
||||||
doubleArrayOf(3.0, 1.0)
|
doubleArrayOf(3.0, 1.0)
|
||||||
)}
|
)}
|
||||||
@ -149,15 +289,24 @@ internal class TestDoubleAnalyticTensorAlgebra {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMean() = DoubleTensorAlgebra {
|
fun testMean() = DoubleTensorAlgebra {
|
||||||
assertTrue { tensor2.mean() == 1.0 }
|
assertTrue { tensor5.mean() == 1.0 }
|
||||||
assertTrue { tensor2.mean(0, true) eq fromArray(
|
assertTrue { tensor5.mean(0, true) eq fromArray(
|
||||||
intArrayOf(1, 2),
|
intArrayOf(1, 2),
|
||||||
doubleArrayOf(-1.0, 3.0)
|
doubleArrayOf(-1.0, 3.0)
|
||||||
)}
|
)}
|
||||||
assertTrue { tensor2.mean(1, false) eq fromArray(
|
assertTrue { tensor5.mean(1, false) eq fromArray(
|
||||||
intArrayOf(2),
|
intArrayOf(2),
|
||||||
doubleArrayOf(1.5, 0.5)
|
doubleArrayOf(1.5, 0.5)
|
||||||
)}
|
)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testStd() = DoubleTensorAlgebra {
|
||||||
|
assertTrue { floor(tensor5.std() * 10000 ) / 10000 == 2.9439 }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testVariance() = DoubleTensorAlgebra {
|
||||||
|
assertTrue { floor(tensor5.variance() * 10000 ) / 10000 == 8.6666 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,23 +156,12 @@ internal class TestDoubleLinearOpsTensorAlgebra {
|
|||||||
|
|
||||||
val res = svd1d(tensor2)
|
val res = svd1d(tensor2)
|
||||||
|
|
||||||
|
val resBuffer = res.mutableBuffer
|
||||||
|
val resStart = res.bufferStart
|
||||||
|
|
||||||
assertTrue(res.shape contentEquals intArrayOf(2))
|
assertTrue(res.shape contentEquals intArrayOf(2))
|
||||||
assertTrue { abs(abs(res.mutableBuffer.array()[res.bufferStart]) - 0.386) < 0.01 }
|
assertTrue { abs(abs(resBuffer[resStart]) - 0.386) < 0.01 }
|
||||||
assertTrue { abs(abs(res.mutableBuffer.array()[res.bufferStart + 1]) - 0.922) < 0.01 }
|
assertTrue { abs(abs(resBuffer[resStart + 1]) - 0.922) < 0.01 }
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSVD() = DoubleTensorAlgebra{
|
|
||||||
testSVDFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
|
|
||||||
testSVDFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testBatchedSVD() = DoubleTensorAlgebra {
|
|
||||||
val tensor = randomNormal(intArrayOf(2, 5, 3), 0)
|
|
||||||
val (tensorU, tensorS, tensorV) = tensor.svd()
|
|
||||||
val tensorSVD = tensorU dot (diagonalEmbedding(tensorS) dot tensorV.transpose())
|
|
||||||
assertTrue(tensor.eq(tensorSVD))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -184,13 +173,130 @@ internal class TestDoubleLinearOpsTensorAlgebra {
|
|||||||
assertTrue(tensorSigma.eq(tensorSigmaCalc))
|
assertTrue(tensorSigma.eq(tensorSigmaCalc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSVD() = DoubleTensorAlgebra{
|
||||||
|
testSVDFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
|
||||||
|
testSVDFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
|
||||||
|
val buffer1 = doubleArrayOf(
|
||||||
|
1.000000, 2.000000, 3.000000,
|
||||||
|
2.000000, 3.000000, 4.000000,
|
||||||
|
3.000000, 4.000000, 5.000000,
|
||||||
|
4.000000, 5.000000, 6.000000,
|
||||||
|
5.000000, 6.000000, 9.000000
|
||||||
|
)
|
||||||
|
testSVDFor(fromArray(intArrayOf(5, 3), buffer1))
|
||||||
|
val buffer2 = doubleArrayOf(
|
||||||
|
1.0, 2.0, 3.0, 2.0, 3.0,
|
||||||
|
4.0, 3.0, 4.0, 5.0, 4.0,
|
||||||
|
5.0, 6.0, 5.0, 6.0, 7.0
|
||||||
|
)
|
||||||
|
testSVDFor(fromArray(intArrayOf(3, 5), buffer2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBatchedSVD() = DoubleTensorAlgebra{
|
||||||
|
val tensor1 = randomNormal(intArrayOf(2, 5, 3), 0)
|
||||||
|
testSVDFor(tensor1)
|
||||||
|
val tensor2 = DoubleTensorAlgebra.randomNormal(intArrayOf(30, 30, 30), 0)
|
||||||
|
testSVDFor(tensor2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSVDGolubKahan() = DoubleTensorAlgebra{
|
||||||
|
testSVDGolubKahanFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
|
||||||
|
testSVDGolubKahanFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
|
||||||
|
val buffer1 = doubleArrayOf(
|
||||||
|
1.000000, 2.000000, 3.000000,
|
||||||
|
2.000000, 3.000000, 4.000000,
|
||||||
|
3.000000, 4.000000, 5.000000,
|
||||||
|
4.000000, 5.000000, 6.000000,
|
||||||
|
5.000000, 6.000000, 9.000000
|
||||||
|
)
|
||||||
|
testSVDGolubKahanFor(fromArray(intArrayOf(5, 3), buffer1))
|
||||||
|
val buffer2 = doubleArrayOf(
|
||||||
|
1.0, 2.0, 3.0, 2.0, 3.0,
|
||||||
|
4.0, 3.0, 4.0, 5.0, 4.0,
|
||||||
|
5.0, 6.0, 5.0, 6.0, 7.0
|
||||||
|
)
|
||||||
|
testSVDGolubKahanFor(fromArray(intArrayOf(3, 5), buffer2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBatchedSVDGolubKahan() = DoubleTensorAlgebra{
|
||||||
|
val tensor1 = randomNormal(intArrayOf(2, 5, 3), 0)
|
||||||
|
testSVDGolubKahanFor(tensor1)
|
||||||
|
val tensor2 = DoubleTensorAlgebra.randomNormal(intArrayOf(30, 30, 30), 0)
|
||||||
|
testSVDGolubKahanFor(tensor2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSVDPowerMethod() = DoubleTensorAlgebra{
|
||||||
|
testSVDPowerMethodFor(fromArray(intArrayOf(2, 3), doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)))
|
||||||
|
testSVDPowerMethodFor(fromArray(intArrayOf(2, 2), doubleArrayOf(-1.0, 0.0, 239.0, 238.0)))
|
||||||
|
val buffer1 = doubleArrayOf(
|
||||||
|
1.000000, 2.000000, 3.000000,
|
||||||
|
2.000000, 3.000000, 4.000000,
|
||||||
|
3.000000, 4.000000, 5.000000,
|
||||||
|
4.000000, 5.000000, 6.000000,
|
||||||
|
5.000000, 6.000000, 9.000000
|
||||||
|
)
|
||||||
|
testSVDPowerMethodFor(fromArray(intArrayOf(5, 3), buffer1))
|
||||||
|
val buffer2 = doubleArrayOf(
|
||||||
|
1.0, 2.0, 3.0, 2.0, 3.0,
|
||||||
|
4.0, 3.0, 4.0, 5.0, 4.0,
|
||||||
|
5.0, 6.0, 5.0, 6.0, 7.0
|
||||||
|
)
|
||||||
|
testSVDPowerMethodFor(fromArray(intArrayOf(3, 5), buffer2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBatchedSVDPowerMethod() = DoubleTensorAlgebra {
|
||||||
|
val tensor1 = randomNormal(intArrayOf(2, 5, 3), 0)
|
||||||
|
testSVDPowerMethodFor(tensor1)
|
||||||
|
val tensor2 = DoubleTensorAlgebra.randomNormal(intArrayOf(30, 30, 30), 0)
|
||||||
|
testSVDPowerMethodFor(tensor2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun testSVDPowerMethodError() = DoubleTensorAlgebra{
|
||||||
|
// val buffer = doubleArrayOf(
|
||||||
|
// 1.000000, 2.000000, 3.000000,
|
||||||
|
// 2.000000, 3.000000, 4.000000,
|
||||||
|
// 3.000000, 4.000000, 5.000000,
|
||||||
|
// 4.000000, 5.000000, 6.000000,
|
||||||
|
// 5.000000, 6.000000, 7.000000
|
||||||
|
// )
|
||||||
|
// testSVDPowerMethodFor(fromArray(intArrayOf(5, 3), buffer))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun DoubleTensorAlgebra.testSVDFor(tensor: DoubleTensor) {
|
||||||
private fun DoubleTensorAlgebra.testSVDFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
|
|
||||||
val svd = tensor.svd()
|
val svd = tensor.svd()
|
||||||
|
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(tensor.eq(tensorSVD))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DoubleTensorAlgebra.testSVDGolubKahanFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
|
||||||
|
val svd = tensor.svdGolubKahan()
|
||||||
|
|
||||||
|
val tensorSVD = svd.first
|
||||||
|
.dot(
|
||||||
|
diagonalEmbedding(svd.second)
|
||||||
|
.dot(svd.third.transpose())
|
||||||
|
)
|
||||||
|
|
||||||
|
assertTrue(tensor.eq(tensorSVD, epsilon))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DoubleTensorAlgebra.testSVDPowerMethodFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
|
||||||
|
val svd = tensor.svdPowerMethod()
|
||||||
|
|
||||||
val tensorSVD = svd.first
|
val tensorSVD = svd.first
|
||||||
.dot(
|
.dot(
|
||||||
diagonalEmbedding(svd.second)
|
diagonalEmbedding(svd.second)
|
||||||
|
@ -23,6 +23,44 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
internal class TestDoubleTensor {
|
internal class TestDoubleTensor {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFullLike() = DoubleTensorAlgebra {
|
||||||
|
val shape = intArrayOf(2, 3)
|
||||||
|
val buffer = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
|
||||||
|
val tensor = DoubleTensor(shape, buffer)
|
||||||
|
val value = 12.5
|
||||||
|
assertTrue { tensor.fullLike(value) eq DoubleTensor(shape, buffer.map { value }.toDoubleArray() ) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnesLike() = DoubleTensorAlgebra {
|
||||||
|
val shape = intArrayOf(2, 3)
|
||||||
|
val buffer = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
|
||||||
|
val tensor = DoubleTensor(shape, buffer)
|
||||||
|
assertTrue { tensor.onesLike() eq DoubleTensor(shape, buffer.map { 1.0 }.toDoubleArray() ) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRowsByIndices() = DoubleTensorAlgebra {
|
||||||
|
val shape = intArrayOf(2, 2)
|
||||||
|
val buffer = doubleArrayOf(1.0, 2.0, -3.0, 4.0)
|
||||||
|
val tensor = fromArray(shape, buffer)
|
||||||
|
assertTrue { tensor.rowsByIndices(intArrayOf(0)) eq DoubleTensor(intArrayOf(1, 2), doubleArrayOf(1.0, 2.0)) }
|
||||||
|
assertTrue { tensor.rowsByIndices(intArrayOf(0, 1)) eq tensor }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testTimes() = DoubleTensorAlgebra {
|
||||||
|
val shape = intArrayOf(2, 2)
|
||||||
|
val buffer = doubleArrayOf(1.0, 2.0, -3.0, 4.0)
|
||||||
|
val tensor = DoubleTensor(shape, buffer)
|
||||||
|
val value = 3
|
||||||
|
assertTrue { tensor.times(value).toBufferedTensor() eq DoubleTensor(shape, buffer.map { x -> 3 * x }.toDoubleArray()) }
|
||||||
|
val buffer2 = doubleArrayOf(7.0, -8.0, -5.0, 2.0)
|
||||||
|
val tensor2 = DoubleTensor(shape, buffer2)
|
||||||
|
assertTrue {tensor.times(tensor2).toBufferedTensor() eq DoubleTensor(shape, doubleArrayOf(7.0, -16.0, 15.0, 8.0)) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testValue() = DoubleTensorAlgebra {
|
fun testValue() = DoubleTensorAlgebra {
|
||||||
val value = 12.5
|
val value = 12.5
|
||||||
|
@ -132,6 +132,27 @@ internal class TestDoubleTensorAlgebra {
|
|||||||
468.0, 501.0, 534.0, 594.0, 636.0, 678.0, 720.0, 771.0, 822.0
|
468.0, 501.0, 534.0, 594.0, 636.0, 678.0, 720.0, 771.0, 822.0
|
||||||
))
|
))
|
||||||
assertTrue(res45.shape contentEquals intArrayOf(2, 3, 3))
|
assertTrue(res45.shape contentEquals intArrayOf(2, 3, 3))
|
||||||
|
|
||||||
|
val oneDimTensor1 = fromArray(intArrayOf(3), doubleArrayOf(1.0, 2.0, 3.0))
|
||||||
|
val oneDimTensor2 = fromArray(intArrayOf(3), doubleArrayOf(4.0, 5.0, 6.0))
|
||||||
|
val resOneDimTensors = oneDimTensor1.dot(oneDimTensor2)
|
||||||
|
assertTrue(resOneDimTensors.mutableBuffer.array() contentEquals doubleArrayOf(32.0))
|
||||||
|
assertTrue(resOneDimTensors.shape contentEquals intArrayOf(1))
|
||||||
|
|
||||||
|
val twoDimTensor1 = fromArray(intArrayOf(2, 2), doubleArrayOf(1.0, 2.0, 3.0, 4.0))
|
||||||
|
val twoDimTensor2 = fromArray(intArrayOf(2, 2), doubleArrayOf(5.0, 6.0, 7.0, 8.0))
|
||||||
|
val resTwoDimTensors = twoDimTensor1.dot(twoDimTensor2)
|
||||||
|
assertTrue(resTwoDimTensors.mutableBuffer.array() contentEquals doubleArrayOf(19.0, 22.0, 43.0, 50.0))
|
||||||
|
assertTrue(resTwoDimTensors.shape contentEquals intArrayOf(2, 2))
|
||||||
|
|
||||||
|
val oneDimTensor3 = fromArray(intArrayOf(2), doubleArrayOf(1.0, 2.0))
|
||||||
|
val resOneDimTensorOnTwoDimTensor = oneDimTensor3.dot(twoDimTensor1)
|
||||||
|
assertTrue(resOneDimTensorOnTwoDimTensor.mutableBuffer.array() contentEquals doubleArrayOf(7.0, 10.0))
|
||||||
|
assertTrue(resOneDimTensorOnTwoDimTensor.shape contentEquals intArrayOf(2))
|
||||||
|
|
||||||
|
val resTwoDimTensorOnOneDimTensor = twoDimTensor1.dot(oneDimTensor3)
|
||||||
|
assertTrue(resTwoDimTensorOnOneDimTensor.mutableBuffer.array() contentEquals doubleArrayOf(5.0, 11.0))
|
||||||
|
assertTrue(resTwoDimTensorOnOneDimTensor.shape contentEquals intArrayOf(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user