From 1f19ac88aee34ed5b790356a81f762b21c1752f3 Mon Sep 17 00:00:00 2001 From: Andrei Kislitsyn Date: Fri, 26 Mar 2021 17:43:08 +0300 Subject: [PATCH 1/4] qr fix --- .../kmath/tensors/LinearOpsTensorAlgebra.kt | 2 +- .../tensors/core/DoubleLinearOpsTensorAlgebra.kt | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt index 3d1625a50..d980c510f 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/LinearOpsTensorAlgebra.kt @@ -14,7 +14,7 @@ public interface LinearOpsTensorAlgebra, Inde public fun TensorType.cholesky(): TensorType //https://pytorch.org/docs/stable/linalg.html#torch.linalg.qr - public fun TensorType.qr(): TensorType + public fun TensorType.qr(): Pair //https://pytorch.org/docs/stable/generated/torch.lu.html public fun TensorType.lu(): Pair diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt index 0b9d824f0..d49fc941a 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt @@ -1,8 +1,10 @@ package space.kscience.kmath.tensors.core +import space.kscience.kmath.linear.Matrix import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.Structure1D import space.kscience.kmath.nd.Structure2D +import space.kscience.kmath.nd.asND import space.kscience.kmath.tensors.LinearOpsTensorAlgebra import kotlin.math.sqrt @@ -141,7 +143,15 @@ public class DoubleLinearOpsTensorAlgebra : return lTensor } - override fun DoubleTensor.qr(): DoubleTensor { + private fun matrixQR( + matrix: Structure2D, + q: MutableStructure2D, + r: MutableStructure2D + ) { + + } + + override fun DoubleTensor.qr(): Pair { TODO("ANDREI") } @@ -154,6 +164,7 @@ public class DoubleLinearOpsTensorAlgebra : } private fun luMatrixDet(lu: Structure2D, pivots: Structure1D): Double { + // todo check val m = lu.shape[0] val sign = if((pivots[m] - m) % 2 == 0) 1.0 else -1.0 return (0 until m).asSequence().map { lu[it, it] }.fold(sign) { left, right -> left * right } @@ -184,6 +195,7 @@ public class DoubleLinearOpsTensorAlgebra : pivots: Structure1D, invMatrix : MutableStructure2D ): Unit { + //todo check val m = lu.shape[0] for (j in 0 until m) { -- 2.34.1 From d281dfca3ab869fcfb16181f82cc91b57bb4d26d Mon Sep 17 00:00:00 2001 From: Roland Grinis Date: Tue, 30 Mar 2021 11:22:55 +0100 Subject: [PATCH 2/4] Separate linear algebra utils into dedicated module --- .../kmath/tensors/core/BufferedTensor.kt | 38 ---- .../core/DoubleLinearOpsTensorAlgebra.kt | 134 +------------ .../kmath/tensors/core/DoubleTensorAlgebra.kt | 20 +- .../kscience/kmath/tensors/core/linutils.kt | 188 ++++++++++++++++++ 4 files changed, 190 insertions(+), 190 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/linutils.kt diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt index d99c35569..9a4d13d2c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BufferedTensor.kt @@ -1,7 +1,5 @@ package space.kscience.kmath.tensors.core -import space.kscience.kmath.nd.MutableStructure1D -import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.TensorStructure @@ -18,9 +16,6 @@ public open class BufferedTensor( public val numel: Int get() = linearStructure.size - internal constructor(tensor: BufferedTensor) : - this(tensor.shape, tensor.buffer, tensor.bufferStart) - override fun get(index: IntArray): T = buffer[bufferStart + linearStructure.offset(index)] override fun set(index: IntArray, value: T) { @@ -35,39 +30,6 @@ public open class BufferedTensor( override fun hashCode(): Int = 0 - internal fun vectorSequence(): Sequence> = sequence { - val n = shape.size - val vectorOffset = shape[n - 1] - val vectorShape = intArrayOf(shape.last()) - for (offset in 0 until numel step vectorOffset) { - val vector = BufferedTensor(vectorShape, buffer, offset) - yield(vector) - } - } - - internal fun matrixSequence(): Sequence> = sequence { - check(shape.size >= 2) { "todo" } - val n = shape.size - val matrixOffset = shape[n - 1] * shape[n - 2] - val matrixShape = intArrayOf(shape[n - 2], shape[n - 1]) - for (offset in 0 until numel step matrixOffset) { - val matrix = BufferedTensor(matrixShape, buffer, offset) - yield(matrix) - } - } - - internal inline fun forEachVector(vectorAction: (BufferedTensor) -> Unit): Unit { - for (vector in vectorSequence()) { - vectorAction(vector) - } - } - - internal inline fun forEachMatrix(matrixAction: (BufferedTensor) -> Unit): Unit { - for (matrix in matrixSequence()) { - matrixAction(matrix) - } - } - } public class IntTensor internal constructor( diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt index 0d19fd3f6..b81cf0364 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt @@ -1,11 +1,8 @@ package space.kscience.kmath.tensors.core -import space.kscience.kmath.nd.MutableStructure1D -import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.tensors.LinearOpsTensorAlgebra import space.kscience.kmath.nd.as1D import space.kscience.kmath.nd.as2D -import space.kscience.kmath.tensors.LinearOpsTensorAlgebra -import kotlin.math.sqrt public class DoubleLinearOpsTensorAlgebra : LinearOpsTensorAlgebra, @@ -15,48 +12,6 @@ public class DoubleLinearOpsTensorAlgebra : override fun DoubleTensor.det(): DoubleTensor = detLU() - private inline fun luHelper(lu: MutableStructure2D, pivots: MutableStructure1D, m: Int) { - for (row in 0 until m) pivots[row] = row - - for (i in 0 until m) { - var maxVal = -1.0 - var maxInd = i - - for (k in i until m) { - val absA = kotlin.math.abs(lu[k, i]) - if (absA > maxVal) { - maxVal = absA - maxInd = k - } - } - - //todo check singularity - - if (maxInd != i) { - - val j = pivots[i] - pivots[i] = pivots[maxInd] - pivots[maxInd] = j - - for (k in 0 until m) { - val tmp = lu[i, k] - lu[i, k] = lu[maxInd, k] - lu[maxInd, k] = tmp - } - - pivots[m] += 1 - - } - - for (j in i + 1 until m) { - lu[j, i] /= lu[i, i] - for (k in i + 1 until m) { - lu[j, k] -= lu[j, i] * lu[i, k] - } - } - } - } - override fun DoubleTensor.lu(): Pair { checkSquareMatrix(shape) @@ -80,37 +35,6 @@ public class DoubleLinearOpsTensorAlgebra : } - private inline fun pivInit( - p: MutableStructure2D, - pivot: MutableStructure1D, - n: Int - ) { - for (i in 0 until n) { - p[i, pivot[i]] = 1.0 - } - } - - private inline fun luPivotHelper( - l: MutableStructure2D, - u: MutableStructure2D, - lu: MutableStructure2D, - n: Int - ) { - for (i in 0 until n) { - for (j in 0 until n) { - if (i == j) { - l[i, j] = 1.0 - } - if (j < i) { - l[i, j] = lu[i, j] - } - if (j >= i) { - u[i, j] = lu[i, j] - } - } - } - } - override fun luPivot( luTensor: DoubleTensor, pivotsTensor: IntTensor @@ -139,27 +63,6 @@ public class DoubleLinearOpsTensorAlgebra : } - private inline fun choleskyHelper( - a: MutableStructure2D, - l: MutableStructure2D, - n: Int - ) { - for (i in 0 until n) { - for (j in 0 until i) { - var h = a[i, j] - for (k in 0 until j) { - h -= l[i, k] * l[j, k] - } - l[i, j] = h / l[j, j] - } - var h = a[i, i] - for (j in 0 until i) { - h -= l[i, j] * l[i, j] - } - l[i, i] = sqrt(h) - } - } - override fun DoubleTensor.cholesky(): DoubleTensor { // todo checks checkSquareMatrix(shape) @@ -185,14 +88,6 @@ public class DoubleLinearOpsTensorAlgebra : TODO("ANDREI") } - private fun luMatrixDet(luTensor: MutableStructure2D, pivotsTensor: MutableStructure1D): Double { - val lu = luTensor.as2D() - val pivots = pivotsTensor.as1D() - val m = lu.shape[0] - val sign = if ((pivots[m] - m) % 2 == 0) 1.0 else -1.0 - return (0 until m).asSequence().map { lu[it, it] }.fold(sign) { left, right -> left * right } - } - public fun DoubleTensor.detLU(): DoubleTensor { val (luTensor, pivotsTensor) = lu() val n = shape.size @@ -213,33 +108,6 @@ public class DoubleLinearOpsTensorAlgebra : return detTensor } - private fun luMatrixInv( - lu: MutableStructure2D, - pivots: MutableStructure1D, - invMatrix: MutableStructure2D - ) { - val m = lu.shape[0] - - for (j in 0 until m) { - for (i in 0 until m) { - if (pivots[i] == j) { - invMatrix[i, j] = 1.0 - } - - for (k in 0 until i) { - invMatrix[i, j] -= lu[i, k] * invMatrix[k, j] - } - } - - for (i in m - 1 downTo 0) { - for (k in i + 1 until m) { - invMatrix[i, j] -= lu[i, k] * invMatrix[k, j] - } - invMatrix[i, j] /= lu[i, i] - } - } - } - public fun DoubleTensor.invLU(): DoubleTensor { val (luTensor, pivotsTensor) = lu() val invTensor = luTensor.zeroesLike() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index b418f3647..c878fc58c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -1,8 +1,7 @@ package space.kscience.kmath.tensors.core -import space.kscience.kmath.nd.MutableStructure2D -import space.kscience.kmath.nd.as2D import space.kscience.kmath.tensors.TensorPartialDivisionAlgebra +import space.kscience.kmath.nd.as2D import kotlin.math.abs public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra { @@ -230,23 +229,6 @@ public open class DoubleTensorAlgebra : TensorPartialDivisionAlgebra, - b: MutableStructure2D, - res: MutableStructure2D, - l: Int, m: Int, n: Int - ) { - for (i in 0 until l) { - for (j in 0 until n) { - var curr = 0.0 - for (k in 0 until m) { - curr += a[i, k] * b[k, j] - } - res[i, j] = curr - } - } - } - override fun DoubleTensor.dot(other: DoubleTensor): DoubleTensor { if (this.shape.size == 1 && other.shape.size == 1) { return DoubleTensor(intArrayOf(1), doubleArrayOf(this.times(other).buffer.array().sum())) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/linutils.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/linutils.kt new file mode 100644 index 000000000..e78cc33c7 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/linutils.kt @@ -0,0 +1,188 @@ +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.nd.MutableStructure1D +import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.nd.as1D +import space.kscience.kmath.nd.as2D +import kotlin.math.sqrt + + +internal inline fun BufferedTensor.vectorSequence(): Sequence> = sequence { + val n = shape.size + val vectorOffset = shape[n - 1] + val vectorShape = intArrayOf(shape.last()) + for (offset in 0 until numel step vectorOffset) { + val vector = BufferedTensor(vectorShape, buffer, offset) + yield(vector) + } +} + +internal inline fun BufferedTensor.matrixSequence(): Sequence> = sequence { + check(shape.size >= 2) { "todo" } + val n = shape.size + val matrixOffset = shape[n - 1] * shape[n - 2] + val matrixShape = intArrayOf(shape[n - 2], shape[n - 1]) + for (offset in 0 until numel step matrixOffset) { + val matrix = BufferedTensor(matrixShape, buffer, offset) + yield(matrix) + } +} + +internal inline fun BufferedTensor.forEachVector(vectorAction: (BufferedTensor) -> Unit): Unit { + for (vector in vectorSequence()) { + vectorAction(vector) + } +} + +internal inline fun BufferedTensor.forEachMatrix(matrixAction: (BufferedTensor) -> Unit): Unit { + for (matrix in matrixSequence()) { + matrixAction(matrix) + } +} + + +internal inline fun dotHelper( + a: MutableStructure2D, + b: MutableStructure2D, + res: MutableStructure2D, + l: Int, m: Int, n: Int +) { + for (i in 0 until l) { + for (j in 0 until n) { + var curr = 0.0 + for (k in 0 until m) { + curr += a[i, k] * b[k, j] + } + res[i, j] = curr + } + } +} + +internal inline fun luHelper(lu: MutableStructure2D, pivots: MutableStructure1D, m: Int) { + for (row in 0 until m) pivots[row] = row + + for (i in 0 until m) { + var maxVal = -1.0 + var maxInd = i + + for (k in i until m) { + val absA = kotlin.math.abs(lu[k, i]) + if (absA > maxVal) { + maxVal = absA + maxInd = k + } + } + + //todo check singularity + + if (maxInd != i) { + + val j = pivots[i] + pivots[i] = pivots[maxInd] + pivots[maxInd] = j + + for (k in 0 until m) { + val tmp = lu[i, k] + lu[i, k] = lu[maxInd, k] + lu[maxInd, k] = tmp + } + + pivots[m] += 1 + + } + + for (j in i + 1 until m) { + lu[j, i] /= lu[i, i] + for (k in i + 1 until m) { + lu[j, k] -= lu[j, i] * lu[i, k] + } + } + } +} + +internal inline fun pivInit( + p: MutableStructure2D, + pivot: MutableStructure1D, + n: Int +) { + for (i in 0 until n) { + p[i, pivot[i]] = 1.0 + } +} + +internal inline fun luPivotHelper( + l: MutableStructure2D, + u: MutableStructure2D, + lu: MutableStructure2D, + n: Int +) { + for (i in 0 until n) { + for (j in 0 until n) { + if (i == j) { + l[i, j] = 1.0 + } + if (j < i) { + l[i, j] = lu[i, j] + } + if (j >= i) { + u[i, j] = lu[i, j] + } + } + } +} + +internal inline fun choleskyHelper( + a: MutableStructure2D, + l: MutableStructure2D, + n: Int +) { + for (i in 0 until n) { + for (j in 0 until i) { + var h = a[i, j] + for (k in 0 until j) { + h -= l[i, k] * l[j, k] + } + l[i, j] = h / l[j, j] + } + var h = a[i, i] + for (j in 0 until i) { + h -= l[i, j] * l[i, j] + } + l[i, i] = sqrt(h) + } +} + +internal inline fun luMatrixDet(luTensor: MutableStructure2D, pivotsTensor: MutableStructure1D): Double { + val lu = luTensor.as2D() + val pivots = pivotsTensor.as1D() + val m = lu.shape[0] + val sign = if ((pivots[m] - m) % 2 == 0) 1.0 else -1.0 + return (0 until m).asSequence().map { lu[it, it] }.fold(sign) { left, right -> left * right } +} + +internal inline fun luMatrixInv( + lu: MutableStructure2D, + pivots: MutableStructure1D, + invMatrix: MutableStructure2D +) { + val m = lu.shape[0] + + for (j in 0 until m) { + for (i in 0 until m) { + if (pivots[i] == j) { + invMatrix[i, j] = 1.0 + } + + for (k in 0 until i) { + invMatrix[i, j] -= lu[i, k] * invMatrix[k, j] + } + } + + for (i in m - 1 downTo 0) { + for (k in i + 1 until m) { + invMatrix[i, j] -= lu[i, k] * invMatrix[k, j] + } + invMatrix[i, j] /= lu[i, i] + } + } +} -- 2.34.1 From 2503d35ba8629055aaffc03f6b2e12e2030d7dd3 Mon Sep 17 00:00:00 2001 From: Andrei Kislitsyn Date: Tue, 30 Mar 2021 14:53:19 +0300 Subject: [PATCH 3/4] complete qr + test qr and lu --- .../core/DoubleLinearOpsTensorAlgebra.kt | 51 +++++++++++++++++-- .../core/TestDoubleLinearOpsAlgebra.kt | 46 +++++++++++++++++ 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt index 1558e6af9..59ec944f2 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt @@ -118,7 +118,8 @@ public class DoubleLinearOpsTensorAlgebra : //todo checks checkSquareMatrix(luTensor.shape) check( - luTensor.shape.dropLast(1).toIntArray() contentEquals pivotsTensor.shape + luTensor.shape.dropLast(2).toIntArray() contentEquals pivotsTensor.shape.dropLast(1).toIntArray() || + luTensor.shape.last() == pivotsTensor.shape.last() - 1 ) { "Bed shapes ((" } //todo rewrite val n = luTensor.shape.last() @@ -173,16 +174,56 @@ public class DoubleLinearOpsTensorAlgebra : return lTensor } - private fun matrixQR( - matrix: Structure2D, + private fun MutableStructure1D.dot(other: MutableStructure1D): Double { + var res = 0.0 + for (i in 0 until size) { + res += this[i] * other[i] + } + return res + } + + private fun MutableStructure1D.l2Norm(): Double { + var squareSum = 0.0 + for (i in 0 until size) { + squareSum += this[i] * this[i] + } + return sqrt(squareSum) + } + + fun qrHelper( + matrix: MutableStructure2D, q: MutableStructure2D, r: MutableStructure2D ) { - + //todo check square + val n = matrix.colNum + for (j in 0 until n) { + val v = matrix.columns[j] + if (j > 0) { + for (i in 0 until j) { + r[i, j] = q.columns[i].dot(matrix.columns[j]) + for (k in 0 until n) { + v[k] = v[k] - r[i, j] * q.columns[i][k] + } + } + } + r[j, j] = v.l2Norm() + for (i in 0 until n) { + q[i, j] = v[i] / r[j, j] + } + } } override fun DoubleTensor.qr(): Pair { - TODO("ANDREI") + checkSquareMatrix(shape) + val qTensor = zeroesLike() + val rTensor = zeroesLike() + val seq = matrixSequence().zip((qTensor.matrixSequence().zip(rTensor.matrixSequence()))) + for ((matrix, qr) in seq) { + val (q, r) = qr + qrHelper(matrix.as2D(), q.as2D(), r.as2D()) + } + return Pair(qTensor, rTensor) } override fun DoubleTensor.svd(): Triple { diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt index d2d4ffb67..b79c54dd1 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleLinearOpsAlgebra.kt @@ -76,4 +76,50 @@ class TestDoubleLinearOpsTensorAlgebra { val b = fromArray(intArrayOf(3), doubleArrayOf(5.5, 2.6, 6.4)) assertEquals(a.dot(b).value(), 59.92) } + + @Test + fun testQR() = DoubleLinearOpsTensorAlgebra { + val shape = intArrayOf(2, 2, 2) + val buffer = doubleArrayOf( + 1.0, 3.0, + 1.0, 2.0, + 1.5, 1.0, + 10.0, 2.0 + ) + + val tensor = fromArray(shape, buffer) + + val (q, r) = tensor.qr() + + assertTrue { q.shape contentEquals shape } + assertTrue { r.shape contentEquals shape } + + assertTrue { q.dot(r).buffer.array().epsEqual(buffer) } + + //todo check orthogonality/upper triang. + } + + @Test + fun testLU() = DoubleLinearOpsTensorAlgebra { + val shape = intArrayOf(2, 2, 2) + val buffer = doubleArrayOf( + 1.0, 3.0, + 1.0, 2.0, + 1.5, 1.0, + 10.0, 2.0 + ) + val tensor = fromArray(shape, buffer) + + val (lu, pivots) = tensor.lu() + + // todo check lu + + val (p, l, u) = luPivot(lu, pivots) + + assertTrue { p.shape contentEquals shape } + assertTrue { l.shape contentEquals shape } + assertTrue { u.shape contentEquals shape } + + assertTrue { p.dot(tensor).buffer.array().epsEqual(l.dot(u).buffer.array()) } + } } -- 2.34.1 From 139534fdb343b984c94f5bc089df34b9e1d5946b Mon Sep 17 00:00:00 2001 From: Andrei Kislitsyn Date: Tue, 30 Mar 2021 15:13:45 +0300 Subject: [PATCH 4/4] fix --- .../kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt index 59ec944f2..6be50e710 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleLinearOpsTensorAlgebra.kt @@ -183,11 +183,7 @@ public class DoubleLinearOpsTensorAlgebra : } private fun MutableStructure1D.l2Norm(): Double { - var squareSum = 0.0 - for (i in 0 until size) { - squareSum += this[i] * this[i] - } - return sqrt(squareSum) + return sqrt((0 until size).sumOf { this[it] * this[it] }) } fun qrHelper( @@ -223,7 +219,7 @@ public class DoubleLinearOpsTensorAlgebra : val (q, r) = qr qrHelper(matrix.as2D(), q.as2D(), r.as2D()) } - return Pair(qTensor, rTensor) + return qTensor to rTensor } override fun DoubleTensor.svd(): Triple { -- 2.34.1