Feature/tensors performance #497

Closed
margarita0303 wants to merge 91 commits from feature/tensors-performance into feature/tensors-performance
4 changed files with 64 additions and 6 deletions
Showing only changes of commit 8008891a01 - Show all commits

View File

@ -8,7 +8,7 @@ import kotlinx.benchmark.Benchmark
import kotlinx.benchmark.Blackhole import kotlinx.benchmark.Blackhole
import kotlinx.benchmark.Scope import kotlinx.benchmark.Scope
import kotlinx.benchmark.State import kotlinx.benchmark.State
import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.svd import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.svdGolubKahan
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.svdPowerMethod import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.svdPowerMethod
@ -26,9 +26,9 @@ class SVDBenchmark {
} }
@Benchmark @Benchmark
fun svdGolabKahan(blackhole: Blackhole) { fun svdGolubKahan(blackhole: Blackhole) {
blackhole.consume( blackhole.consume(
tensor.svd() tensor.svdGolubKahan()
) )
} }
} }

View File

@ -834,6 +834,10 @@ public open class DoubleTensorAlgebra :
} }
override fun StructureND<Double>.svd(): Triple<DoubleTensor, DoubleTensor, DoubleTensor> { override fun StructureND<Double>.svd(): Triple<DoubleTensor, DoubleTensor, DoubleTensor> {
return this.svdGolubKahan()
}
public fun StructureND<Double>.svdGolubKahan(): 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)
@ -855,7 +859,7 @@ public open class DoubleTensorAlgebra :
.slice(matrix.bufferStart until matrix.bufferStart + matrixSize) .slice(matrix.bufferStart until matrix.bufferStart + matrixSize)
.toDoubleArray() .toDoubleArray()
) )
curMatrix.as2D().svdHelper(uTensors[index].as2D(), sTensorVectors[index], vTensors[index].as2D()) curMatrix.as2D().svdGolubKahanHelper(uTensors[index].as2D(), sTensorVectors[index], vTensors[index].as2D())
} }
return Triple(uTensor.transpose(), sTensor, vTensor) return Triple(uTensor.transpose(), sTensor, vTensor)

View File

@ -372,7 +372,7 @@ private fun SIGN(a: Double, b: Double): Double {
else else
return -abs(a) return -abs(a)
} }
internal fun MutableStructure2D<Double>.svdHelper(u: MutableStructure2D<Double>, w: BufferedTensor<Double>, v: MutableStructure2D<Double>) { internal fun MutableStructure2D<Double>.svdGolubKahanHelper(u: MutableStructure2D<Double>, w: BufferedTensor<Double>, v: MutableStructure2D<Double>) {
val shape = this.shape val shape = this.shape
val m = shape.component1() val m = shape.component1()
val n = shape.component2() val n = shape.component2()

View File

@ -179,7 +179,7 @@ internal class TestDoubleLinearOpsTensorAlgebra {
2.000000, 3.000000, 4.000000, 2.000000, 3.000000, 4.000000,
3.000000, 4.000000, 5.000000, 3.000000, 4.000000, 5.000000,
4.000000, 5.000000, 6.000000, 4.000000, 5.000000, 6.000000,
5.000000, 6.000000, 7.000000 5.000000, 6.000000, 9.000000
) )
testSVDFor(fromArray(intArrayOf(5, 3), buffer1)) testSVDFor(fromArray(intArrayOf(5, 3), buffer1))
val buffer2 = doubleArrayOf( val buffer2 = doubleArrayOf(
@ -198,10 +198,52 @@ internal class TestDoubleLinearOpsTensorAlgebra {
testSVDFor(tensor2) 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 @Test
fun testSVDPowerMethod() = DoubleTensorAlgebra{ 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, 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))) 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 @Test
@ -237,6 +279,18 @@ private fun DoubleTensorAlgebra.testSVDFor(tensor: DoubleTensor) {
assertTrue(tensor.eq(tensorSVD)) assertTrue(tensor.eq(tensorSVD))
} }
private fun DoubleTensorAlgebra.testSVDGolubKahanFor(tensor: DoubleTensor) {
val svd = tensor.svdGolubKahan()
val tensorSVD = svd.first
.dot(
diagonalEmbedding(svd.second)
.dot(svd.third.transpose())
)
assertTrue(tensor.eq(tensorSVD))
}
private fun DoubleTensorAlgebra.testSVDPowerMethodFor(tensor: DoubleTensor, epsilon: Double = 1e-10) { private fun DoubleTensorAlgebra.testSVDPowerMethodFor(tensor: DoubleTensor, epsilon: Double = 1e-10) {
val svd = tensor.svdPowerMethod() val svd = tensor.svdPowerMethod()