From bfba653904793b74be89ea9f46461ad8c20a2b8e Mon Sep 17 00:00:00 2001 From: Andrei Kislitsyn Date: Sat, 1 May 2021 17:47:12 +0300 Subject: [PATCH] refactor --- .../kscience/kmath/tensors/OLSWithSVD.kt | 6 ++--- .../algebras/DoubleLinearOpsTensorAlgebra.kt | 8 ++++--- .../core/algebras/DoubleTensorAlgebra.kt | 18 +++++++------- .../kmath/tensors/core/broadcastUtils.kt | 24 +++++++++---------- .../kscience/kmath/tensors/core/utils.kt | 3 +-- .../core/TestDoubleLinearOpsAlgebra.kt | 6 ++--- 6 files changed, 32 insertions(+), 33 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt index 0408bba63..095905f05 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/OLSWithSVD.kt @@ -21,7 +21,7 @@ fun main() { // work in context with linear operations DoubleLinearOpsTensorAlgebra.invoke { // take coefficient vector from normal distribution - val alpha = randNormal( + val alpha = randomNormal( intArrayOf(5), randSeed ) + fromArray( @@ -32,14 +32,14 @@ fun main() { println("Real alpha:\n$alpha") // also take sample of size 20 from normal distribution for x - val x = randNormal( + val x = randomNormal( intArrayOf(20, 5), randSeed ) // calculate y and add gaussian noise (N(0, 0.05)) val y = x dot alpha - y += y.randNormalLike(randSeed) * 0.05 + y += y.randomNormalLike(randSeed) * 0.05 // now restore the coefficient vector with OSL estimator with SVD val (u, singValues, v) = x.svd() diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleLinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleLinearOpsTensorAlgebra.kt index 700fafbeb..dd5ad5a61 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleLinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleLinearOpsTensorAlgebra.kt @@ -31,7 +31,7 @@ public object DoubleLinearOpsTensorAlgebra : public fun TensorStructure.luFactor(epsilon: Double): Pair = computeLU(tensor, epsilon) - ?: throw RuntimeException("Tensor contains matrices which are singular at precision $epsilon") + ?: throw IllegalArgumentException("Tensor contains matrices which are singular at precision $epsilon") public fun TensorStructure.luFactor(): Pair = luFactor(1e-9) @@ -47,8 +47,10 @@ public object DoubleLinearOpsTensorAlgebra : val n = luTensor.shape.last() val pTensor = luTensor.zeroesLike() - for ((p, pivot) in pTensor.matrixSequence().zip(pivotsTensor.tensor.vectorSequence())) - pivInit(p.as2D(), pivot.as1D(), n) + pTensor + .matrixSequence() + .zip(pivotsTensor.tensor.vectorSequence()) + .forEach { (p, pivot) -> pivInit(p.as2D(), pivot.as1D(), n) } val lTensor = luTensor.zeroesLike() val uTensor = luTensor.zeroesLike() diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleTensorAlgebra.kt index c6fb301b5..d3e8bf175 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/algebras/DoubleTensorAlgebra.kt @@ -284,7 +284,7 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra { val m1 = newThis.shape[newThis.shape.size - 1] val m2 = newOther.shape[newOther.shape.size - 2] val n = newOther.shape[newOther.shape.size - 1] - if (m1 != m2) { + check (m1 == m2) { throw RuntimeException("Tensors dot operation dimension mismatch: ($l, $m1) x ($m2, $n)") } @@ -315,11 +315,11 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra { val d1 = minusIndexFrom(n + 1, dim1) val d2 = minusIndexFrom(n + 1, dim2) - if (d1 == d2) { - throw RuntimeException("Diagonal dimensions cannot be identical $d1, $d2") + check(d1 != d2) { + "Diagonal dimensions cannot be identical $d1, $d2" } - if (d1 > n || d2 > n) { - throw RuntimeException("Dimension out of range") + check(d1 <= n && d2 <= n) { + "Dimension out of range" } var lessDim = d1 @@ -366,8 +366,8 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra { ) } - public fun TensorStructure.eq(other: TensorStructure, delta: Double): Boolean { - return tensor.eq(other) { x, y -> abs(x - y) < delta } + public fun TensorStructure.eq(other: TensorStructure, epsilon: Double): Boolean { + return tensor.eq(other) { x, y -> abs(x - y) < epsilon } } public infix fun TensorStructure.eq(other: TensorStructure): Boolean = tensor.eq(other, 1e-5) @@ -393,10 +393,10 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra { return true } - public fun randNormal(shape: IntArray, seed: Long = 0): DoubleTensor = + public fun randomNormal(shape: IntArray, seed: Long = 0): DoubleTensor = DoubleTensor(shape, getRandomNormals(shape.reduce(Int::times), seed)) - public fun TensorStructure.randNormalLike(seed: Long = 0): DoubleTensor = + public fun TensorStructure.randomNormalLike(seed: Long = 0): DoubleTensor = DoubleTensor(tensor.shape, getRandomNormals(tensor.shape.reduce(Int::times), seed)) // stack tensors by axis 0 diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/broadcastUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/broadcastUtils.kt index e883b7861..dfac054b5 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/broadcastUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/broadcastUtils.kt @@ -42,8 +42,8 @@ internal fun broadcastShapes(vararg shapes: IntArray): IntArray { for (i in shape.indices) { val curDim = shape[i] val offset = totalDim - shape.size - if (curDim != 1 && totalShape[i + offset] != curDim) { - throw RuntimeException("Shapes are not compatible and cannot be broadcast") + check(curDim == 1 || totalShape[i + offset] == curDim) { + "Shapes are not compatible and cannot be broadcast" } } } @@ -52,8 +52,8 @@ internal fun broadcastShapes(vararg shapes: IntArray): IntArray { } internal fun broadcastTo(tensor: DoubleTensor, newShape: IntArray): DoubleTensor { - if (tensor.shape.size > newShape.size) { - throw RuntimeException("Tensor is not compatible with the new shape") + require(tensor.shape.size <= newShape.size) { + "Tensor is not compatible with the new shape" } val n = newShape.reduce { acc, i -> acc * i } @@ -62,8 +62,8 @@ internal fun broadcastTo(tensor: DoubleTensor, newShape: IntArray): DoubleTensor for (i in tensor.shape.indices) { val curDim = tensor.shape[i] val offset = newShape.size - tensor.shape.size - if (curDim != 1 && newShape[i + offset] != curDim) { - throw RuntimeException("Tensor is not compatible with the new shape and cannot be broadcast") + check(curDim == 1 || newShape[i + offset] == curDim) { + "Tensor is not compatible with the new shape and cannot be broadcast" } } @@ -75,19 +75,17 @@ internal fun broadcastTensors(vararg tensors: DoubleTensor): List val totalShape = broadcastShapes(*(tensors.map { it.shape }).toTypedArray()) val n = totalShape.reduce { acc, i -> acc * i } - return buildList { - for (tensor in tensors) { - val resTensor = DoubleTensor(totalShape, DoubleArray(n)) - multiIndexBroadCasting(tensor, resTensor, n) - add(resTensor) - } + return tensors.map { tensor -> + val resTensor = DoubleTensor(totalShape, DoubleArray(n)) + multiIndexBroadCasting(tensor, resTensor, n) + resTensor } } internal fun broadcastOuterTensors(vararg tensors: DoubleTensor): List { val onlyTwoDims = tensors.asSequence().onEach { require(it.shape.size >= 2) { - throw RuntimeException("Tensors must have at least 2 dimensions") + "Tensors must have at least 2 dimensions" } }.any { it.shape.size != 2 } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt index 58d280307..88b9c6c5c 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/utils.kt @@ -69,8 +69,7 @@ internal fun DoubleTensor.toPrettyString(): String = buildString { val shape = this@toPrettyString.shape val linearStructure = this@toPrettyString.linearStructure val vectorSize = shape.last() - val initString = "DoubleTensor(\n" - append(initString) + append("DoubleTensor(\n") var charOffset = 3 for (vector in vectorSequence()) { repeat(charOffset) { append(' ') } diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt index 2282d7fcb..6689e893a 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt @@ -135,7 +135,7 @@ internal class TestDoubleLinearOpsTensorAlgebra { @Test fun testCholesky() = DoubleLinearOpsTensorAlgebra.invoke { - val tensor = randNormal(intArrayOf(2, 5, 5), 0) + val tensor = randomNormal(intArrayOf(2, 5, 5), 0) val sigma = (tensor dot tensor.transpose()) + diagonalEmbedding( fromArray(intArrayOf(2, 5), DoubleArray(10) { 0.1 }) ) @@ -163,7 +163,7 @@ internal class TestDoubleLinearOpsTensorAlgebra { @Test fun testBatchedSVD() = DoubleLinearOpsTensorAlgebra.invoke { - val tensor = randNormal(intArrayOf(2, 5, 3), 0) + 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)) @@ -171,7 +171,7 @@ internal class TestDoubleLinearOpsTensorAlgebra { @Test fun testBatchedSymEig() = DoubleLinearOpsTensorAlgebra.invoke { - val tensor = randNormal(shape = intArrayOf(2, 3, 3), 0) + val tensor = randomNormal(shape = intArrayOf(2, 3, 3), 0) val tensorSigma = tensor + tensor.transpose() val (tensorS, tensorV) = tensorSigma.symEig() val tensorSigmaCalc = tensorV dot (diagonalEmbedding(tensorS) dot tensorV.transpose())