From 10f84bd630ae6654fb03898e1fd2d50300976db7 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 3 May 2023 21:14:29 +0300 Subject: [PATCH 01/34] added function solve --- .../kmath/tensors/api/LinearOpsTensorAlgebra.kt | 14 ++++++++++++++ .../kmath/tensors/core/DoubleTensorAlgebra.kt | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index faff2eb80..60865ecba 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -5,8 +5,15 @@ package space.kscience.kmath.tensors.api +import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.nd.as2D import space.kscience.kmath.operations.Field +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.map +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra /** * Common linear algebra operations. Operates on [Tensor]. @@ -103,4 +110,11 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision */ public fun symEig(structureND: StructureND): Pair, StructureND> + /** Returns the solution to the equation Ax = B for the square matrix A as `input1` and + * for the square matrix B as `input2`. + * + * @receiver the `input1` and the `input2`. + * @return the square matrix x which is the solution of the equation. + */ + public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 70a3ef7e2..1571eb7f1 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -711,6 +711,12 @@ public open class DoubleTensorAlgebra : override fun symEig(structureND: StructureND): Pair = symEigJacobi(structureND = structureND, maxIteration = 50, epsilon = 1e-15) + override fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val aSvd = DoubleTensorAlgebra.svd(a) + val s = BroadcastDoubleTensorAlgebra.diagonalEmbedding(aSvd.second.map {1.0 / it}) + val aInverse = aSvd.third.dot(s).dot(aSvd.first.transposed()) + return aInverse.dot(b).as2D() + } } public val Double.Companion.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra From 19c1af18743b899e566c254a121cd4a961d0ca59 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 3 May 2023 21:25:30 +0300 Subject: [PATCH 02/34] added helper functions for levenberg-marquardt algorithm --- .../kmath/tensors/core/internal/linUtils.kt | 228 ++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index cf3697e76..c559803ec 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -12,7 +12,14 @@ import space.kscience.kmath.structures.IntBuffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices import space.kscience.kmath.tensors.core.* +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.minus +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.times +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import kotlin.math.abs +import kotlin.math.max import kotlin.math.min import kotlin.math.sqrt @@ -308,3 +315,224 @@ internal fun DoubleTensorAlgebra.svdHelper( matrixV.source[i] = vBuffer[i] } } + +data class LMSettings ( + var iteration:Int, + var func_calls: Int, + var example_number:Int +) + +/* matrix -> column of all elemnets */ +fun make_column(tensor: MutableStructure2D) : MutableStructure2D { + val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) + var buffer = DoubleArray(tensor.shape.component1() * tensor.shape.component2()) + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + buffer[i * tensor.shape.component2() + j] = tensor[i, j] + } + } + var column = BroadcastDoubleTensorAlgebra.fromArray(ShapeND(shape), buffer).as2D() + return column +} + +/* column length */ +fun length(column: MutableStructure2D) : Int { + return column.shape.component1() +} + +fun MutableStructure2D.abs() { + for (i in 0 until this.shape.component1()) { + for (j in 0 until this.shape.component2()) { + this[i, j] = abs(this[i, j]) + } + } +} + +fun abs(input: MutableStructure2D): MutableStructure2D { + val tensor = BroadcastDoubleTensorAlgebra.ones( + ShapeND( + intArrayOf( + input.shape.component1(), + input.shape.component2() + ) + ) + ).as2D() + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + tensor[i, j] = abs(input[i, j]) + } + } + return tensor +} + +fun diag(input: MutableStructure2D): MutableStructure2D { + val tensor = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(input.shape.component1(), 1))).as2D() + for (i in 0 until tensor.shape.component1()) { + tensor[i, 0] = input[i, i] + } + return tensor +} + +fun make_matrx_with_diagonal(column: MutableStructure2D): MutableStructure2D { + val size = column.shape.component1() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(size, size))).as2D() + for (i in 0 until size) { + tensor[i, i] = column[i, 0] + } + return tensor +} + +fun lm_eye(size: Int): MutableStructure2D { + val column = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(size, 1))).as2D() + return make_matrx_with_diagonal(column) +} + +fun largest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val a_sizeX = a.shape.component1() + val a_sizeY = a.shape.component2() + val b_sizeX = b.shape.component1() + val b_sizeY = b.shape.component2() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { + tensor[i, j] = max(a[i, j], b[i, j]) + } + else if (i < a_sizeX && j < a_sizeY) { + tensor[i, j] = a[i, j] + } + else { + tensor[i, j] = b[i, j] + } + } + } + return tensor +} + +fun smallest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val a_sizeX = a.shape.component1() + val a_sizeY = a.shape.component2() + val b_sizeX = b.shape.component1() + val b_sizeY = b.shape.component2() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { + tensor[i, j] = min(a[i, j], b[i, j]) + } + else if (i < a_sizeX && j < a_sizeY) { + tensor[i, j] = a[i, j] + } + else { + tensor[i, j] = b[i, j] + } + } + } + return tensor +} + +fun get_zero_indices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { + var idx = emptyArray() + for (i in 0 until column.shape.component1()) { + if (abs(column[i, 0]) > epsilon) { + idx += (i + 1.0) + } + } + if (idx.size > 0) { + return BroadcastDoubleTensorAlgebra.fromArray(ShapeND(intArrayOf(idx.size, 1)), idx.toDoubleArray()).as2D() + } + return null +} + +fun feval(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings) + : MutableStructure2D +{ + return func(t, p, settings) +} + +fun lm_matx(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, + t: MutableStructure2D, p_old: MutableStructure2D, y_old: MutableStructure2D, + dX2: Int, J_input: MutableStructure2D, p: MutableStructure2D, + y_dat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> +{ + // default: dp = 0.001 + + val Npnt = length(y_dat) // number of data points + val Npar = length(p) // number of parameters + + val y_hat = feval(func, t, p, settings) // evaluate model using parameters 'p' + settings.func_calls += 1 + + var J = J_input + + if (settings.iteration % (2 * Npar) == 0 || dX2 > 0) { + J = lm_FD_J(func, t, p, y_hat, dp, settings).as2D() // finite difference + } + else { + J = lm_Broyden_J(p_old, y_old, J, p, y_hat).as2D() // rank-1 update + } + + val delta_y = y_dat.minus(y_hat) + + val Chi_sq = delta_y.transposed().dot( delta_y.times(weight) ).as2D() + val JtWJ = J.transposed().dot ( J.times( weight.dot(BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(1, Npar)))) ) ).as2D() + val JtWdy = J.transposed().dot( weight.times(delta_y) ).as2D() + + return arrayOf(JtWJ,JtWdy,Chi_sq,y_hat,J) +} + +fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructure2D, J_input: MutableStructure2D, + p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { + var J = J_input.copyToTensor() + + val h = p.minus(p_old) + val increase = y.minus(y_old).minus( J.dot(h) ).dot(h.transposed()).div( (h.transposed().dot(h)).as2D()[0, 0] ) + J = J.plus(increase) + + return J.as2D() +} + +fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D, settings: LMSettings) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, + dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { + // default: dp = 0.001 * ones(1,n) + + val m = length(y) // number of data points + val n = length(p) // number of parameters + + val ps = p.copyToTensor().as2D() + val J = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, n))).as2D() // initialize Jacobian to Zero + val del = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(n, 1))).as2D() + + for (j in 0 until n) { + + del[j, 0] = dp[j, 0] * (1 + abs(p[j, 0])) // parameter perturbation + p[j, 0] = ps[j, 0] + del[j, 0] // perturb parameter p(j) + + val epsilon = 0.0000001 + if (kotlin.math.abs(del[j, 0]) > epsilon) { + val y1 = feval(func, t, p, settings) + settings.func_calls += 1 + + if (dp[j, 0] < 0) { // backwards difference + for (i in 0 until J.shape.component1()) { + J[i, j] = (y1.as2D().minus(y).as2D())[i, 0] / del[j, 0] + } + } + else { + // Do tests for it + println("Potential mistake") + p[j, 0] = ps[j, 0] - del[j, 0] // central difference, additional func call + for (i in 0 until J.shape.component1()) { + J[i, j] = (y1.as2D().minus(feval(func, t, p, settings)).as2D())[i, 0] / (2 * del[j, 0]) + } + settings.func_calls += 1 + } + } + + p[j, 0] = ps[j, 0] // restore p(j) + } + + return J.as2D() +} From 89a5522144b2052278c252f833ec678628918b06 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Thu, 4 May 2023 00:44:18 +0300 Subject: [PATCH 03/34] added new svd algorithm (Golub Kahan) and used by default for svd --- .../space/kscience/kmath/ejml/_generated.kt | 2 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 2 +- .../kmath/tensors/core/internal/linUtils.kt | 301 +++++++++++++++++- .../kscience/kmath/tensors/core/tensorOps.kt | 30 ++ 4 files changed, 329 insertions(+), 6 deletions(-) diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index c56583fa8..8ad7f7293 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -19,9 +19,9 @@ import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC -import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix +import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.FloatField diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 1571eb7f1..5325fe19e 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -706,7 +706,7 @@ public open class DoubleTensorAlgebra : override fun svd( structureND: StructureND, ): Triple, StructureND, StructureND> = - svd(structureND = structureND, epsilon = 1e-10) + svdGolubKahan(structureND = structureND, epsilon = 1e-10) override fun symEig(structureND: StructureND): Pair = symEigJacobi(structureND = structureND, maxIteration = 50, epsilon = 1e-15) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index c559803ec..6e5456c62 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -7,10 +7,7 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer -import space.kscience.kmath.structures.IntBuffer -import space.kscience.kmath.structures.asBuffer -import space.kscience.kmath.structures.indices +import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.core.* import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot @@ -316,6 +313,302 @@ internal fun DoubleTensorAlgebra.svdHelper( } } +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.svdGolubKahanHelper(u: MutableStructure2D, w: BufferedTensor, + v: MutableStructure2D, 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 = 0 + val wBuffer = w.source + + 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] + } + } +} + data class LMSettings ( var iteration:Int, var func_calls: Int, diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt index e5dc55f68..88ffb0bfe 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt @@ -212,6 +212,36 @@ public fun DoubleTensorAlgebra.svd( return Triple(uTensor.transposed(), sTensor, vTensor.transposed()) } +public fun DoubleTensorAlgebra.svdGolubKahan( + structureND: StructureND, + iterations: Int = 30, epsilon: Double = 1e-10 +): Triple { + val size = structureND.dimension + val commonShape = structureND.shape.slice(0 until size - 2) + val (n, m) = structureND.shape.slice(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 = structureND.asDoubleTensor().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.linearSize + val curMatrix = DoubleTensor( + matrix.shape, + matrix.source.view(0, matrixSize).copy() + ) + curMatrix.as2D().svdGolubKahanHelper(uTensors[index].as2D(), sTensorVectors[index], vTensors[index].as2D(), + iterations, epsilon) + } + + return Triple(uTensor, sTensor, vTensor) +} + /** * Returns eigenvalues and eigenvectors of a real symmetric matrix input or a batch of real symmetric matrices, * represented by a pair `eigenvalues to eigenvectors`. From b526f9a476c3df4d65b945130beebba9f578e76d Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Thu, 4 May 2023 20:05:32 +0300 Subject: [PATCH 04/34] added Levenberg-Marquardt algorithm + test --- .../tensors/api/LinearOpsTensorAlgebra.kt | 8 + .../kmath/tensors/core/DoubleTensorAlgebra.kt | 369 +++++++++++++++++- .../tensors/core/TestDoubleTensorAlgebra.kt | 85 +++- 3 files changed, 455 insertions(+), 7 deletions(-) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index 60865ecba..5cf0081fb 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -14,6 +14,8 @@ import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.map import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.reflect.KFunction3 /** * Common linear algebra operations. Operates on [Tensor]. @@ -117,4 +119,10 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision * @return the square matrix x which is the solution of the equation. */ public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D + + public fun lm( + func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, + p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, + weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, + c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): Double } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 5325fe19e..e7e22afa9 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -9,6 +9,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall +import space.kscience.kmath.linear.transpose import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleBufferOps import space.kscience.kmath.operations.DoubleField @@ -17,10 +18,8 @@ import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.* -import kotlin.math.abs -import kotlin.math.ceil -import kotlin.math.floor -import kotlin.math.sqrt +import kotlin.math.* +import kotlin.reflect.KFunction3 /** * Implementation of basic operations over double tensors and basic algebra operations on them. @@ -717,6 +716,368 @@ public open class DoubleTensorAlgebra : val aInverse = aSvd.third.dot(s).dot(aSvd.first.transposed()) return aInverse.dot(b).as2D() } + + override fun lm( + func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, + p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, + weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, + c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): Double { + + var result_chi_sq = 0.0 + + val tensor_parameter = 0 + val eps:Double = 2.2204e-16 + + var settings = LMSettings(0, 0, example_number) + settings.func_calls = 0 // running count of function evaluations + + var p = p_input + val y_dat = y_dat_input + val t = t_input + + val Npar = length(p) // number of parameters + val Npnt = length(y_dat) // number of data points + var p_old = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters + var y_old = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) + var X2 = 1e-3 / eps // a really big initial Chi-sq value + var X2_old = 1e-3 / eps // a really big initial Chi-sq value + var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix + val DoF = Npnt - Npar + 1 // statistical degrees of freedom + + var corr_p = 0 + var sigma_p = 0 + var sigma_y = 0 + var R_sq = 0 + var cvg_hist = 0 + + if (length(t) != length(y_dat)) { + println("lm.m error: the length of t must equal the length of y_dat") + val length_t = length(t) + val length_y_dat = length(y_dat) + X2 = 0.0 + + corr_p = 0 + sigma_p = 0 + sigma_y = 0 + R_sq = 0 + cvg_hist = 0 + +// if (tensor_parameter != 0) { // Зачем эта проверка? +// return +// } + } + + var weight = weight_input + if (nargin < 5) { + weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((y_dat.transpose().dot(y_dat)).as1D()[0])).as2D() + } + + var dp = dp_input + if (nargin < 6) { + dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.001)).as2D() + } + + var p_min = p_min_input + if (nargin < 7) { + p_min = p + p_min.abs() + p_min = p_min.div(-100.0).as2D() + } + + var p_max = p_max_input + if (nargin < 8) { + p_max = p + p_max.abs() + p_max = p_max.div(100.0).as2D() + } + + var c = c_input + if (nargin < 9) { + c = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(1.0)).as2D() + } + + var opts = opts_input + if (nargin < 10) { + opts = doubleArrayOf(3.0, 10.0 * Npar, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) + } + + val prnt = opts[0] // >1 intermediate results; >2 plots + val MaxIter = opts[1].toInt() // maximum number of iterations + val epsilon_1 = opts[2] // convergence tolerance for gradient + val epsilon_2 = opts[3] // convergence tolerance for parameters + val epsilon_3 = opts[4] // convergence tolerance for Chi-square + val epsilon_4 = opts[5] // determines acceptance of a L-M step + val lambda_0 = opts[6] // initial value of damping paramter, lambda + val lambda_UP_fac = opts[7] // factor for increasing lambda + val lambda_DN_fac = opts[8] // factor for decreasing lambda + val Update_Type = opts[9].toInt() // 1: Levenberg-Marquardt lambda update + // 2: Quadratic update + // 3: Nielsen's lambda update equations + + val plotcmd = "figure(102); plot(t(:,1),y_init,''-k'',t(:,1),y_hat,''-b'',t(:,1),y_dat,''o'',''color'',[0,0.6,0],''MarkerSize'',4); title(sprintf(''\\chi^2_\\nu = %f'',X2/DoF)); drawnow" + + p_min = make_column(p_min) + p_max = make_column(p_max) + + if (length(make_column(dp)) == 1) { + dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() + } + + val idx = get_zero_indices(dp) // indices of the parameters to be fit + val Nfit = idx?.shape?.component1() // number of parameters to fit + var stop = false // termination flag + val y_init = feval(func, t, p, settings) // residual error using p_try + + if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector + weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() // !!! need to check + println("using uniform weights for error analysis") + } + else { + weight = make_column(weight) + weight.abs() + } + + // initialize Jacobian with finite difference calculation + var lm_matx_ans = lm_matx(func, t, p_old, y_old,1, J, p, y_dat, weight, dp, settings) + var JtWJ = lm_matx_ans[0] + var JtWdy = lm_matx_ans[1] + X2 = lm_matx_ans[2][0, 0] + var y_hat = lm_matx_ans[3] + J = lm_matx_ans[4] + + if ( abs(JtWdy).max()!! < epsilon_1 ) { + println(" *** Your Initial Guess is Extremely Close to Optimal ***\n") + println(" *** epsilon_1 = %e\n$epsilon_1") + stop = true + } + + var lambda = 1.0 + var nu = 1 + when (Update_Type) { + 1 -> lambda = lambda_0 // Marquardt: init'l lambda + else -> { // Quadratic and Nielsen + lambda = lambda_0 * (diag(JtWJ)).max()!! + nu = 2 + } + } + + X2_old = X2 // previous value of X2 + var cvg_hst = ones(ShapeND(intArrayOf(MaxIter, Npar + 3))) // initialize convergence history + + var h = JtWJ.copyToTensor() + var dX2 = X2 + while (!stop && settings.iteration <= MaxIter) { //--- Start Main Loop + settings.iteration += 1 + + // incremental change in parameters + h = when (Update_Type) { + 1 -> { // Marquardt + val solve = solve(JtWJ.plus(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda)).as2D(), JtWdy) + solve.asDoubleTensor() + } + + else -> { // Quadratic and Nielsen + val solve = solve(JtWJ.plus(lm_eye(Npar).div(1 / lambda)).as2D(), JtWdy) + solve.asDoubleTensor() + } + } + + // big = max(abs(h./p)) > 2; % this is a big step + + // --- Are parameters [p+h] much better than [p] ? + + var p_try = (p + h).as2D() // update the [idx] elements + p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try.as2D()), p_max) // apply constraints + + var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try + + // TODO + //if ~all(isfinite(delta_y)) // floating point error; break + // stop = 1; + // break + //end + + settings.func_calls += 1 + + val tmp = delta_y.times(weight) + var X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria + + val alpha = 1.0 + if (Update_Type == 2) { // Quadratic + // One step of quadratic line update in the h direction for minimum X2 + +// TODO +// val alpha = JtWdy.transpose().dot(h) / ((X2_try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h))) +// alpha = JtWdy'*h / ( (X2_try - X2)/2 + 2*JtWdy'*h ) ; +// h = alpha * h; +// +// p_try = p + h(idx); % update only [idx] elements +// p_try = min(max(p_min,p_try),p_max); % apply constraints +// +// delta_y = y_dat - feval(func,t,p_try,c); % residual error using p_try +// func_calls = func_calls + 1; +// тX2_try = delta_y' * ( delta_y .* weight ); % Chi-squared error criteria + } + + val rho = when (Update_Type) { // Nielsen + 1 -> { + val tmp = h.transposed().dot(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) + X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + } + else -> { + val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) + X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + } + } + + println() + println("rho = " + rho) + + if (rho > epsilon_4) { // it IS significantly better + val dX2 = X2.minus(X2_old) + X2_old = X2 + p_old = p.copyToTensor().as2D() + y_old = y_hat.copyToTensor().as2D() + p = make_column(p_try) // accept p_try + + lm_matx_ans = lm_matx(func, t, p_old, y_old, dX2.toInt(), J, p, y_dat, weight, dp, settings) + // decrease lambda ==> Gauss-Newton method + + JtWJ = lm_matx_ans[0] + JtWdy = lm_matx_ans[1] + X2 = lm_matx_ans[2][0, 0] + y_hat = lm_matx_ans[3] + J = lm_matx_ans[4] + + lambda = when (Update_Type) { + 1 -> { // Levenberg + max(lambda / lambda_DN_fac, 1e-7); + } + 2 -> { // Quadratic + max( lambda / (1 + alpha) , 1e-7 ); + } + else -> { // Nielsen + nu = 2 + lambda * max( 1.0 / 3, 1 - (2 * rho - 1).pow(3) ) + } + } + + // if (prnt > 2) { + // eval(plotcmd) + // } + } + else { // it IS NOT better + X2 = X2_old // do not accept p_try + if (settings.iteration % (2 * Npar) == 0 ) { // rank-1 update of Jacobian + lm_matx_ans = lm_matx(func, t, p_old, y_old,-1, J, p, y_dat, weight, dp, settings) + JtWJ = lm_matx_ans[0] + JtWdy = lm_matx_ans[1] + dX2 = lm_matx_ans[2][0, 0] + y_hat = lm_matx_ans[3] + J = lm_matx_ans[4] + } + + // increase lambda ==> gradient descent method + lambda = when (Update_Type) { + 1 -> { // Levenberg + min(lambda * lambda_UP_fac, 1e7) + } + 2 -> { // Quadratic + lambda + abs(((X2_try.as2D()[0, 0] - X2) / 2) / alpha) + } + else -> { // Nielsen + nu *= 2 + lambda * (nu / 2) + } + } + } + + if (prnt > 1) { + val chi_sq = X2 / DoF + println("Iteration $settings.iteration, func_calls $settings.func_calls | chi_sq=$chi_sq | lambda=$lambda") + print("param: ") + for (pn in 0 until Npar) { + print(p[pn, 0].toString() + " ") + } + print("\ndp/p: ") + for (pn in 0 until Npar) { + print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") + } + result_chi_sq = chi_sq + } + + // update convergence history ... save _reduced_ Chi-square + // cvg_hst(iteration,:) = [ func_calls p' X2/DoF lambda ]; + + if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { + println(" **** Convergence in r.h.s. (\"JtWdy\") ****") + println(" **** epsilon_1 = $epsilon_1") + stop = true + } + if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { + println(" **** Convergence in Parameters ****") + println(" **** epsilon_2 = $epsilon_2") + stop = true + } + if (X2 / DoF < epsilon_3 && settings.iteration > 2) { + println(" **** Convergence in reduced Chi-square **** ") + println(" **** epsilon_3 = $epsilon_3") + stop = true + } + if (settings.iteration == MaxIter) { + println(" !! Maximum Number of Iterations Reached Without Convergence !!") + stop = true + } + } // --- End of Main Loop + + // --- convergence achieved, find covariance and confidence intervals + + // ---- Error Analysis ---- + +// if (weight.shape.component1() == 1 || weight.variance() == 0.0) { +// weight = DoF / (delta_y.transpose().dot(delta_y)) * ones(intArrayOf(Npt, 1)) +// } + +// if (nargout > 1) { +// val redX2 = X2 / DoF +// } +// +// lm_matx_ans = lm_matx(func, t, p_old, y_old, -1, J, p, y_dat, weight, dp) +// JtWJ = lm_matx_ans[0] +// JtWdy = lm_matx_ans[1] +// X2 = lm_matx_ans[2][0, 0] +// y_hat = lm_matx_ans[3] +// J = lm_matx_ans[4] +// +// if (nargout > 2) { // standard error of parameters +// covar_p = inv(JtWJ); +// siif nagma_p = sqrt(diag(covar_p)); +// } +// +// if (nargout > 3) { // standard error of the fit +// /// sigma_y = sqrt(diag(J * covar_p * J')); // slower version of below +// sigma_y = zeros(Npnt,1); +// for i=1:Npnt +// sigma_y(i) = J(i,:) * covar_p * J(i,:)'; +// end +// sigma_y = sqrt(sigma_y); +// } +// +// if (nargout > 4) { // parameter correlation matrix +// corr_p = covar_p ./ [sigma_p*sigma_p']; +// } +// +// if (nargout > 5) { // coefficient of multiple determination +// R_sq = corr([y_dat y_hat]); +// R_sq = R_sq(1,2).^2; +// } +// +// if (nargout > 6) { // convergence history +// cvg_hst = cvg_hst(1:iteration,:); +// } + + return result_chi_sq + } } public val Double.Companion.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt index cae01bed8..2c3b3a231 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -6,11 +6,11 @@ package space.kscience.kmath.tensors.core -import space.kscience.kmath.nd.ShapeND -import space.kscience.kmath.nd.contentEquals -import space.kscience.kmath.nd.get +import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.core.internal.LMSettings import space.kscience.kmath.testutils.assertBufferEquals +import kotlin.math.roundToInt import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -207,4 +207,83 @@ internal class TestDoubleTensorAlgebra { assertTrue { ShapeND(5, 5) contentEquals res.shape } assertEquals(2.0, res[4, 4]) } + + @Test + fun testLM() = DoubleTensorAlgebra { + fun lm_func(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) + + if (settings.example_number == 1) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( + DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) + ) + } + else if (settings.example_number == 2) { + val mt = t.max() + y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + + (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + + (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + + (t.times(1.0 / mt)).pow(4).times(p[3, 0]) + } + else if (settings.example_number == 3) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) + .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) + } + + return y_hat.as2D() + } + + val lm_matx_y_dat = doubleArrayOf( + 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, + 14.7620, 15.1128, 16.0973, 15.1934, 15.8636, 15.4763, 15.6860, 15.1895, 15.3495, 16.6054, + 16.2247, 15.9854, 16.1421, 17.0960, 16.7769, 17.1997, 17.2767, 17.5882, 17.5378, 16.7894, + 17.7648, 18.2512, 18.1581, 16.7037, 17.8475, 17.9081, 18.3067, 17.9632, 18.2817, 19.1427, + 18.8130, 18.5658, 18.0056, 18.4607, 18.5918, 18.2544, 18.3731, 18.7511, 19.3181, 17.3066, + 17.9632, 19.0513, 18.7528, 18.2928, 18.5967, 17.8567, 17.7859, 18.4016, 18.9423, 18.4959, + 17.8000, 18.4251, 17.7829, 17.4645, 17.5221, 17.3517, 17.4637, 17.7563, 16.8471, 17.4558, + 17.7447, 17.1487, 17.3183, 16.8312, 17.7551, 17.0942, 15.6093, 16.4163, 15.3755, 16.6725, + 16.2332, 16.2316, 16.2236, 16.5361, 15.3721, 15.3347, 15.5815, 15.6319, 14.4538, 14.6044, + 14.7665, 13.3718, 15.0587, 13.8320, 14.7873, 13.6824, 14.2579, 14.2154, 13.5818, 13.8157 + ) + + var example_number = 1 + val p_init = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(5.0, 2.0, 0.2, 10.0) + ).as2D() + + var t = ones(ShapeND(intArrayOf(100, 1))).as2D() + for (i in 0 until 100) { + t[i, 0] = t[i, 0] * (i + 1) + } + + val y_dat = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(100, 1)), lm_matx_y_dat + ).as2D() + + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 4.0 } + ).as2D() + + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + + val p_min = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(-50.0, -20.0, -2.0, -100.0) + ).as2D() + + val p_max = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(50.0, 20.0, 2.0, 100.0) + ).as2D() + + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + + val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) + + val chi_sq = lm(::lm_func, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts, 10, example_number) + assertEquals(0.9131, (chi_sq * 10000).roundToInt() / 10000.0) + } } From 64e563340a04b1f8f1ccb3e0c78921ef479aeb7c Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Sun, 7 May 2023 17:26:59 +0300 Subject: [PATCH 05/34] fixed error for chi_sq and added more complete output for lm --- .../tensors/api/LinearOpsTensorAlgebra.kt | 11 +++++- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 38 ++++++++----------- .../tensors/core/TestDoubleTensorAlgebra.kt | 9 ++++- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index 5cf0081fb..3c74263ec 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -120,9 +120,18 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision */ public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D + data class LMResultInfo ( + var iterations:Int, + var func_calls: Int, + var example_number: Int, + var result_chi_sq: Double, + var result_lambda: Double, + var result_parameters: MutableStructure2D + ) + public fun lm( func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, - c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): Double + c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LMResultInfo } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index e7e22afa9..21a034676 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -721,9 +721,9 @@ public open class DoubleTensorAlgebra : func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, - c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): Double { + c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LinearOpsTensorAlgebra.LMResultInfo { - var result_chi_sq = 0.0 + var resultInfo = LinearOpsTensorAlgebra.LMResultInfo(0, 0, example_number, 0.0, 0.0, p_input) val tensor_parameter = 0 val eps:Double = 2.2204e-16 @@ -742,7 +742,7 @@ public open class DoubleTensorAlgebra : var X2 = 1e-3 / eps // a really big initial Chi-sq value var X2_old = 1e-3 / eps // a really big initial Chi-sq value var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix - val DoF = Npnt - Npar + 1 // statistical degrees of freedom + val DoF = Npnt - Npar // statistical degrees of freedom var corr_p = 0 var sigma_p = 0 @@ -811,10 +811,8 @@ public open class DoubleTensorAlgebra : val lambda_UP_fac = opts[7] // factor for increasing lambda val lambda_DN_fac = opts[8] // factor for decreasing lambda val Update_Type = opts[9].toInt() // 1: Levenberg-Marquardt lambda update - // 2: Quadratic update - // 3: Nielsen's lambda update equations - - val plotcmd = "figure(102); plot(t(:,1),y_init,''-k'',t(:,1),y_hat,''-b'',t(:,1),y_dat,''o'',''color'',[0,0.6,0],''MarkerSize'',4); title(sprintf(''\\chi^2_\\nu = %f'',X2/DoF)); drawnow" + // 2: Quadratic update + // 3: Nielsen's lambda update equations p_min = make_column(p_min) p_max = make_column(p_max) @@ -829,7 +827,7 @@ public open class DoubleTensorAlgebra : val y_init = feval(func, t, p, settings) // residual error using p_try if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector - weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() // !!! need to check + weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / abs(weight[0, 0])).as2D() // !!! need to check println("using uniform weights for error analysis") } else { @@ -864,7 +862,7 @@ public open class DoubleTensorAlgebra : X2_old = X2 // previous value of X2 var cvg_hst = ones(ShapeND(intArrayOf(MaxIter, Npar + 3))) // initialize convergence history - var h = JtWJ.copyToTensor() + var h: DoubleTensor var dX2 = X2 while (!stop && settings.iteration <= MaxIter) { //--- Start Main Loop settings.iteration += 1 @@ -882,10 +880,6 @@ public open class DoubleTensorAlgebra : } } - // big = max(abs(h./p)) > 2; % this is a big step - - // --- Are parameters [p+h] much better than [p] ? - var p_try = (p + h).as2D() // update the [idx] elements p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try.as2D()), p_max) // apply constraints @@ -961,10 +955,6 @@ public open class DoubleTensorAlgebra : lambda * max( 1.0 / 3, 1 - (2 * rho - 1).pow(3) ) } } - - // if (prnt > 2) { - // eval(plotcmd) - // } } else { // it IS NOT better X2 = X2_old // do not accept p_try @@ -994,7 +984,7 @@ public open class DoubleTensorAlgebra : if (prnt > 1) { val chi_sq = X2 / DoF - println("Iteration $settings.iteration, func_calls $settings.func_calls | chi_sq=$chi_sq | lambda=$lambda") + println("Iteration $settings | chi_sq=$chi_sq | lambda=$lambda") print("param: ") for (pn in 0 until Npar) { print(p[pn, 0].toString() + " ") @@ -1003,7 +993,11 @@ public open class DoubleTensorAlgebra : for (pn in 0 until Npar) { print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") } - result_chi_sq = chi_sq + resultInfo.iterations = settings.iteration + resultInfo.func_calls = settings.func_calls + resultInfo.result_chi_sq = chi_sq + resultInfo.result_lambda = lambda + resultInfo.result_parameters = p } // update convergence history ... save _reduced_ Chi-square @@ -1076,11 +1070,9 @@ public open class DoubleTensorAlgebra : // cvg_hst = cvg_hst(1:iteration,:); // } - return result_chi_sq + return resultInfo } } public val Double.Companion.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra -public val DoubleField.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra - - +public val DoubleField.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra \ No newline at end of file diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt index 2c3b3a231..e5c35f8fd 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -11,6 +11,7 @@ import space.kscience.kmath.operations.invoke import space.kscience.kmath.tensors.core.internal.LMSettings import space.kscience.kmath.testutils.assertBufferEquals import kotlin.math.roundToInt +import kotlin.math.roundToLong import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -283,7 +284,11 @@ internal class TestDoubleTensorAlgebra { val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) - val chi_sq = lm(::lm_func, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts, 10, example_number) - assertEquals(0.9131, (chi_sq * 10000).roundToInt() / 10000.0) + val result = lm(::lm_func, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts, 10, example_number) + assertEquals(13, result.iterations) + assertEquals(31, result.func_calls) + assertEquals(1, result.example_number) + assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) + assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) } } From cfe8e9bfee19974d083bfd653a25ed1e8f301dd9 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Sun, 7 May 2023 21:34:20 +0300 Subject: [PATCH 06/34] done TODOs, deleted prints and added type of convergence to output of lm --- .../tensors/api/LinearOpsTensorAlgebra.kt | 13 ++- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 95 ++++++++++--------- .../tensors/core/TestDoubleTensorAlgebra.kt | 2 + 3 files changed, 62 insertions(+), 48 deletions(-) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index 3c74263ec..7964656f1 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -120,13 +120,22 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision */ public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D - data class LMResultInfo ( + public enum class TypeOfConvergence{ + inRHS_JtWdy, + inParameters, + inReducedChi_square, + noConvergence + } + + public data class LMResultInfo ( var iterations:Int, var func_calls: Int, var example_number: Int, var result_chi_sq: Double, var result_lambda: Double, - var result_parameters: MutableStructure2D + var result_parameters: MutableStructure2D, + var typeOfConvergence: TypeOfConvergence, + var epsilon: Double ) public fun lm( diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 21a034676..43f097564 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -723,9 +723,9 @@ public open class DoubleTensorAlgebra : weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LinearOpsTensorAlgebra.LMResultInfo { - var resultInfo = LinearOpsTensorAlgebra.LMResultInfo(0, 0, example_number, 0.0, 0.0, p_input) + var resultInfo = LinearOpsTensorAlgebra.LMResultInfo(0, 0, example_number, 0.0, + 0.0, p_input, LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence, 0.0) - val tensor_parameter = 0 val eps:Double = 2.2204e-16 var settings = LMSettings(0, 0, example_number) @@ -751,7 +751,7 @@ public open class DoubleTensorAlgebra : var cvg_hist = 0 if (length(t) != length(y_dat)) { - println("lm.m error: the length of t must equal the length of y_dat") + // println("lm.m error: the length of t must equal the length of y_dat") val length_t = length(t) val length_y_dat = length(y_dat) X2 = 0.0 @@ -761,10 +761,6 @@ public open class DoubleTensorAlgebra : sigma_y = 0 R_sq = 0 cvg_hist = 0 - -// if (tensor_parameter != 0) { // Зачем эта проверка? -// return -// } } var weight = weight_input @@ -827,8 +823,8 @@ public open class DoubleTensorAlgebra : val y_init = feval(func, t, p, settings) // residual error using p_try if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector - weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / abs(weight[0, 0])).as2D() // !!! need to check - println("using uniform weights for error analysis") + weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / abs(weight[0, 0])).as2D() + // println("using uniform weights for error analysis") } else { weight = make_column(weight) @@ -844,8 +840,8 @@ public open class DoubleTensorAlgebra : J = lm_matx_ans[4] if ( abs(JtWdy).max()!! < epsilon_1 ) { - println(" *** Your Initial Guess is Extremely Close to Optimal ***\n") - println(" *** epsilon_1 = %e\n$epsilon_1") +// println(" *** Your Initial Guess is Extremely Close to Optimal ***\n") +// println(" *** epsilon_1 = %e\n$epsilon_1") stop = true } @@ -885,11 +881,14 @@ public open class DoubleTensorAlgebra : var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try - // TODO - //if ~all(isfinite(delta_y)) // floating point error; break - // stop = 1; - // break - //end + for (i in 0 until delta_y.shape.component1()) { // floating point error; break + for (j in 0 until delta_y.shape.component2()) { + if (delta_y[i, j] == Double.POSITIVE_INFINITY || delta_y[i, j] == Double.NEGATIVE_INFINITY) { + stop = true + break + } + } + } settings.func_calls += 1 @@ -900,17 +899,16 @@ public open class DoubleTensorAlgebra : if (Update_Type == 2) { // Quadratic // One step of quadratic line update in the h direction for minimum X2 -// TODO -// val alpha = JtWdy.transpose().dot(h) / ((X2_try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h))) -// alpha = JtWdy'*h / ( (X2_try - X2)/2 + 2*JtWdy'*h ) ; -// h = alpha * h; -// -// p_try = p + h(idx); % update only [idx] elements -// p_try = min(max(p_min,p_try),p_max); % apply constraints -// -// delta_y = y_dat - feval(func,t,p_try,c); % residual error using p_try -// func_calls = func_calls + 1; -// тX2_try = delta_y' * ( delta_y .* weight ); % Chi-squared error criteria + val alpha = JtWdy.transpose().dot(h) / ( (X2_try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h)) ) + h = h.dot(alpha) + p_try = p.plus(h).as2D() // update only [idx] elements + p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try), p_max) // apply constraints + + var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try + settings.func_calls += 1 + + val tmp = delta_y.times(weight) + X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria } val rho = when (Update_Type) { // Nielsen @@ -924,9 +922,6 @@ public open class DoubleTensorAlgebra : } } - println() - println("rho = " + rho) - if (rho > epsilon_4) { // it IS significantly better val dX2 = X2.minus(X2_old) X2_old = X2 @@ -984,15 +979,15 @@ public open class DoubleTensorAlgebra : if (prnt > 1) { val chi_sq = X2 / DoF - println("Iteration $settings | chi_sq=$chi_sq | lambda=$lambda") - print("param: ") - for (pn in 0 until Npar) { - print(p[pn, 0].toString() + " ") - } - print("\ndp/p: ") - for (pn in 0 until Npar) { - print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") - } +// println("Iteration $settings | chi_sq=$chi_sq | lambda=$lambda") +// print("param: ") +// for (pn in 0 until Npar) { +// print(p[pn, 0].toString() + " ") +// } +// print("\ndp/p: ") +// for (pn in 0 until Npar) { +// print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") +// } resultInfo.iterations = settings.iteration resultInfo.func_calls = settings.func_calls resultInfo.result_chi_sq = chi_sq @@ -1004,22 +999,30 @@ public open class DoubleTensorAlgebra : // cvg_hst(iteration,:) = [ func_calls p' X2/DoF lambda ]; if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { - println(" **** Convergence in r.h.s. (\"JtWdy\") ****") - println(" **** epsilon_1 = $epsilon_1") +// println(" **** Convergence in r.h.s. (\"JtWdy\") ****") +// println(" **** epsilon_1 = $epsilon_1") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inRHS_JtWdy + resultInfo.epsilon = epsilon_1 stop = true } if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { - println(" **** Convergence in Parameters ****") - println(" **** epsilon_2 = $epsilon_2") +// println(" **** Convergence in Parameters ****") +// println(" **** epsilon_2 = $epsilon_2") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inParameters + resultInfo.epsilon = epsilon_2 stop = true } if (X2 / DoF < epsilon_3 && settings.iteration > 2) { - println(" **** Convergence in reduced Chi-square **** ") - println(" **** epsilon_3 = $epsilon_3") +// println(" **** Convergence in reduced Chi-square **** ") +// println(" **** epsilon_3 = $epsilon_3") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inReducedChi_square + resultInfo.epsilon = epsilon_3 stop = true } if (settings.iteration == MaxIter) { - println(" !! Maximum Number of Iterations Reached Without Convergence !!") +// println(" !! Maximum Number of Iterations Reached Without Convergence !!") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence + resultInfo.epsilon = 0.0 stop = true } } // --- End of Main Loop diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt index e5c35f8fd..85b81524b 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -8,6 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.core.internal.LMSettings import space.kscience.kmath.testutils.assertBufferEquals import kotlin.math.roundToInt @@ -290,5 +291,6 @@ internal class TestDoubleTensorAlgebra { assertEquals(1, result.example_number) assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) + assertEquals(result.typeOfConvergence, LinearOpsTensorAlgebra.TypeOfConvergence.inParameters) } } From a18fa01100e5f2689ef0842a9190f08e28f0dd5a Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Fri, 26 May 2023 21:53:50 +0300 Subject: [PATCH 07/34] added parameter check in tests --- .../kmath/tensors/core/TestDoubleTensorAlgebra.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt index 85b81524b..5aea9b879 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -292,5 +292,17 @@ internal class TestDoubleTensorAlgebra { assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) assertEquals(result.typeOfConvergence, LinearOpsTensorAlgebra.TypeOfConvergence.inParameters) + val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) + ).as2D() + result.result_parameters = result.result_parameters.map { x -> (x * 1e12).toLong() / 1e12}.as2D() + val receivedParameters = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(result.result_parameters[0, 0], result.result_parameters[1, 0], + result.result_parameters[2, 0], result.result_parameters[3, 0]) + ).as2D() + assertEquals(expectedParameters[0, 0], receivedParameters[0, 0]) + assertEquals(expectedParameters[1, 0], receivedParameters[1, 0]) + assertEquals(expectedParameters[2, 0], receivedParameters[2, 0]) + assertEquals(expectedParameters[3, 0], receivedParameters[3, 0]) } } From ce169461055a1372baed6baae80e662320150b60 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Sat, 27 May 2023 01:16:43 +0300 Subject: [PATCH 08/34] added streaming version of LM --- .../StreamingLm/functionsToOptimize.kt | 109 ++++++++++++++++++ .../tensors/StreamingLm/streamingLmMain.kt | 75 ++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt new file mode 100644 index 000000000..1d0ce7deb --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.StreamingLm + +import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.nd.component1 +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times +import space.kscience.kmath.tensors.core.internal.LMSettings + +public data class StartDataLm ( + var lm_matx_y_dat: MutableStructure2D, + var example_number: Int, + var p_init: MutableStructure2D, + var t: MutableStructure2D, + var y_dat: MutableStructure2D, + var weight: MutableStructure2D, + var dp: MutableStructure2D, + var p_min: MutableStructure2D, + var p_max: MutableStructure2D, + var consts: MutableStructure2D, + var opts: DoubleArray +) + +fun func1ForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + + if (settings.example_number == 1) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( + DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) + ) + } + else if (settings.example_number == 2) { + val mt = t.max() + y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + + (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + + (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + + (t.times(1.0 / mt)).pow(4).times(p[3, 0]) + } + else if (settings.example_number == 3) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) + .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) + } + + return y_hat.as2D() +} + +fun getStartDataForFunc1(): StartDataLm { + val lm_matx_y_dat = doubleArrayOf( + 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, + 14.7620, 15.1128, 16.0973, 15.1934, 15.8636, 15.4763, 15.6860, 15.1895, 15.3495, 16.6054, + 16.2247, 15.9854, 16.1421, 17.0960, 16.7769, 17.1997, 17.2767, 17.5882, 17.5378, 16.7894, + 17.7648, 18.2512, 18.1581, 16.7037, 17.8475, 17.9081, 18.3067, 17.9632, 18.2817, 19.1427, + 18.8130, 18.5658, 18.0056, 18.4607, 18.5918, 18.2544, 18.3731, 18.7511, 19.3181, 17.3066, + 17.9632, 19.0513, 18.7528, 18.2928, 18.5967, 17.8567, 17.7859, 18.4016, 18.9423, 18.4959, + 17.8000, 18.4251, 17.7829, 17.4645, 17.5221, 17.3517, 17.4637, 17.7563, 16.8471, 17.4558, + 17.7447, 17.1487, 17.3183, 16.8312, 17.7551, 17.0942, 15.6093, 16.4163, 15.3755, 16.6725, + 16.2332, 16.2316, 16.2236, 16.5361, 15.3721, 15.3347, 15.5815, 15.6319, 14.4538, 14.6044, + 14.7665, 13.3718, 15.0587, 13.8320, 14.7873, 13.6824, 14.2579, 14.2154, 13.5818, 13.8157 + ) + + var example_number = 1 + val p_init = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(5.0, 2.0, 0.2, 10.0) + ).as2D() + + var t = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(100, 1))).as2D() + for (i in 0 until 100) { + t[i, 0] = t[i, 0] * (i + 1) + } + + val y_dat = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(100, 1)), lm_matx_y_dat + ).as2D() + + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 4.0 } + ).as2D() + + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + + val p_min = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(-50.0, -20.0, -2.0, -100.0) + ).as2D() + + val p_max = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(50.0, 20.0, 2.0, 100.0) + ).as2D() + + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + + val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) + + return StartDataLm(y_dat, example_number, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts) +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt new file mode 100644 index 000000000..e33cfac80 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.StreamingLm + +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.* +import space.kscience.kmath.nd.* +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.zeros +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.math.roundToInt +import kotlin.random.Random +import kotlin.reflect.KFunction3 + +fun streamLm(lm_func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, + startData: StartDataLm, launchFrequencyInMs: Long): Flow> = flow{ + + var example_number = startData.example_number + var p_init = startData.p_init + var t = startData.t + val y_dat = startData.y_dat + val weight = startData.weight + val dp = startData.dp + val p_min = startData.p_min + val p_max = startData.p_max + val consts = startData.consts + val opts = startData.opts + + while (true) { + val result = DoubleTensorAlgebra.lm( + lm_func, + p_init, + t, + y_dat, + weight, + dp, + p_min, + p_max, + consts, + opts, + 10, + example_number + ) + emit(result.result_parameters) + delay(launchFrequencyInMs) + p_init = generateNewParameters(p_init, 0.1) + } +} + +fun generateNewParameters(p: MutableStructure2D, delta: Double): MutableStructure2D{ + val n = p.shape.component1() + val p_new = zeros(ShapeND(intArrayOf(n, 1))).as2D() + for (i in 0 until n) { + val randomEps = Random.nextDouble(delta + delta) - delta + p_new[i, 0] = p[i, 0] + randomEps + } + return p_new +} + +suspend fun main(){ + val startData = getStartDataForFunc1() + // Создание потока: + val numberFlow = streamLm(::func1ForLm, startData, 1000) + // Запуск потока + numberFlow.collect { parameters -> + for (i in 0 until parameters.shape.component1()) { + val x = (parameters[i, 0] * 10000).roundToInt() / 10000.0 + print("$x ") + if (i == parameters.shape.component1() - 1) println() + } + } +} \ No newline at end of file From e738fbc86d27597dcc9521756837026910b46648 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Sat, 27 May 2023 01:24:37 +0300 Subject: [PATCH 09/34] typo fixed --- .../kscience/kmath/tensors/StreamingLm/streamingLmMain.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt index e33cfac80..06105fb9f 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt @@ -63,9 +63,9 @@ fun generateNewParameters(p: MutableStructure2D, delta: Double): Mutable suspend fun main(){ val startData = getStartDataForFunc1() // Создание потока: - val numberFlow = streamLm(::func1ForLm, startData, 1000) + val lmFlow = streamLm(::func1ForLm, startData, 1000) // Запуск потока - numberFlow.collect { parameters -> + lmFlow.collect { parameters -> for (i in 0 until parameters.shape.component1()) { val x = (parameters[i, 0] * 10000).roundToInt() / 10000.0 print("$x ") From 20c20a30e8b7f670f7586efffbb8cf63a4e9e917 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Sat, 27 May 2023 16:07:13 +0300 Subject: [PATCH 10/34] y_dat added generation --- .../kmath/tensors/StreamingLm/streamingLmMain.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt index 06105fb9f..0fa934955 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt @@ -21,7 +21,7 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< var example_number = startData.example_number var p_init = startData.p_init var t = startData.t - val y_dat = startData.y_dat + var y_dat = startData.y_dat val weight = startData.weight val dp = startData.dp val p_min = startData.p_min @@ -46,18 +46,19 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< ) emit(result.result_parameters) delay(launchFrequencyInMs) - p_init = generateNewParameters(p_init, 0.1) + p_init = result.result_parameters + y_dat = generateNewYDat(y_dat, 0.1) } } -fun generateNewParameters(p: MutableStructure2D, delta: Double): MutableStructure2D{ - val n = p.shape.component1() - val p_new = zeros(ShapeND(intArrayOf(n, 1))).as2D() +fun generateNewYDat(y_dat: MutableStructure2D, delta: Double): MutableStructure2D{ + val n = y_dat.shape.component1() + val y_dat_new = zeros(ShapeND(intArrayOf(n, 1))).as2D() for (i in 0 until n) { val randomEps = Random.nextDouble(delta + delta) - delta - p_new[i, 0] = p[i, 0] + randomEps + y_dat_new[i, 0] = y_dat[i, 0] + randomEps } - return p_new + return y_dat_new } suspend fun main(){ From 33cb317ceee1256f3d271dd6723b8d55c57b814f Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Sun, 28 May 2023 23:07:01 +0300 Subject: [PATCH 11/34] added examples and tests --- .../StaticLm/staticDifficultTest.kt | 92 ++++++ .../StaticLm/staticEasyTest.kt | 56 ++++ .../StaticLm/staticMiddleTest.kt | 91 ++++++ .../StreamingLm/streamLm.kt} | 26 +- .../StreamingLm/streamingLmTest.kt | 25 ++ .../functionsToOptimize.kt | 39 ++- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 6 +- .../kmath/tensors/core/TestLmAlgorithm.kt | 267 ++++++++++++++++++ 8 files changed, 579 insertions(+), 23 deletions(-) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt rename examples/src/main/kotlin/space/kscience/kmath/tensors/{StreamingLm/streamingLmMain.kt => LevenbergMarquardt/StreamingLm/streamLm.kt} (72%) create mode 100644 examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt rename examples/src/main/kotlin/space/kscience/kmath/tensors/{StreamingLm => LevenbergMarquardt}/functionsToOptimize.kt (77%) create mode 100644 kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt new file mode 100644 index 000000000..621943aea --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.LevenbergMarquardt.StaticLm + +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.nd.component1 +import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.math.roundToInt + +fun main() { + val NData = 200 + var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + for (i in 0 until NData) { + t_example[i, 0] = t_example[i, 0] * (i + 1) - 104 + } + + val Nparams = 15 + var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_example[i, 0] = p_example[i, 0] + i - 25 + } + + val settings = LMSettings(0, 0, 1) + + var y_hat = funcDifficultForLm(t_example, p_example, settings) + + var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_init[i, 0] = (p_example[i, 0] + 0.9) + } + + var t = t_example + val y_dat = y_hat + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 / Nparams * 1.0 - 0.085 } + ).as2D() + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / -50.0) + val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / 50.0) + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + val opts = doubleArrayOf(3.0, 10000.0, 1e-2, 0.015, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) +// val opts = doubleArrayOf(3.0, 10000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-3, 11.0, 9.0, 1.0) + + val result = DoubleTensorAlgebra.lm( + ::funcDifficultForLm, + p_init.as2D(), + t, + y_dat, + weight, + dp, + p_min.as2D(), + p_max.as2D(), + consts, + opts, + 10, + 1 + ) + + println("Parameters:") + for (i in 0 until result.result_parameters.shape.component1()) { + val x = (result.result_parameters[i, 0] * 10000).roundToInt() / 10000.0 + print("$x ") + } + println() + + println("Y true and y received:") + var y_hat_after = funcDifficultForLm(t_example, result.result_parameters, settings) + for (i in 0 until y_hat.shape.component1()) { + val x = (y_hat[i, 0] * 10000).roundToInt() / 10000.0 + val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0 + println("$x $y") + } + + println("Сhi_sq:") + println(result.result_chi_sq) + println("Number of iterations:") + println(result.iterations) +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt new file mode 100644 index 000000000..bae5a674f --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.LevenbergMarquardt.StaticLm + +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.nd.component1 +import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm +import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm +import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.math.roundToInt + +fun main() { + val startedData = getStartDataForFuncEasy() + + val result = DoubleTensorAlgebra.lm( + ::funcEasyForLm, + DoubleTensorAlgebra.ones(ShapeND(intArrayOf(4, 1))).as2D(), + startedData.t, + startedData.y_dat, + startedData.weight, + startedData.dp, + startedData.p_min, + startedData.p_max, + startedData.consts, + startedData.opts, + 10, + startedData.example_number + ) + + println("Parameters:") + for (i in 0 until result.result_parameters.shape.component1()) { + val x = (result.result_parameters[i, 0] * 10000).roundToInt() / 10000.0 + print("$x ") + } + println() + + println("Y true and y received:") + var y_hat_after = funcDifficultForLm(startedData.t, result.result_parameters, LMSettings(0, 0, startedData.example_number)) + for (i in 0 until startedData.y_dat.shape.component1()) { + val x = (startedData.y_dat[i, 0] * 10000).roundToInt() / 10000.0 + val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0 + println("$x $y") + } + + println("Сhi_sq:") + println(result.result_chi_sq) + println("Number of iterations:") + println(result.iterations) +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt new file mode 100644 index 000000000..a39572858 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.LevenbergMarquardt.StaticLm + +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.nd.component1 +import space.kscience.kmath.tensors.LevenbergMarquardt.funcMiddleForLm +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.math.roundToInt +fun main() { + val NData = 100 + var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + for (i in 0 until NData) { + t_example[i, 0] = t_example[i, 0] * (i + 1) + } + + val Nparams = 20 + var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_example[i, 0] = p_example[i, 0] + i - 25 + } + + val settings = LMSettings(0, 0, 1) + + var y_hat = funcMiddleForLm(t_example, p_example, settings) + + var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_init[i, 0] = (p_example[i, 0] + 0.9) + } +// val p_init = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) +// val p_init = p_example + var t = t_example + val y_dat = y_hat + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 } + ).as2D() + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / -50.0) + val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / 50.0) + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + val opts = doubleArrayOf(3.0, 10000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) + + val result = DoubleTensorAlgebra.lm( + ::funcMiddleForLm, + p_init.as2D(), + t, + y_dat, + weight, + dp, + p_min.as2D(), + p_max.as2D(), + consts, + opts, + 10, + 1 + ) + + println("Parameters:") + for (i in 0 until result.result_parameters.shape.component1()) { + val x = (result.result_parameters[i, 0] * 10000).roundToInt() / 10000.0 + print("$x ") + } + println() + + println("Y true and y received:") + var y_hat_after = funcMiddleForLm(t_example, result.result_parameters, settings) + for (i in 0 until y_hat.shape.component1()) { + val x = (y_hat[i, 0] * 10000).roundToInt() / 10000.0 + val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0 + println("$x $y") + } + + println("Сhi_sq:") + println(result.result_chi_sq) + println("Number of iterations:") + println(result.iterations) +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt similarity index 72% rename from examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt rename to examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index 0fa934955..5a1037618 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/streamingLmMain.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -3,20 +3,20 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.tensors.StreamingLm +package space.kscience.kmath.tensors.LevenbergMarquardt.StreamingLm import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import space.kscience.kmath.nd.* +import space.kscience.kmath.tensors.LevenbergMarquardt.StartDataLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.zeros import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.internal.LMSettings -import kotlin.math.roundToInt import kotlin.random.Random import kotlin.reflect.KFunction3 fun streamLm(lm_func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, - startData: StartDataLm, launchFrequencyInMs: Long): Flow> = flow{ + startData: StartDataLm, launchFrequencyInMs: Long, numberOfLaunches: Int): Flow> = flow{ var example_number = startData.example_number var p_init = startData.p_init @@ -29,7 +29,10 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< val consts = startData.consts val opts = startData.opts - while (true) { + var steps = numberOfLaunches + val isEndless = (steps <= 0) + + while (isEndless || steps > 0) { val result = DoubleTensorAlgebra.lm( lm_func, p_init, @@ -48,6 +51,7 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< delay(launchFrequencyInMs) p_init = result.result_parameters y_dat = generateNewYDat(y_dat, 0.1) + if (!isEndless) steps -= 1 } } @@ -59,18 +63,4 @@ fun generateNewYDat(y_dat: MutableStructure2D, delta: Double): MutableSt y_dat_new[i, 0] = y_dat[i, 0] + randomEps } return y_dat_new -} - -suspend fun main(){ - val startData = getStartDataForFunc1() - // Создание потока: - val lmFlow = streamLm(::func1ForLm, startData, 1000) - // Запуск потока - lmFlow.collect { parameters -> - for (i in 0 until parameters.shape.component1()) { - val x = (parameters[i, 0] * 10000).roundToInt() / 10000.0 - print("$x ") - if (i == parameters.shape.component1() - 1) println() - } - } } \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt new file mode 100644 index 000000000..f81b048e5 --- /dev/null +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.LevenbergMarquardt.StreamingLm + +import space.kscience.kmath.nd.* +import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm +import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy +import kotlin.math.roundToInt + +suspend fun main(){ + val startData = getStartDataForFuncEasy() + // Создание потока: + val lmFlow = streamLm(::funcEasyForLm, startData, 1000, 10) + // Запуск потока + lmFlow.collect { parameters -> + for (i in 0 until parameters.shape.component1()) { + val x = (parameters[i, 0] * 10000).roundToInt() / 10000.0 + print("$x ") + if (i == parameters.shape.component1() - 1) println() + } + } +} \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt similarity index 77% rename from examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt rename to examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt index 1d0ce7deb..191dc5c67 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/StreamingLm/functionsToOptimize.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package space.kscience.kmath.tensors.StreamingLm +package space.kscience.kmath.tensors.LevenbergMarquardt import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.ShapeND @@ -15,6 +15,7 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times +import space.kscience.kmath.tensors.core.asDoubleTensor import space.kscience.kmath.tensors.core.internal.LMSettings public data class StartDataLm ( @@ -31,7 +32,39 @@ public data class StartDataLm ( var opts: DoubleArray ) -fun func1ForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { +fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + + val mt = t.max() + for(i in 0 until p.shape.component1()){ + y_hat = y_hat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) + } + + for(i in 0 until 4){ + y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, settings).asDoubleTensor() + } + + return y_hat.as2D() +} + +fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + + val mt = t.max() + for(i in 0 until p.shape.component1()){ + y_hat += (t.times(1.0 / mt)).times(p[i, 0]) + } + + for(i in 0 until 5){ + y_hat = funcEasyForLm(y_hat.as2D(), p, settings).asDoubleTensor() + } + + return y_hat.as2D() +} + +fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) @@ -55,7 +88,7 @@ fun func1ForLm(t: MutableStructure2D, p: MutableStructure2D, set return y_hat.as2D() } -fun getStartDataForFunc1(): StartDataLm { +fun getStartDataForFuncEasy(): StartDataLm { val lm_matx_y_dat = doubleArrayOf( 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, 14.7620, 15.1128, 16.0973, 15.1934, 15.8636, 15.4763, 15.6860, 15.1895, 15.3495, 16.6054, diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 43f097564..2b8bf84c0 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -765,12 +765,12 @@ public open class DoubleTensorAlgebra : var weight = weight_input if (nargin < 5) { - weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((y_dat.transpose().dot(y_dat)).as1D()[0])).as2D() + fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(1.0)).as2D() } var dp = dp_input if (nargin < 6) { - dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.001)).as2D() + dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(-0.001)).as2D() } var p_min = p_min_input @@ -1023,6 +1023,8 @@ public open class DoubleTensorAlgebra : // println(" !! Maximum Number of Iterations Reached Without Convergence !!") resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence resultInfo.epsilon = 0.0 + print("noConvergence, MaxIter = ") + println(MaxIter) stop = true } } // --- End of Main Loop diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt new file mode 100644 index 000000000..29accbbfa --- /dev/null +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -0,0 +1,267 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.nd.MutableStructure2D +import space.kscience.kmath.nd.ShapeND +import space.kscience.kmath.nd.as2D +import space.kscience.kmath.nd.component1 +import space.kscience.kmath.operations.invoke +import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times +import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.math.roundToLong +import kotlin.test.Test +import kotlin.test.assertEquals + +class TestLmAlgorithm { + companion object { + fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) + + if (settings.example_number == 1) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( + DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) + ) + } + else if (settings.example_number == 2) { + val mt = t.max() + y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + + (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + + (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + + (t.times(1.0 / mt)).pow(4).times(p[3, 0]) + } + else if (settings.example_number == 3) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) + .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) + } + + return y_hat.as2D() + } + + fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + + val mt = t.max() + for(i in 0 until p.shape.component1()){ + y_hat += (t.times(1.0 / mt)).times(p[i, 0]) + } + + for(i in 0 until 5){ + y_hat = funcEasyForLm(y_hat.as2D(), p, settings).asDoubleTensor() + } + + return y_hat.as2D() + } + + fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + val m = t.shape.component1() + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + + val mt = t.max() + for(i in 0 until p.shape.component1()){ + y_hat = y_hat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) + } + + for(i in 0 until 4){ + y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, settings).asDoubleTensor() + } + + return y_hat.as2D() + } + + } + @Test + fun testLMEasy() = DoubleTensorAlgebra { + val lm_matx_y_dat = doubleArrayOf( + 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, + 14.7620, 15.1128, 16.0973, 15.1934, 15.8636, 15.4763, 15.6860, 15.1895, 15.3495, 16.6054, + 16.2247, 15.9854, 16.1421, 17.0960, 16.7769, 17.1997, 17.2767, 17.5882, 17.5378, 16.7894, + 17.7648, 18.2512, 18.1581, 16.7037, 17.8475, 17.9081, 18.3067, 17.9632, 18.2817, 19.1427, + 18.8130, 18.5658, 18.0056, 18.4607, 18.5918, 18.2544, 18.3731, 18.7511, 19.3181, 17.3066, + 17.9632, 19.0513, 18.7528, 18.2928, 18.5967, 17.8567, 17.7859, 18.4016, 18.9423, 18.4959, + 17.8000, 18.4251, 17.7829, 17.4645, 17.5221, 17.3517, 17.4637, 17.7563, 16.8471, 17.4558, + 17.7447, 17.1487, 17.3183, 16.8312, 17.7551, 17.0942, 15.6093, 16.4163, 15.3755, 16.6725, + 16.2332, 16.2316, 16.2236, 16.5361, 15.3721, 15.3347, 15.5815, 15.6319, 14.4538, 14.6044, + 14.7665, 13.3718, 15.0587, 13.8320, 14.7873, 13.6824, 14.2579, 14.2154, 13.5818, 13.8157 + ) + + var example_number = 1 + val p_init = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(5.0, 2.0, 0.2, 10.0) + ).as2D() + + var t = ones(ShapeND(intArrayOf(100, 1))).as2D() + for (i in 0 until 100) { + t[i, 0] = t[i, 0] * (i + 1) + } + + val y_dat = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(100, 1)), lm_matx_y_dat + ).as2D() + + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 4.0 } + ).as2D() + + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + + val p_min = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(-50.0, -20.0, -2.0, -100.0) + ).as2D() + + val p_max = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(50.0, 20.0, 2.0, 100.0) + ).as2D() + + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + + val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) + + val result = lm(::funcEasyForLm, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts, 10, example_number) + assertEquals(13, result.iterations) + assertEquals(31, result.func_calls) + assertEquals(1, result.example_number) + assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) + assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) + assertEquals(result.typeOfConvergence, LinearOpsTensorAlgebra.TypeOfConvergence.inParameters) + val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) + ).as2D() + result.result_parameters = result.result_parameters.map { x -> (x * 1e12).toLong() / 1e12}.as2D() + val receivedParameters = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(4, 1)), doubleArrayOf(result.result_parameters[0, 0], result.result_parameters[1, 0], + result.result_parameters[2, 0], result.result_parameters[3, 0]) + ).as2D() + assertEquals(expectedParameters[0, 0], receivedParameters[0, 0]) + assertEquals(expectedParameters[1, 0], receivedParameters[1, 0]) + assertEquals(expectedParameters[2, 0], receivedParameters[2, 0]) + assertEquals(expectedParameters[3, 0], receivedParameters[3, 0]) + } + + @Test + fun TestLMMiddle() = DoubleTensorAlgebra { + val NData = 100 + var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + for (i in 0 until NData) { + t_example[i, 0] = t_example[i, 0] * (i + 1) + } + + val Nparams = 20 + var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_example[i, 0] = p_example[i, 0] + i - 25 + } + + val settings = LMSettings(0, 0, 1) + + var y_hat = funcMiddleForLm(t_example, p_example, settings) + + var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_init[i, 0] = (p_example[i, 0] + 0.9) + } + + var t = t_example + val y_dat = y_hat + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 } + ).as2D() + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / -50.0) + val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / 50.0) + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) + + val result = DoubleTensorAlgebra.lm( + ::funcMiddleForLm, + p_init.as2D(), + t, + y_dat, + weight, + dp, + p_min.as2D(), + p_max.as2D(), + consts, + opts, + 10, + 1 + ) + } + + @Test + fun TestLMDifficult() = DoubleTensorAlgebra { + val NData = 200 + var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + for (i in 0 until NData) { + t_example[i, 0] = t_example[i, 0] * (i + 1) - 104 + } + + val Nparams = 15 + var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_example[i, 0] = p_example[i, 0] + i - 25 + } + + val settings = LMSettings(0, 0, 1) + + var y_hat = funcDifficultForLm(t_example, p_example, settings) + + var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_init[i, 0] = (p_example[i, 0] + 0.9) + } + + var t = t_example + val y_dat = y_hat + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 / Nparams * 1.0 - 0.085 } + ).as2D() + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / -50.0) + val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / 50.0) + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + val opts = doubleArrayOf(3.0, 7000.0, 1e-2, 1e-1, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) + + val result = DoubleTensorAlgebra.lm( + ::funcDifficultForLm, + p_init.as2D(), + t, + y_dat, + weight, + dp, + p_min.as2D(), + p_max.as2D(), + consts, + opts, + 10, + 1 + ) + +// assertEquals(1.15, (result.result_chi_sq * 1e2).roundToLong() / 1e2) + } +} \ No newline at end of file From 1afb0d0a4c3f88a03e04358e636745937aeab32d Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Mon, 29 May 2023 15:13:13 +0300 Subject: [PATCH 12/34] fixed time for js tests for lm --- kmath-tensors/build.gradle.kts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/kmath-tensors/build.gradle.kts b/kmath-tensors/build.gradle.kts index d27faeeef..fdd841bc3 100644 --- a/kmath-tensors/build.gradle.kts +++ b/kmath-tensors/build.gradle.kts @@ -4,7 +4,13 @@ plugins { kscience{ jvm() - js() + js { + browser { + testTask { + useMocha().timeout = "100000" + } + } + } native() dependencies { From 47600dff23a63e6d11341e509a698288114cb057 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Tue, 6 Jun 2023 00:39:19 +0300 Subject: [PATCH 13/34] tests changed --- .../StaticLm/staticDifficultTest.kt | 4 +- .../StaticLm/staticMiddleTest.kt | 5 +- .../StreamingLm/streamingLmTest.kt | 15 +++- .../LevenbergMarquardt/functionsToOptimize.kt | 87 +++++++++++++++++++ kmath-tensors/build.gradle.kts | 2 +- .../kmath/tensors/core/TestLmAlgorithm.kt | 4 +- 6 files changed, 105 insertions(+), 12 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt index 621943aea..0a502afa8 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt @@ -52,8 +52,8 @@ fun main() { val consts = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) ).as2D() - val opts = doubleArrayOf(3.0, 10000.0, 1e-2, 0.015, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) -// val opts = doubleArrayOf(3.0, 10000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-3, 11.0, 9.0, 1.0) + val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-2, 11.0, 9.0, 1.0) +// val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-3, 11.0, 9.0, 1.0) val result = DoubleTensorAlgebra.lm( ::funcDifficultForLm, diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt index a39572858..02917caf2 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt @@ -12,6 +12,7 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcMiddleForLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times import space.kscience.kmath.tensors.core.internal.LMSettings import kotlin.math.roundToInt fun main() { @@ -52,7 +53,7 @@ fun main() { val consts = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) ).as2D() - val opts = doubleArrayOf(3.0, 10000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) + val opts = doubleArrayOf(3.0, 10000.0, 1e-3, 1e-3, 1e-3, 1e-3, 1e-15, 11.0, 9.0, 1.0) val result = DoubleTensorAlgebra.lm( ::funcMiddleForLm, @@ -76,7 +77,7 @@ fun main() { } println() - println("Y true and y received:") + var y_hat_after = funcMiddleForLm(t_example, result.result_parameters, settings) for (i in 0 until y_hat.shape.component1()) { val x = (y_hat[i, 0] * 10000).roundToInt() / 10000.0 diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt index f81b048e5..c9dd5029e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamingLmTest.kt @@ -6,20 +6,27 @@ package space.kscience.kmath.tensors.LevenbergMarquardt.StreamingLm import space.kscience.kmath.nd.* -import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm -import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy +import space.kscience.kmath.tensors.LevenbergMarquardt.* import kotlin.math.roundToInt suspend fun main(){ - val startData = getStartDataForFuncEasy() + val startData = getStartDataForFuncDifficult() // Создание потока: - val lmFlow = streamLm(::funcEasyForLm, startData, 1000, 10) + val lmFlow = streamLm(::funcDifficultForLm, startData, 0, 100) + var initialTime = System.currentTimeMillis() + var lastTime: Long + val launches = mutableListOf() // Запуск потока lmFlow.collect { parameters -> + lastTime = System.currentTimeMillis() + launches.add(lastTime - initialTime) + initialTime = lastTime for (i in 0 until parameters.shape.component1()) { val x = (parameters[i, 0] * 10000).roundToInt() / 10000.0 print("$x ") if (i == parameters.shape.component1() - 1) println() } } + + println("Average without first is: ${launches.subList(1, launches.size - 1).average()}") } \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt index 191dc5c67..5b194ab6b 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt @@ -10,6 +10,7 @@ import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.component1 import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus @@ -17,6 +18,7 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times import space.kscience.kmath.tensors.core.asDoubleTensor import space.kscience.kmath.tensors.core.internal.LMSettings +import kotlin.math.roundToInt public data class StartDataLm ( var lm_matx_y_dat: MutableStructure2D, @@ -88,6 +90,91 @@ fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, return y_hat.as2D() } +fun getStartDataForFuncDifficult(): StartDataLm { + val NData = 200 + var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + for (i in 0 until NData) { + t_example[i, 0] = t_example[i, 0] * (i + 1) - 104 + } + + val Nparams = 15 + var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_example[i, 0] = p_example[i, 0] + i - 25 + } + + val settings = LMSettings(0, 0, 1) + + var y_hat = funcDifficultForLm(t_example, p_example, settings) + + var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_init[i, 0] = (p_example[i, 0] + 0.9) + } + + var t = t_example + val y_dat = y_hat + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 / Nparams * 1.0 - 0.085 } + ).as2D() + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / -50.0) + val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / 50.0) + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + val opts = doubleArrayOf(3.0, 10000.0, 1e-2, 1e-3, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) + + return StartDataLm(y_dat, 1, p_init, t, y_dat, weight, dp, p_min.as2D(), p_max.as2D(), consts, opts) +} + +fun getStartDataForFuncMiddle(): StartDataLm { + val NData = 100 + var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + for (i in 0 until NData) { + t_example[i, 0] = t_example[i, 0] * (i + 1) + } + + val Nparams = 20 + var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_example[i, 0] = p_example[i, 0] + i - 25 + } + + val settings = LMSettings(0, 0, 1) + + var y_hat = funcMiddleForLm(t_example, p_example, settings) + + var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + p_init[i, 0] = (p_example[i, 0] + 10.0) + } + var t = t_example + val y_dat = y_hat + val weight = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 } + ).as2D() + val dp = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } + ).as2D() + var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / -50.0) + val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + p_min = p_min.div(1.0 / 50.0) + val consts = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) + ).as2D() + val opts = doubleArrayOf(3.0, 10000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) + + var example_number = 1 + + return StartDataLm(y_dat, example_number, p_init, t, y_dat, weight, dp, p_min.as2D(), p_max.as2D(), consts, opts) +} + fun getStartDataForFuncEasy(): StartDataLm { val lm_matx_y_dat = doubleArrayOf( 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, diff --git a/kmath-tensors/build.gradle.kts b/kmath-tensors/build.gradle.kts index fdd841bc3..5e82835a7 100644 --- a/kmath-tensors/build.gradle.kts +++ b/kmath-tensors/build.gradle.kts @@ -7,7 +7,7 @@ kscience{ js { browser { testTask { - useMocha().timeout = "100000" + useMocha().timeout = "0" } } } diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 29accbbfa..f554a742c 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -245,7 +245,7 @@ class TestLmAlgorithm { val consts = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) ).as2D() - val opts = doubleArrayOf(3.0, 7000.0, 1e-2, 1e-1, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) + val opts = doubleArrayOf(3.0, 7000.0, 1e-2, 1e-3, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) val result = DoubleTensorAlgebra.lm( ::funcDifficultForLm, @@ -261,7 +261,5 @@ class TestLmAlgorithm { 10, 1 ) - -// assertEquals(1.15, (result.result_chi_sq * 1e2).roundToLong() / 1e2) } } \ No newline at end of file From 8d81d2d8d5a39d0309dbd2c4b70b516092f5bfea Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Tue, 6 Jun 2023 01:41:08 +0300 Subject: [PATCH 14/34] move lm-algorithm from DoubleTensorAlgebra as extension --- .../tensors/api/LinearOpsTensorAlgebra.kt | 14 - .../kmath/tensors/core/DoubleTensorAlgebra.kt | 363 ------------ .../core/LevenbergMarquardtAlgorithm.kt | 553 ++++++++++++++++++ .../kmath/tensors/core/internal/linUtils.kt | 229 +------- .../tensors/core/TestDoubleTensorAlgebra.kt | 100 ---- .../kmath/tensors/core/TestLmAlgorithm.kt | 1 - 6 files changed, 554 insertions(+), 706 deletions(-) create mode 100644 kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index 7964656f1..6b3859316 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -7,15 +7,7 @@ package space.kscience.kmath.tensors.api import space.kscience.kmath.nd.MutableStructure2D import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.nd.as2D import space.kscience.kmath.operations.Field -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.map -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.internal.LMSettings -import kotlin.reflect.KFunction3 /** * Common linear algebra operations. Operates on [Tensor]. @@ -137,10 +129,4 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision var typeOfConvergence: TypeOfConvergence, var epsilon: Double ) - - public fun lm( - func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, - p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, - weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, - c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LMResultInfo } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 2b8bf84c0..c8cf56888 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -9,7 +9,6 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall -import space.kscience.kmath.linear.transpose import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleBufferOps import space.kscience.kmath.operations.DoubleField @@ -19,7 +18,6 @@ import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.* import kotlin.math.* -import kotlin.reflect.KFunction3 /** * Implementation of basic operations over double tensors and basic algebra operations on them. @@ -716,367 +714,6 @@ public open class DoubleTensorAlgebra : val aInverse = aSvd.third.dot(s).dot(aSvd.first.transposed()) return aInverse.dot(b).as2D() } - - override fun lm( - func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, - p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, - weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, - c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LinearOpsTensorAlgebra.LMResultInfo { - - var resultInfo = LinearOpsTensorAlgebra.LMResultInfo(0, 0, example_number, 0.0, - 0.0, p_input, LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence, 0.0) - - val eps:Double = 2.2204e-16 - - var settings = LMSettings(0, 0, example_number) - settings.func_calls = 0 // running count of function evaluations - - var p = p_input - val y_dat = y_dat_input - val t = t_input - - val Npar = length(p) // number of parameters - val Npnt = length(y_dat) // number of data points - var p_old = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters - var y_old = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) - var X2 = 1e-3 / eps // a really big initial Chi-sq value - var X2_old = 1e-3 / eps // a really big initial Chi-sq value - var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix - val DoF = Npnt - Npar // statistical degrees of freedom - - var corr_p = 0 - var sigma_p = 0 - var sigma_y = 0 - var R_sq = 0 - var cvg_hist = 0 - - if (length(t) != length(y_dat)) { - // println("lm.m error: the length of t must equal the length of y_dat") - val length_t = length(t) - val length_y_dat = length(y_dat) - X2 = 0.0 - - corr_p = 0 - sigma_p = 0 - sigma_y = 0 - R_sq = 0 - cvg_hist = 0 - } - - var weight = weight_input - if (nargin < 5) { - fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(1.0)).as2D() - } - - var dp = dp_input - if (nargin < 6) { - dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(-0.001)).as2D() - } - - var p_min = p_min_input - if (nargin < 7) { - p_min = p - p_min.abs() - p_min = p_min.div(-100.0).as2D() - } - - var p_max = p_max_input - if (nargin < 8) { - p_max = p - p_max.abs() - p_max = p_max.div(100.0).as2D() - } - - var c = c_input - if (nargin < 9) { - c = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(1.0)).as2D() - } - - var opts = opts_input - if (nargin < 10) { - opts = doubleArrayOf(3.0, 10.0 * Npar, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) - } - - val prnt = opts[0] // >1 intermediate results; >2 plots - val MaxIter = opts[1].toInt() // maximum number of iterations - val epsilon_1 = opts[2] // convergence tolerance for gradient - val epsilon_2 = opts[3] // convergence tolerance for parameters - val epsilon_3 = opts[4] // convergence tolerance for Chi-square - val epsilon_4 = opts[5] // determines acceptance of a L-M step - val lambda_0 = opts[6] // initial value of damping paramter, lambda - val lambda_UP_fac = opts[7] // factor for increasing lambda - val lambda_DN_fac = opts[8] // factor for decreasing lambda - val Update_Type = opts[9].toInt() // 1: Levenberg-Marquardt lambda update - // 2: Quadratic update - // 3: Nielsen's lambda update equations - - p_min = make_column(p_min) - p_max = make_column(p_max) - - if (length(make_column(dp)) == 1) { - dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() - } - - val idx = get_zero_indices(dp) // indices of the parameters to be fit - val Nfit = idx?.shape?.component1() // number of parameters to fit - var stop = false // termination flag - val y_init = feval(func, t, p, settings) // residual error using p_try - - if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector - weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / abs(weight[0, 0])).as2D() - // println("using uniform weights for error analysis") - } - else { - weight = make_column(weight) - weight.abs() - } - - // initialize Jacobian with finite difference calculation - var lm_matx_ans = lm_matx(func, t, p_old, y_old,1, J, p, y_dat, weight, dp, settings) - var JtWJ = lm_matx_ans[0] - var JtWdy = lm_matx_ans[1] - X2 = lm_matx_ans[2][0, 0] - var y_hat = lm_matx_ans[3] - J = lm_matx_ans[4] - - if ( abs(JtWdy).max()!! < epsilon_1 ) { -// println(" *** Your Initial Guess is Extremely Close to Optimal ***\n") -// println(" *** epsilon_1 = %e\n$epsilon_1") - stop = true - } - - var lambda = 1.0 - var nu = 1 - when (Update_Type) { - 1 -> lambda = lambda_0 // Marquardt: init'l lambda - else -> { // Quadratic and Nielsen - lambda = lambda_0 * (diag(JtWJ)).max()!! - nu = 2 - } - } - - X2_old = X2 // previous value of X2 - var cvg_hst = ones(ShapeND(intArrayOf(MaxIter, Npar + 3))) // initialize convergence history - - var h: DoubleTensor - var dX2 = X2 - while (!stop && settings.iteration <= MaxIter) { //--- Start Main Loop - settings.iteration += 1 - - // incremental change in parameters - h = when (Update_Type) { - 1 -> { // Marquardt - val solve = solve(JtWJ.plus(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda)).as2D(), JtWdy) - solve.asDoubleTensor() - } - - else -> { // Quadratic and Nielsen - val solve = solve(JtWJ.plus(lm_eye(Npar).div(1 / lambda)).as2D(), JtWdy) - solve.asDoubleTensor() - } - } - - var p_try = (p + h).as2D() // update the [idx] elements - p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try.as2D()), p_max) // apply constraints - - var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try - - for (i in 0 until delta_y.shape.component1()) { // floating point error; break - for (j in 0 until delta_y.shape.component2()) { - if (delta_y[i, j] == Double.POSITIVE_INFINITY || delta_y[i, j] == Double.NEGATIVE_INFINITY) { - stop = true - break - } - } - } - - settings.func_calls += 1 - - val tmp = delta_y.times(weight) - var X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria - - val alpha = 1.0 - if (Update_Type == 2) { // Quadratic - // One step of quadratic line update in the h direction for minimum X2 - - val alpha = JtWdy.transpose().dot(h) / ( (X2_try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h)) ) - h = h.dot(alpha) - p_try = p.plus(h).as2D() // update only [idx] elements - p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try), p_max) // apply constraints - - var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try - settings.func_calls += 1 - - val tmp = delta_y.times(weight) - X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria - } - - val rho = when (Update_Type) { // Nielsen - 1 -> { - val tmp = h.transposed().dot(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) - X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] - } - else -> { - val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) - X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] - } - } - - if (rho > epsilon_4) { // it IS significantly better - val dX2 = X2.minus(X2_old) - X2_old = X2 - p_old = p.copyToTensor().as2D() - y_old = y_hat.copyToTensor().as2D() - p = make_column(p_try) // accept p_try - - lm_matx_ans = lm_matx(func, t, p_old, y_old, dX2.toInt(), J, p, y_dat, weight, dp, settings) - // decrease lambda ==> Gauss-Newton method - - JtWJ = lm_matx_ans[0] - JtWdy = lm_matx_ans[1] - X2 = lm_matx_ans[2][0, 0] - y_hat = lm_matx_ans[3] - J = lm_matx_ans[4] - - lambda = when (Update_Type) { - 1 -> { // Levenberg - max(lambda / lambda_DN_fac, 1e-7); - } - 2 -> { // Quadratic - max( lambda / (1 + alpha) , 1e-7 ); - } - else -> { // Nielsen - nu = 2 - lambda * max( 1.0 / 3, 1 - (2 * rho - 1).pow(3) ) - } - } - } - else { // it IS NOT better - X2 = X2_old // do not accept p_try - if (settings.iteration % (2 * Npar) == 0 ) { // rank-1 update of Jacobian - lm_matx_ans = lm_matx(func, t, p_old, y_old,-1, J, p, y_dat, weight, dp, settings) - JtWJ = lm_matx_ans[0] - JtWdy = lm_matx_ans[1] - dX2 = lm_matx_ans[2][0, 0] - y_hat = lm_matx_ans[3] - J = lm_matx_ans[4] - } - - // increase lambda ==> gradient descent method - lambda = when (Update_Type) { - 1 -> { // Levenberg - min(lambda * lambda_UP_fac, 1e7) - } - 2 -> { // Quadratic - lambda + abs(((X2_try.as2D()[0, 0] - X2) / 2) / alpha) - } - else -> { // Nielsen - nu *= 2 - lambda * (nu / 2) - } - } - } - - if (prnt > 1) { - val chi_sq = X2 / DoF -// println("Iteration $settings | chi_sq=$chi_sq | lambda=$lambda") -// print("param: ") -// for (pn in 0 until Npar) { -// print(p[pn, 0].toString() + " ") -// } -// print("\ndp/p: ") -// for (pn in 0 until Npar) { -// print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") -// } - resultInfo.iterations = settings.iteration - resultInfo.func_calls = settings.func_calls - resultInfo.result_chi_sq = chi_sq - resultInfo.result_lambda = lambda - resultInfo.result_parameters = p - } - - // update convergence history ... save _reduced_ Chi-square - // cvg_hst(iteration,:) = [ func_calls p' X2/DoF lambda ]; - - if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { -// println(" **** Convergence in r.h.s. (\"JtWdy\") ****") -// println(" **** epsilon_1 = $epsilon_1") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inRHS_JtWdy - resultInfo.epsilon = epsilon_1 - stop = true - } - if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { -// println(" **** Convergence in Parameters ****") -// println(" **** epsilon_2 = $epsilon_2") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inParameters - resultInfo.epsilon = epsilon_2 - stop = true - } - if (X2 / DoF < epsilon_3 && settings.iteration > 2) { -// println(" **** Convergence in reduced Chi-square **** ") -// println(" **** epsilon_3 = $epsilon_3") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inReducedChi_square - resultInfo.epsilon = epsilon_3 - stop = true - } - if (settings.iteration == MaxIter) { -// println(" !! Maximum Number of Iterations Reached Without Convergence !!") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence - resultInfo.epsilon = 0.0 - print("noConvergence, MaxIter = ") - println(MaxIter) - stop = true - } - } // --- End of Main Loop - - // --- convergence achieved, find covariance and confidence intervals - - // ---- Error Analysis ---- - -// if (weight.shape.component1() == 1 || weight.variance() == 0.0) { -// weight = DoF / (delta_y.transpose().dot(delta_y)) * ones(intArrayOf(Npt, 1)) -// } - -// if (nargout > 1) { -// val redX2 = X2 / DoF -// } -// -// lm_matx_ans = lm_matx(func, t, p_old, y_old, -1, J, p, y_dat, weight, dp) -// JtWJ = lm_matx_ans[0] -// JtWdy = lm_matx_ans[1] -// X2 = lm_matx_ans[2][0, 0] -// y_hat = lm_matx_ans[3] -// J = lm_matx_ans[4] -// -// if (nargout > 2) { // standard error of parameters -// covar_p = inv(JtWJ); -// siif nagma_p = sqrt(diag(covar_p)); -// } -// -// if (nargout > 3) { // standard error of the fit -// /// sigma_y = sqrt(diag(J * covar_p * J')); // slower version of below -// sigma_y = zeros(Npnt,1); -// for i=1:Npnt -// sigma_y(i) = J(i,:) * covar_p * J(i,:)'; -// end -// sigma_y = sqrt(sigma_y); -// } -// -// if (nargout > 4) { // parameter correlation matrix -// corr_p = covar_p ./ [sigma_p*sigma_p']; -// } -// -// if (nargout > 5) { // coefficient of multiple determination -// R_sq = corr([y_dat y_hat]); -// R_sq = R_sq(1,2).^2; -// } -// -// if (nargout > 6) { // convergence history -// cvg_hst = cvg_hst(1:iteration,:); -// } - - return resultInfo - } } public val Double.Companion.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt new file mode 100644 index 000000000..f4b50626a --- /dev/null +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -0,0 +1,553 @@ +/* + * Copyright 2018-2023 KMath contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package space.kscience.kmath.tensors.core + +import space.kscience.kmath.linear.transpose +import space.kscience.kmath.nd.* +import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.minus +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.times +import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed +import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus +import kotlin.math.max +import kotlin.math.min +import kotlin.math.pow +import kotlin.reflect.KFunction3 + +public fun DoubleTensorAlgebra.lm( + func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, + p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, + weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, + c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LinearOpsTensorAlgebra.LMResultInfo { + + val resultInfo = LinearOpsTensorAlgebra.LMResultInfo(0, 0, example_number, 0.0, + 0.0, p_input, LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence, 0.0) + + val eps:Double = 2.2204e-16 + + val settings = LMSettings(0, 0, example_number) + settings.func_calls = 0 // running count of function evaluations + + var p = p_input + val y_dat = y_dat_input + val t = t_input + + val Npar = length(p) // number of parameters + val Npnt = length(y_dat) // number of data points + var p_old = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters + var y_old = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) + var X2 = 1e-3 / eps // a really big initial Chi-sq value + var X2_old = 1e-3 / eps // a really big initial Chi-sq value + var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix + val DoF = Npnt - Npar // statistical degrees of freedom + + var corr_p = 0 + var sigma_p = 0 + var sigma_y = 0 + var R_sq = 0 + var cvg_hist = 0 + + if (length(t) != length(y_dat)) { + // println("lm.m error: the length of t must equal the length of y_dat") + val length_t = length(t) + val length_y_dat = length(y_dat) + X2 = 0.0 + + corr_p = 0 + sigma_p = 0 + sigma_y = 0 + R_sq = 0 + cvg_hist = 0 + } + + var weight = weight_input + if (nargin < 5) { + weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((y_dat.transpose().dot(y_dat)).as1D()[0])).as2D() + } + + var dp = dp_input + if (nargin < 6) { + dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.001)).as2D() + } + + var p_min = p_min_input + if (nargin < 7) { + p_min = p + p_min.abs() + p_min = p_min.div(-100.0).as2D() + } + + var p_max = p_max_input + if (nargin < 8) { + p_max = p + p_max.abs() + p_max = p_max.div(100.0).as2D() + } + + var c = c_input + if (nargin < 9) { + c = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(1.0)).as2D() + } + + var opts = opts_input + if (nargin < 10) { + opts = doubleArrayOf(3.0, 10.0 * Npar, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) + } + + val prnt = opts[0] // >1 intermediate results; >2 plots + val MaxIter = opts[1].toInt() // maximum number of iterations + val epsilon_1 = opts[2] // convergence tolerance for gradient + val epsilon_2 = opts[3] // convergence tolerance for parameters + val epsilon_3 = opts[4] // convergence tolerance for Chi-square + val epsilon_4 = opts[5] // determines acceptance of a L-M step + val lambda_0 = opts[6] // initial value of damping paramter, lambda + val lambda_UP_fac = opts[7] // factor for increasing lambda + val lambda_DN_fac = opts[8] // factor for decreasing lambda + val Update_Type = opts[9].toInt() // 1: Levenberg-Marquardt lambda update + // 2: Quadratic update + // 3: Nielsen's lambda update equations + + p_min = make_column(p_min) + p_max = make_column(p_max) + + if (length(make_column(dp)) == 1) { + dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() + } + + val idx = get_zero_indices(dp) // indices of the parameters to be fit + val Nfit = idx?.shape?.component1() // number of parameters to fit + var stop = false // termination flag + val y_init = feval(func, t, p, settings) // residual error using p_try + + if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector + weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() + // println("using uniform weights for error analysis") + } + else { + weight = make_column(weight) + weight.abs() + } + + // initialize Jacobian with finite difference calculation + var lm_matx_ans = lm_matx(func, t, p_old, y_old,1, J, p, y_dat, weight, dp, settings) + var JtWJ = lm_matx_ans[0] + var JtWdy = lm_matx_ans[1] + X2 = lm_matx_ans[2][0, 0] + var y_hat = lm_matx_ans[3] + J = lm_matx_ans[4] + + if ( abs(JtWdy).max()!! < epsilon_1 ) { +// println(" *** Your Initial Guess is Extremely Close to Optimal ***\n") +// println(" *** epsilon_1 = %e\n$epsilon_1") + stop = true + } + + var lambda = 1.0 + var nu = 1 + when (Update_Type) { + 1 -> lambda = lambda_0 // Marquardt: init'l lambda + else -> { // Quadratic and Nielsen + lambda = lambda_0 * (diag(JtWJ)).max()!! + nu = 2 + } + } + + X2_old = X2 // previous value of X2 + var cvg_hst = ones(ShapeND(intArrayOf(MaxIter, Npar + 3))) // initialize convergence history + + var h: DoubleTensor + var dX2 = X2 + while (!stop && settings.iteration <= MaxIter) { //--- Start Main Loop + settings.iteration += 1 + + // incremental change in parameters + h = when (Update_Type) { + 1 -> { // Marquardt + val solve = solve(JtWJ.plus(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda)).as2D(), JtWdy) + solve.asDoubleTensor() + } + + else -> { // Quadratic and Nielsen + val solve = solve(JtWJ.plus(lm_eye(Npar).div(1 / lambda)).as2D(), JtWdy) + solve.asDoubleTensor() + } + } + + var p_try = (p + h).as2D() // update the [idx] elements + p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try.as2D()), p_max) // apply constraints + + var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try + + for (i in 0 until delta_y.shape.component1()) { // floating point error; break + for (j in 0 until delta_y.shape.component2()) { + if (delta_y[i, j] == Double.POSITIVE_INFINITY || delta_y[i, j] == Double.NEGATIVE_INFINITY) { + stop = true + break + } + } + } + + settings.func_calls += 1 + + val tmp = delta_y.times(weight) + var X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria + + val alpha = 1.0 + if (Update_Type == 2) { // Quadratic + // One step of quadratic line update in the h direction for minimum X2 + + val alpha = JtWdy.transpose().dot(h) / ( (X2_try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h)) ) + h = h.dot(alpha) + p_try = p.plus(h).as2D() // update only [idx] elements + p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try), p_max) // apply constraints + + var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try + settings.func_calls += 1 + + val tmp = delta_y.times(weight) + X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria + } + + val rho = when (Update_Type) { // Nielsen + 1 -> { + val tmp = h.transposed().dot(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) + X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + } + else -> { + val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) + X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + } + } + + if (rho > epsilon_4) { // it IS significantly better + val dX2 = X2.minus(X2_old) + X2_old = X2 + p_old = p.copyToTensor().as2D() + y_old = y_hat.copyToTensor().as2D() + p = make_column(p_try) // accept p_try + + lm_matx_ans = lm_matx(func, t, p_old, y_old, dX2.toInt(), J, p, y_dat, weight, dp, settings) + // decrease lambda ==> Gauss-Newton method + + JtWJ = lm_matx_ans[0] + JtWdy = lm_matx_ans[1] + X2 = lm_matx_ans[2][0, 0] + y_hat = lm_matx_ans[3] + J = lm_matx_ans[4] + + lambda = when (Update_Type) { + 1 -> { // Levenberg + max(lambda / lambda_DN_fac, 1e-7); + } + 2 -> { // Quadratic + max( lambda / (1 + alpha) , 1e-7 ); + } + else -> { // Nielsen + nu = 2 + lambda * max( 1.0 / 3, 1 - (2 * rho - 1).pow(3) ) + } + } + } + else { // it IS NOT better + X2 = X2_old // do not accept p_try + if (settings.iteration % (2 * Npar) == 0 ) { // rank-1 update of Jacobian + lm_matx_ans = lm_matx(func, t, p_old, y_old,-1, J, p, y_dat, weight, dp, settings) + JtWJ = lm_matx_ans[0] + JtWdy = lm_matx_ans[1] + dX2 = lm_matx_ans[2][0, 0] + y_hat = lm_matx_ans[3] + J = lm_matx_ans[4] + } + + // increase lambda ==> gradient descent method + lambda = when (Update_Type) { + 1 -> { // Levenberg + min(lambda * lambda_UP_fac, 1e7) + } + 2 -> { // Quadratic + lambda + kotlin.math.abs(((X2_try.as2D()[0, 0] - X2) / 2) / alpha) + } + else -> { // Nielsen + nu *= 2 + lambda * (nu / 2) + } + } + } + + if (prnt > 1) { + val chi_sq = X2 / DoF +// println("Iteration $settings | chi_sq=$chi_sq | lambda=$lambda") +// print("param: ") +// for (pn in 0 until Npar) { +// print(p[pn, 0].toString() + " ") +// } +// print("\ndp/p: ") +// for (pn in 0 until Npar) { +// print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") +// } + resultInfo.iterations = settings.iteration + resultInfo.func_calls = settings.func_calls + resultInfo.result_chi_sq = chi_sq + resultInfo.result_lambda = lambda + resultInfo.result_parameters = p + } + + // update convergence history ... save _reduced_ Chi-square + // cvg_hst(iteration,:) = [ func_calls p' X2/DoF lambda ]; + + if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { +// println(" **** Convergence in r.h.s. (\"JtWdy\") ****") +// println(" **** epsilon_1 = $epsilon_1") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inRHS_JtWdy + resultInfo.epsilon = epsilon_1 + stop = true + } + if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { +// println(" **** Convergence in Parameters ****") +// println(" **** epsilon_2 = $epsilon_2") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inParameters + resultInfo.epsilon = epsilon_2 + stop = true + } + if (X2 / DoF < epsilon_3 && settings.iteration > 2) { +// println(" **** Convergence in reduced Chi-square **** ") +// println(" **** epsilon_3 = $epsilon_3") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inReducedChi_square + resultInfo.epsilon = epsilon_3 + stop = true + } + if (settings.iteration == MaxIter) { +// println(" !! Maximum Number of Iterations Reached Without Convergence !!") + resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence + resultInfo.epsilon = 0.0 + stop = true + } + } // --- End of Main Loop + return resultInfo +} + +public data class LMSettings ( + var iteration:Int, + var func_calls: Int, + var example_number:Int +) + +/* matrix -> column of all elemnets */ +public fun make_column(tensor: MutableStructure2D) : MutableStructure2D { + val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) + val buffer = DoubleArray(tensor.shape.component1() * tensor.shape.component2()) + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + buffer[i * tensor.shape.component2() + j] = tensor[i, j] + } + } + val column = BroadcastDoubleTensorAlgebra.fromArray(ShapeND(shape), buffer).as2D() + return column +} + +/* column length */ +public fun length(column: MutableStructure2D) : Int { + return column.shape.component1() +} + +public fun MutableStructure2D.abs() { + for (i in 0 until this.shape.component1()) { + for (j in 0 until this.shape.component2()) { + this[i, j] = kotlin.math.abs(this[i, j]) + } + } +} + +public fun abs(input: MutableStructure2D): MutableStructure2D { + val tensor = BroadcastDoubleTensorAlgebra.ones( + ShapeND( + intArrayOf( + input.shape.component1(), + input.shape.component2() + ) + ) + ).as2D() + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + tensor[i, j] = kotlin.math.abs(input[i, j]) + } + } + return tensor +} + +public fun diag(input: MutableStructure2D): MutableStructure2D { + val tensor = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(input.shape.component1(), 1))).as2D() + for (i in 0 until tensor.shape.component1()) { + tensor[i, 0] = input[i, i] + } + return tensor +} + +public fun make_matrx_with_diagonal(column: MutableStructure2D): MutableStructure2D { + val size = column.shape.component1() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(size, size))).as2D() + for (i in 0 until size) { + tensor[i, i] = column[i, 0] + } + return tensor +} + +public fun lm_eye(size: Int): MutableStructure2D { + val column = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(size, 1))).as2D() + return make_matrx_with_diagonal(column) +} + +public fun largest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val a_sizeX = a.shape.component1() + val a_sizeY = a.shape.component2() + val b_sizeX = b.shape.component1() + val b_sizeY = b.shape.component2() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { + tensor[i, j] = max(a[i, j], b[i, j]) + } + else if (i < a_sizeX && j < a_sizeY) { + tensor[i, j] = a[i, j] + } + else { + tensor[i, j] = b[i, j] + } + } + } + return tensor +} + +public fun smallest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val a_sizeX = a.shape.component1() + val a_sizeY = a.shape.component2() + val b_sizeX = b.shape.component1() + val b_sizeY = b.shape.component2() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() + for (i in 0 until tensor.shape.component1()) { + for (j in 0 until tensor.shape.component2()) { + if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { + tensor[i, j] = min(a[i, j], b[i, j]) + } + else if (i < a_sizeX && j < a_sizeY) { + tensor[i, j] = a[i, j] + } + else { + tensor[i, j] = b[i, j] + } + } + } + return tensor +} + +public fun get_zero_indices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { + var idx = emptyArray() + for (i in 0 until column.shape.component1()) { + if (kotlin.math.abs(column[i, 0]) > epsilon) { + idx += (i + 1.0) + } + } + if (idx.size > 0) { + return BroadcastDoubleTensorAlgebra.fromArray(ShapeND(intArrayOf(idx.size, 1)), idx.toDoubleArray()).as2D() + } + return null +} + +public fun feval(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings) + : MutableStructure2D +{ + return func(t, p, settings) +} + +public fun lm_matx(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, + t: MutableStructure2D, p_old: MutableStructure2D, y_old: MutableStructure2D, + dX2: Int, J_input: MutableStructure2D, p: MutableStructure2D, + y_dat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> +{ + // default: dp = 0.001 + + val Npnt = length(y_dat) // number of data points + val Npar = length(p) // number of parameters + + val y_hat = feval(func, t, p, settings) // evaluate model using parameters 'p' + settings.func_calls += 1 + + var J = J_input + + if (settings.iteration % (2 * Npar) == 0 || dX2 > 0) { + J = lm_FD_J(func, t, p, y_hat, dp, settings).as2D() // finite difference + } + else { + J = lm_Broyden_J(p_old, y_old, J, p, y_hat).as2D() // rank-1 update + } + + val delta_y = y_dat.minus(y_hat) + + val Chi_sq = delta_y.transposed().dot( delta_y.times(weight) ).as2D() + val JtWJ = J.transposed().dot ( J.times( weight.dot(BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(1, Npar)))) ) ).as2D() + val JtWdy = J.transposed().dot( weight.times(delta_y) ).as2D() + + return arrayOf(JtWJ,JtWdy,Chi_sq,y_hat,J) +} + +public fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructure2D, J_input: MutableStructure2D, + p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { + var J = J_input.copyToTensor() + + val h = p.minus(p_old) + val increase = y.minus(y_old).minus( J.dot(h) ).dot(h.transposed()).div( (h.transposed().dot(h)).as2D()[0, 0] ) + J = J.plus(increase) + + return J.as2D() +} + +public fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D, settings: LMSettings) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, + dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { + // default: dp = 0.001 * ones(1,n) + + val m = length(y) // number of data points + val n = length(p) // number of parameters + + val ps = p.copyToTensor().as2D() + val J = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, n))).as2D() // initialize Jacobian to Zero + val del = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(n, 1))).as2D() + + for (j in 0 until n) { + + del[j, 0] = dp[j, 0] * (1 + kotlin.math.abs(p[j, 0])) // parameter perturbation + p[j, 0] = ps[j, 0] + del[j, 0] // perturb parameter p(j) + + val epsilon = 0.0000001 + if (kotlin.math.abs(del[j, 0]) > epsilon) { + val y1 = feval(func, t, p, settings) + settings.func_calls += 1 + + if (dp[j, 0] < 0) { // backwards difference + for (i in 0 until J.shape.component1()) { + J[i, j] = (y1.as2D().minus(y).as2D())[i, 0] / del[j, 0] + } + } + else { + // Do tests for it + println("Potential mistake") + p[j, 0] = ps[j, 0] - del[j, 0] // central difference, additional func call + for (i in 0 until J.shape.component1()) { + J[i, j] = (y1.as2D().minus(feval(func, t, p, settings)).as2D())[i, 0] / (2 * del[j, 0]) + } + settings.func_calls += 1 + } + } + + p[j, 0] = ps[j, 0] // restore p(j) + } + + return J.as2D() +} diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index 6e5456c62..086c69e49 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -9,12 +9,6 @@ import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.core.* -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.minus -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.times -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -607,225 +601,4 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 u[j, i] = this[j, i] } } -} - -data class LMSettings ( - var iteration:Int, - var func_calls: Int, - var example_number:Int -) - -/* matrix -> column of all elemnets */ -fun make_column(tensor: MutableStructure2D) : MutableStructure2D { - val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) - var buffer = DoubleArray(tensor.shape.component1() * tensor.shape.component2()) - for (i in 0 until tensor.shape.component1()) { - for (j in 0 until tensor.shape.component2()) { - buffer[i * tensor.shape.component2() + j] = tensor[i, j] - } - } - var column = BroadcastDoubleTensorAlgebra.fromArray(ShapeND(shape), buffer).as2D() - return column -} - -/* column length */ -fun length(column: MutableStructure2D) : Int { - return column.shape.component1() -} - -fun MutableStructure2D.abs() { - for (i in 0 until this.shape.component1()) { - for (j in 0 until this.shape.component2()) { - this[i, j] = abs(this[i, j]) - } - } -} - -fun abs(input: MutableStructure2D): MutableStructure2D { - val tensor = BroadcastDoubleTensorAlgebra.ones( - ShapeND( - intArrayOf( - input.shape.component1(), - input.shape.component2() - ) - ) - ).as2D() - for (i in 0 until tensor.shape.component1()) { - for (j in 0 until tensor.shape.component2()) { - tensor[i, j] = abs(input[i, j]) - } - } - return tensor -} - -fun diag(input: MutableStructure2D): MutableStructure2D { - val tensor = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(input.shape.component1(), 1))).as2D() - for (i in 0 until tensor.shape.component1()) { - tensor[i, 0] = input[i, i] - } - return tensor -} - -fun make_matrx_with_diagonal(column: MutableStructure2D): MutableStructure2D { - val size = column.shape.component1() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(size, size))).as2D() - for (i in 0 until size) { - tensor[i, i] = column[i, 0] - } - return tensor -} - -fun lm_eye(size: Int): MutableStructure2D { - val column = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(size, 1))).as2D() - return make_matrx_with_diagonal(column) -} - -fun largest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { - val a_sizeX = a.shape.component1() - val a_sizeY = a.shape.component2() - val b_sizeX = b.shape.component1() - val b_sizeY = b.shape.component2() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() - for (i in 0 until tensor.shape.component1()) { - for (j in 0 until tensor.shape.component2()) { - if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { - tensor[i, j] = max(a[i, j], b[i, j]) - } - else if (i < a_sizeX && j < a_sizeY) { - tensor[i, j] = a[i, j] - } - else { - tensor[i, j] = b[i, j] - } - } - } - return tensor -} - -fun smallest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { - val a_sizeX = a.shape.component1() - val a_sizeY = a.shape.component2() - val b_sizeX = b.shape.component1() - val b_sizeY = b.shape.component2() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() - for (i in 0 until tensor.shape.component1()) { - for (j in 0 until tensor.shape.component2()) { - if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { - tensor[i, j] = min(a[i, j], b[i, j]) - } - else if (i < a_sizeX && j < a_sizeY) { - tensor[i, j] = a[i, j] - } - else { - tensor[i, j] = b[i, j] - } - } - } - return tensor -} - -fun get_zero_indices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { - var idx = emptyArray() - for (i in 0 until column.shape.component1()) { - if (abs(column[i, 0]) > epsilon) { - idx += (i + 1.0) - } - } - if (idx.size > 0) { - return BroadcastDoubleTensorAlgebra.fromArray(ShapeND(intArrayOf(idx.size, 1)), idx.toDoubleArray()).as2D() - } - return null -} - -fun feval(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings) - : MutableStructure2D -{ - return func(t, p, settings) -} - -fun lm_matx(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, - t: MutableStructure2D, p_old: MutableStructure2D, y_old: MutableStructure2D, - dX2: Int, J_input: MutableStructure2D, p: MutableStructure2D, - y_dat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> -{ - // default: dp = 0.001 - - val Npnt = length(y_dat) // number of data points - val Npar = length(p) // number of parameters - - val y_hat = feval(func, t, p, settings) // evaluate model using parameters 'p' - settings.func_calls += 1 - - var J = J_input - - if (settings.iteration % (2 * Npar) == 0 || dX2 > 0) { - J = lm_FD_J(func, t, p, y_hat, dp, settings).as2D() // finite difference - } - else { - J = lm_Broyden_J(p_old, y_old, J, p, y_hat).as2D() // rank-1 update - } - - val delta_y = y_dat.minus(y_hat) - - val Chi_sq = delta_y.transposed().dot( delta_y.times(weight) ).as2D() - val JtWJ = J.transposed().dot ( J.times( weight.dot(BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(1, Npar)))) ) ).as2D() - val JtWdy = J.transposed().dot( weight.times(delta_y) ).as2D() - - return arrayOf(JtWJ,JtWdy,Chi_sq,y_hat,J) -} - -fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructure2D, J_input: MutableStructure2D, - p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { - var J = J_input.copyToTensor() - - val h = p.minus(p_old) - val increase = y.minus(y_old).minus( J.dot(h) ).dot(h.transposed()).div( (h.transposed().dot(h)).as2D()[0, 0] ) - J = J.plus(increase) - - return J.as2D() -} - -fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D, settings: LMSettings) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, - dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { - // default: dp = 0.001 * ones(1,n) - - val m = length(y) // number of data points - val n = length(p) // number of parameters - - val ps = p.copyToTensor().as2D() - val J = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, n))).as2D() // initialize Jacobian to Zero - val del = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(n, 1))).as2D() - - for (j in 0 until n) { - - del[j, 0] = dp[j, 0] * (1 + abs(p[j, 0])) // parameter perturbation - p[j, 0] = ps[j, 0] + del[j, 0] // perturb parameter p(j) - - val epsilon = 0.0000001 - if (kotlin.math.abs(del[j, 0]) > epsilon) { - val y1 = feval(func, t, p, settings) - settings.func_calls += 1 - - if (dp[j, 0] < 0) { // backwards difference - for (i in 0 until J.shape.component1()) { - J[i, j] = (y1.as2D().minus(y).as2D())[i, 0] / del[j, 0] - } - } - else { - // Do tests for it - println("Potential mistake") - p[j, 0] = ps[j, 0] - del[j, 0] // central difference, additional func call - for (i in 0 until J.shape.component1()) { - J[i, j] = (y1.as2D().minus(feval(func, t, p, settings)).as2D())[i, 0] / (2 * del[j, 0]) - } - settings.func_calls += 1 - } - } - - p[j, 0] = ps[j, 0] // restore p(j) - } - - return J.as2D() -} +} \ No newline at end of file diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt index 5aea9b879..7222fc7a6 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensorAlgebra.kt @@ -8,11 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra -import space.kscience.kmath.tensors.core.internal.LMSettings import space.kscience.kmath.testutils.assertBufferEquals -import kotlin.math.roundToInt -import kotlin.math.roundToLong import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse @@ -209,100 +205,4 @@ internal class TestDoubleTensorAlgebra { assertTrue { ShapeND(5, 5) contentEquals res.shape } assertEquals(2.0, res[4, 4]) } - - @Test - fun testLM() = DoubleTensorAlgebra { - fun lm_func(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { - val m = t.shape.component1() - var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) - - if (settings.example_number == 1) { - y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( - DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) - ) - } - else if (settings.example_number == 2) { - val mt = t.max() - y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + - (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + - (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + - (t.times(1.0 / mt)).pow(4).times(p[3, 0]) - } - else if (settings.example_number == 3) { - y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) - .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) - } - - return y_hat.as2D() - } - - val lm_matx_y_dat = doubleArrayOf( - 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, - 14.7620, 15.1128, 16.0973, 15.1934, 15.8636, 15.4763, 15.6860, 15.1895, 15.3495, 16.6054, - 16.2247, 15.9854, 16.1421, 17.0960, 16.7769, 17.1997, 17.2767, 17.5882, 17.5378, 16.7894, - 17.7648, 18.2512, 18.1581, 16.7037, 17.8475, 17.9081, 18.3067, 17.9632, 18.2817, 19.1427, - 18.8130, 18.5658, 18.0056, 18.4607, 18.5918, 18.2544, 18.3731, 18.7511, 19.3181, 17.3066, - 17.9632, 19.0513, 18.7528, 18.2928, 18.5967, 17.8567, 17.7859, 18.4016, 18.9423, 18.4959, - 17.8000, 18.4251, 17.7829, 17.4645, 17.5221, 17.3517, 17.4637, 17.7563, 16.8471, 17.4558, - 17.7447, 17.1487, 17.3183, 16.8312, 17.7551, 17.0942, 15.6093, 16.4163, 15.3755, 16.6725, - 16.2332, 16.2316, 16.2236, 16.5361, 15.3721, 15.3347, 15.5815, 15.6319, 14.4538, 14.6044, - 14.7665, 13.3718, 15.0587, 13.8320, 14.7873, 13.6824, 14.2579, 14.2154, 13.5818, 13.8157 - ) - - var example_number = 1 - val p_init = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(5.0, 2.0, 0.2, 10.0) - ).as2D() - - var t = ones(ShapeND(intArrayOf(100, 1))).as2D() - for (i in 0 until 100) { - t[i, 0] = t[i, 0] * (i + 1) - } - - val y_dat = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(100, 1)), lm_matx_y_dat - ).as2D() - - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 4.0 } - ).as2D() - - val dp = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } - ).as2D() - - val p_min = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(-50.0, -20.0, -2.0, -100.0) - ).as2D() - - val p_max = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(50.0, 20.0, 2.0, 100.0) - ).as2D() - - val consts = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) - ).as2D() - - val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) - - val result = lm(::lm_func, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts, 10, example_number) - assertEquals(13, result.iterations) - assertEquals(31, result.func_calls) - assertEquals(1, result.example_number) - assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) - assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) - assertEquals(result.typeOfConvergence, LinearOpsTensorAlgebra.TypeOfConvergence.inParameters) - val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) - ).as2D() - result.result_parameters = result.result_parameters.map { x -> (x * 1e12).toLong() / 1e12}.as2D() - val receivedParameters = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(result.result_parameters[0, 0], result.result_parameters[1, 0], - result.result_parameters[2, 0], result.result_parameters[3, 0]) - ).as2D() - assertEquals(expectedParameters[0, 0], receivedParameters[0, 0]) - assertEquals(expectedParameters[1, 0], receivedParameters[1, 0]) - assertEquals(expectedParameters[2, 0], receivedParameters[2, 0]) - assertEquals(expectedParameters[3, 0], receivedParameters[3, 0]) - } } diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index f554a742c..db8bc9d20 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -15,7 +15,6 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times -import space.kscience.kmath.tensors.core.internal.LMSettings import kotlin.math.roundToLong import kotlin.test.Test import kotlin.test.assertEquals From 963e14b00a273c55049f66dcb96a93e1c158d7c4 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Tue, 6 Jun 2023 20:07:42 +0300 Subject: [PATCH 15/34] move enums --- kmath-symja/build.gradle.kts | 2 +- .../tensors/api/LinearOpsTensorAlgebra.kt | 18 ----------- .../core/LevenbergMarquardtAlgorithm.kt | 32 +++++++++++++++---- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/kmath-symja/build.gradle.kts b/kmath-symja/build.gradle.kts index 8741de2ae..a996f3bec 100644 --- a/kmath-symja/build.gradle.kts +++ b/kmath-symja/build.gradle.kts @@ -10,7 +10,7 @@ plugins { description = "Symja integration module" dependencies { - api("org.matheclipse:matheclipse-core:2.0.0-SNAPSHOT") { + api("org.matheclipse:matheclipse-core:2.0.0") { // Incorrect transitive dependencies exclude("org.apfloat", "apfloat") exclude("org.hipparchus", "hipparchus-clustering") diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt index 6b3859316..f2c7f1821 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/api/LinearOpsTensorAlgebra.kt @@ -111,22 +111,4 @@ public interface LinearOpsTensorAlgebra> : TensorPartialDivision * @return the square matrix x which is the solution of the equation. */ public fun solve(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D - - public enum class TypeOfConvergence{ - inRHS_JtWdy, - inParameters, - inReducedChi_square, - noConvergence - } - - public data class LMResultInfo ( - var iterations:Int, - var func_calls: Int, - var example_number: Int, - var result_chi_sq: Double, - var result_lambda: Double, - var result_parameters: MutableStructure2D, - var typeOfConvergence: TypeOfConvergence, - var epsilon: Double - ) } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index f4b50626a..4323a86a3 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -19,14 +19,32 @@ import kotlin.math.min import kotlin.math.pow import kotlin.reflect.KFunction3 +public enum class TypeOfConvergence{ + inRHS_JtWdy, + inParameters, + inReducedChi_square, + noConvergence +} + +public data class LMResultInfo ( + var iterations:Int, + var func_calls: Int, + var example_number: Int, + var result_chi_sq: Double, + var result_lambda: Double, + var result_parameters: MutableStructure2D, + var typeOfConvergence: TypeOfConvergence, + var epsilon: Double +) + public fun DoubleTensorAlgebra.lm( func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, - c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LinearOpsTensorAlgebra.LMResultInfo { + c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LMResultInfo { - val resultInfo = LinearOpsTensorAlgebra.LMResultInfo(0, 0, example_number, 0.0, - 0.0, p_input, LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence, 0.0) + val resultInfo = LMResultInfo(0, 0, example_number, 0.0, + 0.0, p_input, TypeOfConvergence.noConvergence, 0.0) val eps:Double = 2.2204e-16 @@ -303,27 +321,27 @@ public fun DoubleTensorAlgebra.lm( if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { // println(" **** Convergence in r.h.s. (\"JtWdy\") ****") // println(" **** epsilon_1 = $epsilon_1") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inRHS_JtWdy + resultInfo.typeOfConvergence = TypeOfConvergence.inRHS_JtWdy resultInfo.epsilon = epsilon_1 stop = true } if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { // println(" **** Convergence in Parameters ****") // println(" **** epsilon_2 = $epsilon_2") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inParameters + resultInfo.typeOfConvergence = TypeOfConvergence.inParameters resultInfo.epsilon = epsilon_2 stop = true } if (X2 / DoF < epsilon_3 && settings.iteration > 2) { // println(" **** Convergence in reduced Chi-square **** ") // println(" **** epsilon_3 = $epsilon_3") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.inReducedChi_square + resultInfo.typeOfConvergence = TypeOfConvergence.inReducedChi_square resultInfo.epsilon = epsilon_3 stop = true } if (settings.iteration == MaxIter) { // println(" !! Maximum Number of Iterations Reached Without Convergence !!") - resultInfo.typeOfConvergence = LinearOpsTensorAlgebra.TypeOfConvergence.noConvergence + resultInfo.typeOfConvergence = TypeOfConvergence.noConvergence resultInfo.epsilon = 0.0 stop = true } From 29d392a8a05aa8bb27b342a53b5920607644217e Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Tue, 6 Jun 2023 20:31:15 +0300 Subject: [PATCH 16/34] fix problem with imports --- .../LevenbergMarquardt/StaticLm/staticDifficultTest.kt | 3 ++- .../tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt | 4 ++-- .../tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt | 3 ++- .../kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt | 3 ++- .../kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt | 3 +-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt index 0a502afa8..e996b7f7e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt @@ -12,7 +12,8 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.internal.LMSettings +import space.kscience.kmath.tensors.core.LMSettings +import space.kscience.kmath.tensors.core.lm import kotlin.math.roundToInt fun main() { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt index bae5a674f..d9e5f350e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt @@ -11,9 +11,9 @@ import space.kscience.kmath.nd.component1 import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.internal.LMSettings +import space.kscience.kmath.tensors.core.LMSettings +import space.kscience.kmath.tensors.core.lm import kotlin.math.roundToInt fun main() { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt index 02917caf2..b5553a930 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt @@ -13,7 +13,8 @@ import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times -import space.kscience.kmath.tensors.core.internal.LMSettings +import space.kscience.kmath.tensors.core.LMSettings +import space.kscience.kmath.tensors.core.lm import kotlin.math.roundToInt fun main() { val NData = 100 diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index 5a1037618..46bda40c6 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -11,7 +11,8 @@ import space.kscience.kmath.nd.* import space.kscience.kmath.tensors.LevenbergMarquardt.StartDataLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.zeros import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.internal.LMSettings +import space.kscience.kmath.tensors.core.LMSettings +import space.kscience.kmath.tensors.core.lm import kotlin.random.Random import kotlin.reflect.KFunction3 diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt index 5b194ab6b..a22de71d8 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt @@ -16,9 +16,8 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times +import space.kscience.kmath.tensors.core.LMSettings import space.kscience.kmath.tensors.core.asDoubleTensor -import space.kscience.kmath.tensors.core.internal.LMSettings -import kotlin.math.roundToInt public data class StartDataLm ( var lm_matx_y_dat: MutableStructure2D, From 1ed40cd8ce5bd0f6d70da097b328267c2a49203c Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Tue, 6 Jun 2023 20:43:59 +0300 Subject: [PATCH 17/34] fix problem with imports --- .../space/kscience/kmath/tensors/core/TestLmAlgorithm.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index db8bc9d20..dae3006c9 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -10,7 +10,6 @@ import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.component1 import space.kscience.kmath.operations.invoke -import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow @@ -135,7 +134,7 @@ class TestLmAlgorithm { assertEquals(1, result.example_number) assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) - assertEquals(result.typeOfConvergence, LinearOpsTensorAlgebra.TypeOfConvergence.inParameters) + assertEquals(result.typeOfConvergence, TypeOfConvergence.inParameters) val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) ).as2D() From 0c7f5697da6e8a52176ca5f90f780b37ed918f07 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 00:50:27 +0300 Subject: [PATCH 18/34] add documentation for enum TypeOfConvergence --- .../core/LevenbergMarquardtAlgorithm.kt | 66 +++++++++---------- .../kmath/tensors/core/TestLmAlgorithm.kt | 2 +- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index 4323a86a3..deb7ee300 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -7,7 +7,6 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.linear.transpose import space.kscience.kmath.nd.* -import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.minus @@ -19,11 +18,26 @@ import kotlin.math.min import kotlin.math.pow import kotlin.reflect.KFunction3 +/** + * Type of convergence achieved as a result of executing the Levenberg-Marquardt algorithm + * + * InGradient: gradient convergence achieved + * (max(J^T W dy) < epsilon1 = opts[2], + * where J - Jacobi matrix (dy^/dp) for the current approximation y^, + * W - weight matrix from input, dy = (y - y^(p))) + * InParameters: convergence in parameters achieved + * (max(h_i / p_i) < epsilon2 = opts[3], + * where h_i - offset for parameter p_i on the current iteration) + * InReducedChiSquare: chi-squared convergence achieved + * (chi squared value divided by (m - n + 1) < epsilon2 = opts[4], + * where n - number of parameters, m - amount of points + * NoConvergence: the maximum number of iterations has been reached without reaching convergence + */ public enum class TypeOfConvergence{ - inRHS_JtWdy, - inParameters, - inReducedChi_square, - noConvergence + InGradient, + InParameters, + InReducedChiSquare, + NoConvergence } public data class LMResultInfo ( @@ -37,6 +51,12 @@ public data class LMResultInfo ( var epsilon: Double ) +public data class LMSettings ( + var iteration:Int, + var func_calls: Int, + var example_number:Int +) + public fun DoubleTensorAlgebra.lm( func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, @@ -44,7 +64,7 @@ public fun DoubleTensorAlgebra.lm( c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LMResultInfo { val resultInfo = LMResultInfo(0, 0, example_number, 0.0, - 0.0, p_input, TypeOfConvergence.noConvergence, 0.0) + 0.0, p_input, TypeOfConvergence.NoConvergence, 0.0) val eps:Double = 2.2204e-16 @@ -299,15 +319,6 @@ public fun DoubleTensorAlgebra.lm( if (prnt > 1) { val chi_sq = X2 / DoF -// println("Iteration $settings | chi_sq=$chi_sq | lambda=$lambda") -// print("param: ") -// for (pn in 0 until Npar) { -// print(p[pn, 0].toString() + " ") -// } -// print("\ndp/p: ") -// for (pn in 0 until Npar) { -// print((h.as2D()[pn, 0] / p[pn, 0]).toString() + " ") -// } resultInfo.iterations = settings.iteration resultInfo.func_calls = settings.func_calls resultInfo.result_chi_sq = chi_sq @@ -319,42 +330,29 @@ public fun DoubleTensorAlgebra.lm( // cvg_hst(iteration,:) = [ func_calls p' X2/DoF lambda ]; if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { -// println(" **** Convergence in r.h.s. (\"JtWdy\") ****") -// println(" **** epsilon_1 = $epsilon_1") - resultInfo.typeOfConvergence = TypeOfConvergence.inRHS_JtWdy + resultInfo.typeOfConvergence = TypeOfConvergence.InGradient resultInfo.epsilon = epsilon_1 stop = true } if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { -// println(" **** Convergence in Parameters ****") -// println(" **** epsilon_2 = $epsilon_2") - resultInfo.typeOfConvergence = TypeOfConvergence.inParameters + resultInfo.typeOfConvergence = TypeOfConvergence.InParameters resultInfo.epsilon = epsilon_2 stop = true } if (X2 / DoF < epsilon_3 && settings.iteration > 2) { -// println(" **** Convergence in reduced Chi-square **** ") -// println(" **** epsilon_3 = $epsilon_3") - resultInfo.typeOfConvergence = TypeOfConvergence.inReducedChi_square + resultInfo.typeOfConvergence = TypeOfConvergence.InReducedChiSquare resultInfo.epsilon = epsilon_3 stop = true } if (settings.iteration == MaxIter) { -// println(" !! Maximum Number of Iterations Reached Without Convergence !!") - resultInfo.typeOfConvergence = TypeOfConvergence.noConvergence + resultInfo.typeOfConvergence = TypeOfConvergence.NoConvergence resultInfo.epsilon = 0.0 stop = true } - } // --- End of Main Loop + } return resultInfo } -public data class LMSettings ( - var iteration:Int, - var func_calls: Int, - var example_number:Int -) - /* matrix -> column of all elemnets */ public fun make_column(tensor: MutableStructure2D) : MutableStructure2D { val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) @@ -564,7 +562,7 @@ public fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D } } - p[j, 0] = ps[j, 0] // restore p(j) + p[j, 0] = ps[j, 0] } return J.as2D() diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index dae3006c9..1112043fc 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -134,7 +134,7 @@ class TestLmAlgorithm { assertEquals(1, result.example_number) assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) - assertEquals(result.typeOfConvergence, TypeOfConvergence.inParameters) + assertEquals(result.typeOfConvergence, TypeOfConvergence.InParameters) val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) ).as2D() From cac5b513f30515e38b4f7706b7161905733781e3 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 01:55:38 +0300 Subject: [PATCH 19/34] made class for settings private and removed settings as input from a custom function --- .../StaticLm/staticDifficultTest.kt | 13 +-- .../StaticLm/staticEasyTest.kt | 9 +- .../StaticLm/staticMiddleTest.kt | 19 ++-- .../StreamingLm/streamLm.kt | 7 +- .../LevenbergMarquardt/functionsToOptimize.kt | 60 +++++----- .../core/LevenbergMarquardtAlgorithm.kt | 107 +++++++++--------- .../kmath/tensors/core/TestLmAlgorithm.kt | 38 +++---- 7 files changed, 125 insertions(+), 128 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt index e996b7f7e..24cfa955a 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt @@ -12,7 +12,6 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.LMSettings import space.kscience.kmath.tensors.core.lm import kotlin.math.roundToInt @@ -29,9 +28,9 @@ fun main() { p_example[i, 0] = p_example[i, 0] + i - 25 } - val settings = LMSettings(0, 0, 1) + val exampleNumber = 1 - var y_hat = funcDifficultForLm(t_example, p_example, settings) + var y_hat = funcDifficultForLm(t_example, p_example, exampleNumber) var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { @@ -72,14 +71,14 @@ fun main() { ) println("Parameters:") - for (i in 0 until result.result_parameters.shape.component1()) { - val x = (result.result_parameters[i, 0] * 10000).roundToInt() / 10000.0 + for (i in 0 until result.resultParameters.shape.component1()) { + val x = (result.resultParameters[i, 0] * 10000).roundToInt() / 10000.0 print("$x ") } println() println("Y true and y received:") - var y_hat_after = funcDifficultForLm(t_example, result.result_parameters, settings) + var y_hat_after = funcDifficultForLm(t_example, result.resultParameters, exampleNumber) for (i in 0 until y_hat.shape.component1()) { val x = (y_hat[i, 0] * 10000).roundToInt() / 10000.0 val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0 @@ -87,7 +86,7 @@ fun main() { } println("Сhi_sq:") - println(result.result_chi_sq) + println(result.resultChiSq) println("Number of iterations:") println(result.iterations) } \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt index d9e5f350e..c7b2b0def 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt @@ -12,7 +12,6 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.LMSettings import space.kscience.kmath.tensors.core.lm import kotlin.math.roundToInt @@ -35,14 +34,14 @@ fun main() { ) println("Parameters:") - for (i in 0 until result.result_parameters.shape.component1()) { - val x = (result.result_parameters[i, 0] * 10000).roundToInt() / 10000.0 + for (i in 0 until result.resultParameters.shape.component1()) { + val x = (result.resultParameters[i, 0] * 10000).roundToInt() / 10000.0 print("$x ") } println() println("Y true and y received:") - var y_hat_after = funcDifficultForLm(startedData.t, result.result_parameters, LMSettings(0, 0, startedData.example_number)) + var y_hat_after = funcDifficultForLm(startedData.t, result.resultParameters, startedData.example_number) for (i in 0 until startedData.y_dat.shape.component1()) { val x = (startedData.y_dat[i, 0] * 10000).roundToInt() / 10000.0 val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0 @@ -50,7 +49,7 @@ fun main() { } println("Сhi_sq:") - println(result.result_chi_sq) + println(result.resultChiSq) println("Number of iterations:") println(result.iterations) } \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt index b5553a930..471143102 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt @@ -12,8 +12,6 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcMiddleForLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times -import space.kscience.kmath.tensors.core.LMSettings import space.kscience.kmath.tensors.core.lm import kotlin.math.roundToInt fun main() { @@ -29,16 +27,15 @@ fun main() { p_example[i, 0] = p_example[i, 0] + i - 25 } - val settings = LMSettings(0, 0, 1) + val exampleNumber = 1 - var y_hat = funcMiddleForLm(t_example, p_example, settings) + var y_hat = funcMiddleForLm(t_example, p_example, exampleNumber) var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { p_init[i, 0] = (p_example[i, 0] + 0.9) } -// val p_init = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) -// val p_init = p_example + var t = t_example val y_dat = y_hat val weight = BroadcastDoubleTensorAlgebra.fromArray( @@ -54,7 +51,7 @@ fun main() { val consts = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) ).as2D() - val opts = doubleArrayOf(3.0, 10000.0, 1e-3, 1e-3, 1e-3, 1e-3, 1e-15, 11.0, 9.0, 1.0) + val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) val result = DoubleTensorAlgebra.lm( ::funcMiddleForLm, @@ -72,14 +69,14 @@ fun main() { ) println("Parameters:") - for (i in 0 until result.result_parameters.shape.component1()) { - val x = (result.result_parameters[i, 0] * 10000).roundToInt() / 10000.0 + for (i in 0 until result.resultParameters.shape.component1()) { + val x = (result.resultParameters[i, 0] * 10000).roundToInt() / 10000.0 print("$x ") } println() - var y_hat_after = funcMiddleForLm(t_example, result.result_parameters, settings) + var y_hat_after = funcMiddleForLm(t_example, result.resultParameters, exampleNumber) for (i in 0 until y_hat.shape.component1()) { val x = (y_hat[i, 0] * 10000).roundToInt() / 10000.0 val y = (y_hat_after[i, 0] * 10000).roundToInt() / 10000.0 @@ -87,7 +84,7 @@ fun main() { } println("Сhi_sq:") - println(result.result_chi_sq) + println(result.resultChiSq) println("Number of iterations:") println(result.iterations) } \ No newline at end of file diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index 46bda40c6..f031d82bf 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -11,12 +11,11 @@ import space.kscience.kmath.nd.* import space.kscience.kmath.tensors.LevenbergMarquardt.StartDataLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.zeros import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.LMSettings import space.kscience.kmath.tensors.core.lm import kotlin.random.Random import kotlin.reflect.KFunction3 -fun streamLm(lm_func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, +fun streamLm(lm_func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, startData: StartDataLm, launchFrequencyInMs: Long, numberOfLaunches: Int): Flow> = flow{ var example_number = startData.example_number @@ -48,9 +47,9 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< 10, example_number ) - emit(result.result_parameters) + emit(result.resultParameters) delay(launchFrequencyInMs) - p_init = result.result_parameters + p_init = result.resultParameters y_dat = generateNewYDat(y_dat, 0.1) if (!isEndless) steps -= 1 } diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt index a22de71d8..537b86da3 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt @@ -16,7 +16,6 @@ import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times -import space.kscience.kmath.tensors.core.LMSettings import space.kscience.kmath.tensors.core.asDoubleTensor public data class StartDataLm ( @@ -33,23 +32,31 @@ public data class StartDataLm ( var opts: DoubleArray ) -fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { +fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() - var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) - val mt = t.max() - for(i in 0 until p.shape.component1()){ - y_hat = y_hat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) + if (exampleNumber == 1) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( + DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) + ) } - - for(i in 0 until 4){ - y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, settings).asDoubleTensor() + else if (exampleNumber == 2) { + val mt = t.max() + y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + + (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + + (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + + (t.times(1.0 / mt)).pow(4).times(p[3, 0]) + } + else if (exampleNumber == 3) { + y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) + .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) } return y_hat.as2D() } -fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { +fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) @@ -59,36 +66,29 @@ fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D } for(i in 0 until 5){ - y_hat = funcEasyForLm(y_hat.as2D(), p, settings).asDoubleTensor() + y_hat = funcEasyForLm(y_hat.as2D(), p, exampleNumber).asDoubleTensor() } return y_hat.as2D() } -fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { +fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) - if (settings.example_number == 1) { - y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( - DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) - ) + val mt = t.max() + for(i in 0 until p.shape.component1()){ + y_hat = y_hat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) } - else if (settings.example_number == 2) { - val mt = t.max() - y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + - (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + - (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + - (t.times(1.0 / mt)).pow(4).times(p[3, 0]) - } - else if (settings.example_number == 3) { - y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) - .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) + + for(i in 0 until 4){ + y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, exampleNumber).asDoubleTensor() } return y_hat.as2D() } + fun getStartDataForFuncDifficult(): StartDataLm { val NData = 200 var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() @@ -102,9 +102,9 @@ fun getStartDataForFuncDifficult(): StartDataLm { p_example[i, 0] = p_example[i, 0] + i - 25 } - val settings = LMSettings(0, 0, 1) + val exampleNumber = 1 - var y_hat = funcDifficultForLm(t_example, p_example, settings) + var y_hat = funcDifficultForLm(t_example, p_example, exampleNumber) var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { @@ -144,9 +144,9 @@ fun getStartDataForFuncMiddle(): StartDataLm { p_example[i, 0] = p_example[i, 0] + i - 25 } - val settings = LMSettings(0, 0, 1) + val exampleNumber = 1 - var y_hat = funcMiddleForLm(t_example, p_example, settings) + var y_hat = funcMiddleForLm(t_example, p_example, exampleNumber) var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index deb7ee300..7fbf6ecd8 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -40,36 +40,39 @@ public enum class TypeOfConvergence{ NoConvergence } +/** + * Class for the data obtained as a result of the execution of the Levenberg-Marquardt algorithm + * + * iterations: number of completed iterations + * funcCalls: the number of evaluations of the input function during execution + * resultChiSq: chi squared value on final parameters + * resultLambda: final lambda parameter used to calculate the offset + * resultParameters: final parameters + * typeOfConvergence: type of convergence + */ public data class LMResultInfo ( var iterations:Int, - var func_calls: Int, - var example_number: Int, - var result_chi_sq: Double, - var result_lambda: Double, - var result_parameters: MutableStructure2D, + var funcCalls: Int, + var resultChiSq: Double, + var resultLambda: Double, + var resultParameters: MutableStructure2D, var typeOfConvergence: TypeOfConvergence, - var epsilon: Double -) - -public data class LMSettings ( - var iteration:Int, - var func_calls: Int, - var example_number:Int ) public fun DoubleTensorAlgebra.lm( - func: KFunction3, MutableStructure2D, LMSettings, MutableStructure2D>, + func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LMResultInfo { - val resultInfo = LMResultInfo(0, 0, example_number, 0.0, - 0.0, p_input, TypeOfConvergence.NoConvergence, 0.0) + + val resultInfo = LMResultInfo(0, 0, 0.0, + 0.0, p_input, TypeOfConvergence.NoConvergence) val eps:Double = 2.2204e-16 val settings = LMSettings(0, 0, example_number) - settings.func_calls = 0 // running count of function evaluations + settings.funcCalls = 0 // running count of function evaluations var p = p_input val y_dat = y_dat_input @@ -160,7 +163,7 @@ public fun DoubleTensorAlgebra.lm( val idx = get_zero_indices(dp) // indices of the parameters to be fit val Nfit = idx?.shape?.component1() // number of parameters to fit var stop = false // termination flag - val y_init = feval(func, t, p, settings) // residual error using p_try + val y_init = feval(func, t, p, example_number) // residual error using p_try if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() @@ -219,7 +222,7 @@ public fun DoubleTensorAlgebra.lm( var p_try = (p + h).as2D() // update the [idx] elements p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try.as2D()), p_max) // apply constraints - var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try + var delta_y = y_dat.minus(feval(func, t, p_try, example_number)) // residual error using p_try for (i in 0 until delta_y.shape.component1()) { // floating point error; break for (j in 0 until delta_y.shape.component2()) { @@ -230,7 +233,7 @@ public fun DoubleTensorAlgebra.lm( } } - settings.func_calls += 1 + settings.funcCalls += 1 val tmp = delta_y.times(weight) var X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria @@ -244,8 +247,8 @@ public fun DoubleTensorAlgebra.lm( p_try = p.plus(h).as2D() // update only [idx] elements p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try), p_max) // apply constraints - var delta_y = y_dat.minus(feval(func, t, p_try, settings)) // residual error using p_try - settings.func_calls += 1 + var delta_y = y_dat.minus(feval(func, t, p_try, example_number)) // residual error using p_try + settings.funcCalls += 1 val tmp = delta_y.times(weight) X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria @@ -320,10 +323,10 @@ public fun DoubleTensorAlgebra.lm( if (prnt > 1) { val chi_sq = X2 / DoF resultInfo.iterations = settings.iteration - resultInfo.func_calls = settings.func_calls - resultInfo.result_chi_sq = chi_sq - resultInfo.result_lambda = lambda - resultInfo.result_parameters = p + resultInfo.funcCalls = settings.funcCalls + resultInfo.resultChiSq = chi_sq + resultInfo.resultLambda = lambda + resultInfo.resultParameters = p } // update convergence history ... save _reduced_ Chi-square @@ -331,30 +334,32 @@ public fun DoubleTensorAlgebra.lm( if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InGradient - resultInfo.epsilon = epsilon_1 stop = true } if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InParameters - resultInfo.epsilon = epsilon_2 stop = true } if (X2 / DoF < epsilon_3 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InReducedChiSquare - resultInfo.epsilon = epsilon_3 stop = true } if (settings.iteration == MaxIter) { resultInfo.typeOfConvergence = TypeOfConvergence.NoConvergence - resultInfo.epsilon = 0.0 stop = true } } return resultInfo } +private data class LMSettings ( + var iteration:Int, + var funcCalls: Int, + var exampleNumber:Int +) + /* matrix -> column of all elemnets */ -public fun make_column(tensor: MutableStructure2D) : MutableStructure2D { +private fun make_column(tensor: MutableStructure2D) : MutableStructure2D { val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) val buffer = DoubleArray(tensor.shape.component1() * tensor.shape.component2()) for (i in 0 until tensor.shape.component1()) { @@ -367,11 +372,11 @@ public fun make_column(tensor: MutableStructure2D) : MutableStructure2D< } /* column length */ -public fun length(column: MutableStructure2D) : Int { +private fun length(column: MutableStructure2D) : Int { return column.shape.component1() } -public fun MutableStructure2D.abs() { +private fun MutableStructure2D.abs() { for (i in 0 until this.shape.component1()) { for (j in 0 until this.shape.component2()) { this[i, j] = kotlin.math.abs(this[i, j]) @@ -379,7 +384,7 @@ public fun MutableStructure2D.abs() { } } -public fun abs(input: MutableStructure2D): MutableStructure2D { +private fun abs(input: MutableStructure2D): MutableStructure2D { val tensor = BroadcastDoubleTensorAlgebra.ones( ShapeND( intArrayOf( @@ -396,7 +401,7 @@ public fun abs(input: MutableStructure2D): MutableStructure2D { return tensor } -public fun diag(input: MutableStructure2D): MutableStructure2D { +private fun diag(input: MutableStructure2D): MutableStructure2D { val tensor = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(input.shape.component1(), 1))).as2D() for (i in 0 until tensor.shape.component1()) { tensor[i, 0] = input[i, i] @@ -404,7 +409,7 @@ public fun diag(input: MutableStructure2D): MutableStructure2D { return tensor } -public fun make_matrx_with_diagonal(column: MutableStructure2D): MutableStructure2D { +private fun make_matrx_with_diagonal(column: MutableStructure2D): MutableStructure2D { val size = column.shape.component1() val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(size, size))).as2D() for (i in 0 until size) { @@ -413,12 +418,12 @@ public fun make_matrx_with_diagonal(column: MutableStructure2D): Mutable return tensor } -public fun lm_eye(size: Int): MutableStructure2D { +private fun lm_eye(size: Int): MutableStructure2D { val column = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(size, 1))).as2D() return make_matrx_with_diagonal(column) } -public fun largest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { +private fun largest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { val a_sizeX = a.shape.component1() val a_sizeY = a.shape.component2() val b_sizeX = b.shape.component1() @@ -440,7 +445,7 @@ public fun largest_element_comparison(a: MutableStructure2D, b: MutableS return tensor } -public fun smallest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { +private fun smallest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { val a_sizeX = a.shape.component1() val a_sizeY = a.shape.component2() val b_sizeX = b.shape.component1() @@ -462,7 +467,7 @@ public fun smallest_element_comparison(a: MutableStructure2D, b: Mutable return tensor } -public fun get_zero_indices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { +private fun get_zero_indices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { var idx = emptyArray() for (i in 0 until column.shape.component1()) { if (kotlin.math.abs(column[i, 0]) > epsilon) { @@ -475,14 +480,14 @@ public fun get_zero_indices(column: MutableStructure2D, epsilon: Double return null } -public fun feval(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings) +private fun feval(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int) : MutableStructure2D { - return func(t, p, settings) + return func(t, p, exampleNumber) } -public fun lm_matx(func: (MutableStructure2D, MutableStructure2D, LMSettings) -> MutableStructure2D, +private fun lm_matx(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, t: MutableStructure2D, p_old: MutableStructure2D, y_old: MutableStructure2D, dX2: Int, J_input: MutableStructure2D, p: MutableStructure2D, y_dat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> @@ -492,8 +497,8 @@ public fun lm_matx(func: (MutableStructure2D, MutableStructure2D val Npnt = length(y_dat) // number of data points val Npar = length(p) // number of parameters - val y_hat = feval(func, t, p, settings) // evaluate model using parameters 'p' - settings.func_calls += 1 + val y_hat = feval(func, t, p, settings.exampleNumber) // evaluate model using parameters 'p' + settings.funcCalls += 1 var J = J_input @@ -513,7 +518,7 @@ public fun lm_matx(func: (MutableStructure2D, MutableStructure2D return arrayOf(JtWJ,JtWdy,Chi_sq,y_hat,J) } -public fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructure2D, J_input: MutableStructure2D, +private fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructure2D, J_input: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { var J = J_input.copyToTensor() @@ -524,7 +529,7 @@ public fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructu return J.as2D() } -public fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D, settings: LMSettings) -> MutableStructure2D, +private fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { // default: dp = 0.001 * ones(1,n) @@ -543,8 +548,8 @@ public fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D val epsilon = 0.0000001 if (kotlin.math.abs(del[j, 0]) > epsilon) { - val y1 = feval(func, t, p, settings) - settings.func_calls += 1 + val y1 = feval(func, t, p, settings.exampleNumber) + settings.funcCalls += 1 if (dp[j, 0] < 0) { // backwards difference for (i in 0 until J.shape.component1()) { @@ -556,9 +561,9 @@ public fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D println("Potential mistake") p[j, 0] = ps[j, 0] - del[j, 0] // central difference, additional func call for (i in 0 until J.shape.component1()) { - J[i, j] = (y1.as2D().minus(feval(func, t, p, settings)).as2D())[i, 0] / (2 * del[j, 0]) + J[i, j] = (y1.as2D().minus(feval(func, t, p, settings.exampleNumber)).as2D())[i, 0] / (2 * del[j, 0]) } - settings.func_calls += 1 + settings.funcCalls += 1 } } diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 1112043fc..c3e5fa16f 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -20,23 +20,23 @@ import kotlin.test.assertEquals class TestLmAlgorithm { companion object { - fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) - if (settings.example_number == 1) { + if (exampleNumber == 1) { y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) ) } - else if (settings.example_number == 2) { + else if (exampleNumber == 2) { val mt = t.max() y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + (t.times(1.0 / mt)).pow(4).times(p[3, 0]) } - else if (settings.example_number == 3) { + else if (exampleNumber == 3) { y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) } @@ -44,7 +44,7 @@ class TestLmAlgorithm { return y_hat.as2D() } - fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) @@ -54,13 +54,13 @@ class TestLmAlgorithm { } for(i in 0 until 5){ - y_hat = funcEasyForLm(y_hat.as2D(), p, settings).asDoubleTensor() + y_hat = funcEasyForLm(y_hat.as2D(), p, exampleNumber).asDoubleTensor() } return y_hat.as2D() } - fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, settings: LMSettings): MutableStructure2D { + fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) @@ -70,12 +70,11 @@ class TestLmAlgorithm { } for(i in 0 until 4){ - y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, settings).asDoubleTensor() + y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, exampleNumber).asDoubleTensor() } return y_hat.as2D() } - } @Test fun testLMEasy() = DoubleTensorAlgebra { @@ -130,18 +129,17 @@ class TestLmAlgorithm { val result = lm(::funcEasyForLm, p_init, t, y_dat, weight, dp, p_min, p_max, consts, opts, 10, example_number) assertEquals(13, result.iterations) - assertEquals(31, result.func_calls) - assertEquals(1, result.example_number) - assertEquals(0.9131368192633, (result.result_chi_sq * 1e13).roundToLong() / 1e13) - assertEquals(3.7790980 * 1e-7, (result.result_lambda * 1e13).roundToLong() / 1e13) + assertEquals(31, result.funcCalls) + assertEquals(0.9131368192633, (result.resultChiSq * 1e13).roundToLong() / 1e13) + assertEquals(3.7790980 * 1e-7, (result.resultLambda * 1e13).roundToLong() / 1e13) assertEquals(result.typeOfConvergence, TypeOfConvergence.InParameters) val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) ).as2D() - result.result_parameters = result.result_parameters.map { x -> (x * 1e12).toLong() / 1e12}.as2D() + result.resultParameters = result.resultParameters.map { x -> (x * 1e12).toLong() / 1e12}.as2D() val receivedParameters = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(result.result_parameters[0, 0], result.result_parameters[1, 0], - result.result_parameters[2, 0], result.result_parameters[3, 0]) + ShapeND(intArrayOf(4, 1)), doubleArrayOf(result.resultParameters[0, 0], result.resultParameters[1, 0], + result.resultParameters[2, 0], result.resultParameters[3, 0]) ).as2D() assertEquals(expectedParameters[0, 0], receivedParameters[0, 0]) assertEquals(expectedParameters[1, 0], receivedParameters[1, 0]) @@ -163,9 +161,9 @@ class TestLmAlgorithm { p_example[i, 0] = p_example[i, 0] + i - 25 } - val settings = LMSettings(0, 0, 1) + val exampleNumber = 1 - var y_hat = funcMiddleForLm(t_example, p_example, settings) + var y_hat = funcMiddleForLm(t_example, p_example, exampleNumber) var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { @@ -219,9 +217,9 @@ class TestLmAlgorithm { p_example[i, 0] = p_example[i, 0] + i - 25 } - val settings = LMSettings(0, 0, 1) + val exampleNumber = 1 - var y_hat = funcDifficultForLm(t_example, p_example, settings) + var y_hat = funcDifficultForLm(t_example, p_example, exampleNumber) var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { From 162e37cb2fe8a3b27df82cbde91ff3442f4394de Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 02:52:00 +0300 Subject: [PATCH 20/34] removed extra comments, unnecessary variables, renaming variables and secondary functions --- .../StaticLm/staticDifficultTest.kt | 4 - .../StaticLm/staticEasyTest.kt | 1 - .../StaticLm/staticMiddleTest.kt | 4 - .../StreamingLm/streamLm.kt | 2 - .../core/LevenbergMarquardtAlgorithm.kt | 363 ++++++++---------- .../kmath/tensors/core/TestLmAlgorithm.kt | 14 +- 6 files changed, 165 insertions(+), 223 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt index 24cfa955a..95a62e21e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt @@ -49,9 +49,6 @@ fun main() { p_min = p_min.div(1.0 / -50.0) val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) p_min = p_min.div(1.0 / 50.0) - val consts = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) - ).as2D() val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-2, 11.0, 9.0, 1.0) // val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-3, 11.0, 9.0, 1.0) @@ -64,7 +61,6 @@ fun main() { dp, p_min.as2D(), p_max.as2D(), - consts, opts, 10, 1 diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt index c7b2b0def..0b26838a0 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt @@ -27,7 +27,6 @@ fun main() { startedData.dp, startedData.p_min, startedData.p_max, - startedData.consts, startedData.opts, 10, startedData.example_number diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt index 471143102..b60ea1897 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt @@ -48,9 +48,6 @@ fun main() { p_min = p_min.div(1.0 / -50.0) val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) p_min = p_min.div(1.0 / 50.0) - val consts = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.0) - ).as2D() val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) val result = DoubleTensorAlgebra.lm( @@ -62,7 +59,6 @@ fun main() { dp, p_min.as2D(), p_max.as2D(), - consts, opts, 10, 1 diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index f031d82bf..99fb97923 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -26,7 +26,6 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< val dp = startData.dp val p_min = startData.p_min val p_max = startData.p_max - val consts = startData.consts val opts = startData.opts var steps = numberOfLaunches @@ -42,7 +41,6 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< dp, p_min, p_max, - consts, opts, 10, example_number diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index 7fbf6ecd8..af41a7a00 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -31,7 +31,7 @@ import kotlin.reflect.KFunction3 * InReducedChiSquare: chi-squared convergence achieved * (chi squared value divided by (m - n + 1) < epsilon2 = opts[4], * where n - number of parameters, m - amount of points - * NoConvergence: the maximum number of iterations has been reached without reaching convergence + * NoConvergence: the maximum number of iterations has been reached without reaching any convergence */ public enum class TypeOfConvergence{ InGradient, @@ -61,172 +61,141 @@ public data class LMResultInfo ( public fun DoubleTensorAlgebra.lm( func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, - p_input: MutableStructure2D, t_input: MutableStructure2D, y_dat_input: MutableStructure2D, - weight_input: MutableStructure2D, dp_input: MutableStructure2D, p_min_input: MutableStructure2D, p_max_input: MutableStructure2D, - c_input: MutableStructure2D, opts_input: DoubleArray, nargin: Int, example_number: Int): LMResultInfo { + pInput: MutableStructure2D, tInput: MutableStructure2D, yDatInput: MutableStructure2D, + weightInput: MutableStructure2D, dpInput: MutableStructure2D, pMinInput: MutableStructure2D, + pMaxInput: MutableStructure2D, optsInput: DoubleArray, nargin: Int, exampleNumber: Int): LMResultInfo { val resultInfo = LMResultInfo(0, 0, 0.0, - 0.0, p_input, TypeOfConvergence.NoConvergence) + 0.0, pInput, TypeOfConvergence.NoConvergence) - val eps:Double = 2.2204e-16 + val eps = 2.2204e-16 - val settings = LMSettings(0, 0, example_number) + val settings = LMSettings(0, 0, exampleNumber) settings.funcCalls = 0 // running count of function evaluations - var p = p_input - val y_dat = y_dat_input - val t = t_input + var p = pInput + val t = tInput val Npar = length(p) // number of parameters - val Npnt = length(y_dat) // number of data points - var p_old = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters - var y_old = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) + val Npnt = length(yDatInput) // number of data points + var pOld = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters + var yOld = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) var X2 = 1e-3 / eps // a really big initial Chi-sq value - var X2_old = 1e-3 / eps // a really big initial Chi-sq value + var X2Old = 1e-3 / eps // a really big initial Chi-sq value var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix val DoF = Npnt - Npar // statistical degrees of freedom - var corr_p = 0 - var sigma_p = 0 - var sigma_y = 0 - var R_sq = 0 - var cvg_hist = 0 - - if (length(t) != length(y_dat)) { - // println("lm.m error: the length of t must equal the length of y_dat") - val length_t = length(t) - val length_y_dat = length(y_dat) - X2 = 0.0 - - corr_p = 0 - sigma_p = 0 - sigma_y = 0 - R_sq = 0 - cvg_hist = 0 - } - - var weight = weight_input + var weight = weightInput if (nargin < 5) { - weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((y_dat.transpose().dot(y_dat)).as1D()[0])).as2D() + weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((yDatInput.transpose().dot(yDatInput)).as1D()[0])).as2D() } - var dp = dp_input + var dp = dpInput if (nargin < 6) { dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.001)).as2D() } - var p_min = p_min_input + var pMin = pMinInput if (nargin < 7) { - p_min = p - p_min.abs() - p_min = p_min.div(-100.0).as2D() + pMin = p + pMin.abs() + pMin = pMin.div(-100.0).as2D() } - var p_max = p_max_input + var pMax = pMaxInput if (nargin < 8) { - p_max = p - p_max.abs() - p_max = p_max.div(100.0).as2D() + pMax = p + pMax.abs() + pMax = pMax.div(100.0).as2D() } - var c = c_input - if (nargin < 9) { - c = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(1.0)).as2D() - } - - var opts = opts_input + var opts = optsInput if (nargin < 10) { opts = doubleArrayOf(3.0, 10.0 * Npar, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) } val prnt = opts[0] // >1 intermediate results; >2 plots - val MaxIter = opts[1].toInt() // maximum number of iterations - val epsilon_1 = opts[2] // convergence tolerance for gradient - val epsilon_2 = opts[3] // convergence tolerance for parameters - val epsilon_3 = opts[4] // convergence tolerance for Chi-square - val epsilon_4 = opts[5] // determines acceptance of a L-M step - val lambda_0 = opts[6] // initial value of damping paramter, lambda - val lambda_UP_fac = opts[7] // factor for increasing lambda - val lambda_DN_fac = opts[8] // factor for decreasing lambda - val Update_Type = opts[9].toInt() // 1: Levenberg-Marquardt lambda update - // 2: Quadratic update - // 3: Nielsen's lambda update equations + val maxIterations = opts[1].toInt() // maximum number of iterations + val epsilon1 = opts[2] // convergence tolerance for gradient + val epsilon2 = opts[3] // convergence tolerance for parameters + val epsilon3 = opts[4] // convergence tolerance for Chi-square + val epsilon4 = opts[5] // determines acceptance of a L-M step + val lambda0 = opts[6] // initial value of damping paramter, lambda + val lambdaUpFac = opts[7] // factor for increasing lambda + val lambdaDnFac = opts[8] // factor for decreasing lambda + val updateType = opts[9].toInt() // 1: Levenberg-Marquardt lambda update + // 2: Quadratic update + // 3: Nielsen's lambda update equations - p_min = make_column(p_min) - p_max = make_column(p_max) + pMin = makeColumn(pMin) + pMax = makeColumn(pMax) - if (length(make_column(dp)) == 1) { + if (length(makeColumn(dp)) == 1) { dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() } - val idx = get_zero_indices(dp) // indices of the parameters to be fit - val Nfit = idx?.shape?.component1() // number of parameters to fit var stop = false // termination flag - val y_init = feval(func, t, p, example_number) // residual error using p_try if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() - // println("using uniform weights for error analysis") } else { - weight = make_column(weight) + weight = makeColumn(weight) weight.abs() } // initialize Jacobian with finite difference calculation - var lm_matx_ans = lm_matx(func, t, p_old, y_old,1, J, p, y_dat, weight, dp, settings) - var JtWJ = lm_matx_ans[0] - var JtWdy = lm_matx_ans[1] - X2 = lm_matx_ans[2][0, 0] - var y_hat = lm_matx_ans[3] - J = lm_matx_ans[4] + var lmMatxAns = lmMatx(func, t, pOld, yOld, 1, J, p, yDatInput, weight, dp, settings) + var JtWJ = lmMatxAns[0] + var JtWdy = lmMatxAns[1] + X2 = lmMatxAns[2][0, 0] + var yHat = lmMatxAns[3] + J = lmMatxAns[4] - if ( abs(JtWdy).max()!! < epsilon_1 ) { -// println(" *** Your Initial Guess is Extremely Close to Optimal ***\n") -// println(" *** epsilon_1 = %e\n$epsilon_1") + if ( abs(JtWdy).max() < epsilon1 ) { stop = true } var lambda = 1.0 var nu = 1 - when (Update_Type) { - 1 -> lambda = lambda_0 // Marquardt: init'l lambda + when (updateType) { + 1 -> lambda = lambda0 // Marquardt: init'l lambda else -> { // Quadratic and Nielsen - lambda = lambda_0 * (diag(JtWJ)).max()!! + lambda = lambda0 * (makeColumnFromDiagonal(JtWJ)).max()!! nu = 2 } } - X2_old = X2 // previous value of X2 - var cvg_hst = ones(ShapeND(intArrayOf(MaxIter, Npar + 3))) // initialize convergence history + X2Old = X2 // previous value of X2 var h: DoubleTensor - var dX2 = X2 - while (!stop && settings.iteration <= MaxIter) { //--- Start Main Loop + + while (!stop && settings.iteration <= maxIterations) { //--- Start Main Loop settings.iteration += 1 // incremental change in parameters - h = when (Update_Type) { + h = when (updateType) { 1 -> { // Marquardt - val solve = solve(JtWJ.plus(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda)).as2D(), JtWdy) + val solve = + solve(JtWJ.plus(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda)).as2D(), JtWdy) solve.asDoubleTensor() } else -> { // Quadratic and Nielsen - val solve = solve(JtWJ.plus(lm_eye(Npar).div(1 / lambda)).as2D(), JtWdy) + val solve = solve(JtWJ.plus(lmEye(Npar).div(1 / lambda)).as2D(), JtWdy) solve.asDoubleTensor() } } - var p_try = (p + h).as2D() // update the [idx] elements - p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try.as2D()), p_max) // apply constraints + var pTry = (p + h).as2D() // update the [idx] elements + pTry = smallestElementComparison(largestElementComparison(pMin, pTry.as2D()), pMax) // apply constraints - var delta_y = y_dat.minus(feval(func, t, p_try, example_number)) // residual error using p_try + var deltaY = yDatInput.minus(evaluateFunction(func, t, pTry, exampleNumber)) // residual error using p_try - for (i in 0 until delta_y.shape.component1()) { // floating point error; break - for (j in 0 until delta_y.shape.component2()) { - if (delta_y[i, j] == Double.POSITIVE_INFINITY || delta_y[i, j] == Double.NEGATIVE_INFINITY) { + for (i in 0 until deltaY.shape.component1()) { // floating point error; break + for (j in 0 until deltaY.shape.component2()) { + if (deltaY[i, j] == Double.POSITIVE_INFINITY || deltaY[i, j] == Double.NEGATIVE_INFINITY) { stop = true break } @@ -235,84 +204,87 @@ public fun DoubleTensorAlgebra.lm( settings.funcCalls += 1 - val tmp = delta_y.times(weight) - var X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria + val tmp = deltaY.times(weight) + var X2Try = deltaY.as2D().transpose().dot(tmp) // Chi-squared error criteria val alpha = 1.0 - if (Update_Type == 2) { // Quadratic + if (updateType == 2) { // Quadratic // One step of quadratic line update in the h direction for minimum X2 - val alpha = JtWdy.transpose().dot(h) / ( (X2_try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h)) ) + val alpha = JtWdy.transpose().dot(h) / ((X2Try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h))) h = h.dot(alpha) - p_try = p.plus(h).as2D() // update only [idx] elements - p_try = smallest_element_comparison(largest_element_comparison(p_min, p_try), p_max) // apply constraints + pTry = p.plus(h).as2D() // update only [idx] elements + pTry = smallestElementComparison(largestElementComparison(pMin, pTry), pMax) // apply constraints - var delta_y = y_dat.minus(feval(func, t, p_try, example_number)) // residual error using p_try + deltaY = yDatInput.minus(evaluateFunction(func, t, pTry, exampleNumber)) // residual error using p_try settings.funcCalls += 1 - val tmp = delta_y.times(weight) - X2_try = delta_y.as2D().transpose().dot(tmp) // Chi-squared error criteria + X2Try = deltaY.as2D().transpose().dot(deltaY.times(weight)) // Chi-squared error criteria } - val rho = when (Update_Type) { // Nielsen + val rho = when (updateType) { // Nielsen 1 -> { - val tmp = h.transposed().dot(make_matrx_with_diagonal(diag(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) - X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + val tmp = h.transposed() + .dot(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) + X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] } + else -> { val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) - X2.minus(X2_try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] } } - if (rho > epsilon_4) { // it IS significantly better - val dX2 = X2.minus(X2_old) - X2_old = X2 - p_old = p.copyToTensor().as2D() - y_old = y_hat.copyToTensor().as2D() - p = make_column(p_try) // accept p_try + if (rho > epsilon4) { // it IS significantly better + val dX2 = X2.minus(X2Old) + X2Old = X2 + pOld = p.copyToTensor().as2D() + yOld = yHat.copyToTensor().as2D() + p = makeColumn(pTry) // accept p_try - lm_matx_ans = lm_matx(func, t, p_old, y_old, dX2.toInt(), J, p, y_dat, weight, dp, settings) + lmMatxAns = lmMatx(func, t, pOld, yOld, dX2.toInt(), J, p, yDatInput, weight, dp, settings) // decrease lambda ==> Gauss-Newton method - JtWJ = lm_matx_ans[0] - JtWdy = lm_matx_ans[1] - X2 = lm_matx_ans[2][0, 0] - y_hat = lm_matx_ans[3] - J = lm_matx_ans[4] + JtWJ = lmMatxAns[0] + JtWdy = lmMatxAns[1] + X2 = lmMatxAns[2][0, 0] + yHat = lmMatxAns[3] + J = lmMatxAns[4] - lambda = when (Update_Type) { + lambda = when (updateType) { 1 -> { // Levenberg - max(lambda / lambda_DN_fac, 1e-7); + max(lambda / lambdaDnFac, 1e-7); } + 2 -> { // Quadratic - max( lambda / (1 + alpha) , 1e-7 ); + max(lambda / (1 + alpha), 1e-7); } + else -> { // Nielsen nu = 2 - lambda * max( 1.0 / 3, 1 - (2 * rho - 1).pow(3) ) + lambda * max(1.0 / 3, 1 - (2 * rho - 1).pow(3)) } } - } - else { // it IS NOT better - X2 = X2_old // do not accept p_try - if (settings.iteration % (2 * Npar) == 0 ) { // rank-1 update of Jacobian - lm_matx_ans = lm_matx(func, t, p_old, y_old,-1, J, p, y_dat, weight, dp, settings) - JtWJ = lm_matx_ans[0] - JtWdy = lm_matx_ans[1] - dX2 = lm_matx_ans[2][0, 0] - y_hat = lm_matx_ans[3] - J = lm_matx_ans[4] + } else { // it IS NOT better + X2 = X2Old // do not accept p_try + if (settings.iteration % (2 * Npar) == 0) { // rank-1 update of Jacobian + lmMatxAns = lmMatx(func, t, pOld, yOld, -1, J, p, yDatInput, weight, dp, settings) + JtWJ = lmMatxAns[0] + JtWdy = lmMatxAns[1] + yHat = lmMatxAns[3] + J = lmMatxAns[4] } // increase lambda ==> gradient descent method - lambda = when (Update_Type) { + lambda = when (updateType) { 1 -> { // Levenberg - min(lambda * lambda_UP_fac, 1e7) + min(lambda * lambdaUpFac, 1e7) } + 2 -> { // Quadratic - lambda + kotlin.math.abs(((X2_try.as2D()[0, 0] - X2) / 2) / alpha) + lambda + kotlin.math.abs(((X2Try.as2D()[0, 0] - X2) / 2) / alpha) } + else -> { // Nielsen nu *= 2 lambda * (nu / 2) @@ -321,30 +293,27 @@ public fun DoubleTensorAlgebra.lm( } if (prnt > 1) { - val chi_sq = X2 / DoF + val chiSq = X2 / DoF resultInfo.iterations = settings.iteration resultInfo.funcCalls = settings.funcCalls - resultInfo.resultChiSq = chi_sq + resultInfo.resultChiSq = chiSq resultInfo.resultLambda = lambda resultInfo.resultParameters = p } - // update convergence history ... save _reduced_ Chi-square - // cvg_hst(iteration,:) = [ func_calls p' X2/DoF lambda ]; - - if (abs(JtWdy).max()!! < epsilon_1 && settings.iteration > 2) { + if (abs(JtWdy).max() < epsilon1 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InGradient stop = true } - if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon_2 && settings.iteration > 2) { + if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon2 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InParameters stop = true } - if (X2 / DoF < epsilon_3 && settings.iteration > 2) { + if (X2 / DoF < epsilon3 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InReducedChiSquare stop = true } - if (settings.iteration == MaxIter) { + if (settings.iteration == maxIterations) { resultInfo.typeOfConvergence = TypeOfConvergence.NoConvergence stop = true } @@ -358,8 +327,8 @@ private data class LMSettings ( var exampleNumber:Int ) -/* matrix -> column of all elemnets */ -private fun make_column(tensor: MutableStructure2D) : MutableStructure2D { +/* matrix -> column of all elements */ +private fun makeColumn(tensor: MutableStructure2D): MutableStructure2D { val shape = intArrayOf(tensor.shape.component1() * tensor.shape.component2(), 1) val buffer = DoubleArray(tensor.shape.component1() * tensor.shape.component2()) for (i in 0 until tensor.shape.component1()) { @@ -367,8 +336,7 @@ private fun make_column(tensor: MutableStructure2D) : MutableStructure2D buffer[i * tensor.shape.component2() + j] = tensor[i, j] } } - val column = BroadcastDoubleTensorAlgebra.fromArray(ShapeND(shape), buffer).as2D() - return column + return BroadcastDoubleTensorAlgebra.fromArray(ShapeND(shape), buffer).as2D() } /* column length */ @@ -401,7 +369,7 @@ private fun abs(input: MutableStructure2D): MutableStructure2D { return tensor } -private fun diag(input: MutableStructure2D): MutableStructure2D { +private fun makeColumnFromDiagonal(input: MutableStructure2D): MutableStructure2D { val tensor = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(input.shape.component1(), 1))).as2D() for (i in 0 until tensor.shape.component1()) { tensor[i, 0] = input[i, i] @@ -409,7 +377,7 @@ private fun diag(input: MutableStructure2D): MutableStructure2D return tensor } -private fun make_matrx_with_diagonal(column: MutableStructure2D): MutableStructure2D { +private fun makeMatrixWithDiagonal(column: MutableStructure2D): MutableStructure2D { val size = column.shape.component1() val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(size, size))).as2D() for (i in 0 until size) { @@ -418,23 +386,23 @@ private fun make_matrx_with_diagonal(column: MutableStructure2D): Mutabl return tensor } -private fun lm_eye(size: Int): MutableStructure2D { +private fun lmEye(size: Int): MutableStructure2D { val column = BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(size, 1))).as2D() - return make_matrx_with_diagonal(column) + return makeMatrixWithDiagonal(column) } -private fun largest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { - val a_sizeX = a.shape.component1() - val a_sizeY = a.shape.component2() - val b_sizeX = b.shape.component1() - val b_sizeY = b.shape.component2() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() +private fun largestElementComparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val aSizeX = a.shape.component1() + val aSizeY = a.shape.component2() + val bSizeX = b.shape.component1() + val bSizeY = b.shape.component2() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(aSizeX, bSizeX), max(aSizeY, bSizeY)))).as2D() for (i in 0 until tensor.shape.component1()) { for (j in 0 until tensor.shape.component2()) { - if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { + if (i < aSizeX && i < bSizeX && j < aSizeY && j < bSizeY) { tensor[i, j] = max(a[i, j], b[i, j]) } - else if (i < a_sizeX && j < a_sizeY) { + else if (i < aSizeX && j < aSizeY) { tensor[i, j] = a[i, j] } else { @@ -445,18 +413,18 @@ private fun largest_element_comparison(a: MutableStructure2D, b: Mutable return tensor } -private fun smallest_element_comparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { - val a_sizeX = a.shape.component1() - val a_sizeY = a.shape.component2() - val b_sizeX = b.shape.component1() - val b_sizeY = b.shape.component2() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(a_sizeX, b_sizeX), max(a_sizeY, b_sizeY)))).as2D() +private fun smallestElementComparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { + val aSizeX = a.shape.component1() + val aSizeY = a.shape.component2() + val bSizeX = b.shape.component1() + val bSizeY = b.shape.component2() + val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(aSizeX, bSizeX), max(aSizeY, bSizeY)))).as2D() for (i in 0 until tensor.shape.component1()) { for (j in 0 until tensor.shape.component2()) { - if (i < a_sizeX && i < b_sizeX && j < a_sizeY && j < b_sizeY) { + if (i < aSizeX && i < bSizeX && j < aSizeY && j < bSizeY) { tensor[i, j] = min(a[i, j], b[i, j]) } - else if (i < a_sizeX && j < a_sizeY) { + else if (i < aSizeX && j < aSizeY) { tensor[i, j] = a[i, j] } else { @@ -467,71 +435,69 @@ private fun smallest_element_comparison(a: MutableStructure2D, b: Mutabl return tensor } -private fun get_zero_indices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { +private fun getZeroIndices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { var idx = emptyArray() for (i in 0 until column.shape.component1()) { if (kotlin.math.abs(column[i, 0]) > epsilon) { idx += (i + 1.0) } } - if (idx.size > 0) { + if (idx.isNotEmpty()) { return BroadcastDoubleTensorAlgebra.fromArray(ShapeND(intArrayOf(idx.size, 1)), idx.toDoubleArray()).as2D() } return null } -private fun feval(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int) +private fun evaluateFunction(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int) : MutableStructure2D { return func(t, p, exampleNumber) } -private fun lm_matx(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, - t: MutableStructure2D, p_old: MutableStructure2D, y_old: MutableStructure2D, - dX2: Int, J_input: MutableStructure2D, p: MutableStructure2D, - y_dat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> +private fun lmMatx(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, pOld: MutableStructure2D, yOld: MutableStructure2D, + dX2: Int, JInput: MutableStructure2D, p: MutableStructure2D, + yDat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> { // default: dp = 0.001 - - val Npnt = length(y_dat) // number of data points val Npar = length(p) // number of parameters - val y_hat = feval(func, t, p, settings.exampleNumber) // evaluate model using parameters 'p' + val yHat = evaluateFunction(func, t, p, settings.exampleNumber) // evaluate model using parameters 'p' settings.funcCalls += 1 - var J = J_input + var J = JInput - if (settings.iteration % (2 * Npar) == 0 || dX2 > 0) { - J = lm_FD_J(func, t, p, y_hat, dp, settings).as2D() // finite difference + J = if (settings.iteration % (2 * Npar) == 0 || dX2 > 0) { + lmFdJ(func, t, p, yHat, dp, settings).as2D() // finite difference } else { - J = lm_Broyden_J(p_old, y_old, J, p, y_hat).as2D() // rank-1 update + lmBroydenJ(pOld, yOld, J, p, yHat).as2D() // rank-1 update } - val delta_y = y_dat.minus(y_hat) + val deltaY = yDat.minus(yHat) - val Chi_sq = delta_y.transposed().dot( delta_y.times(weight) ).as2D() + val chiSq = deltaY.transposed().dot( deltaY.times(weight) ).as2D() val JtWJ = J.transposed().dot ( J.times( weight.dot(BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(1, Npar)))) ) ).as2D() - val JtWdy = J.transposed().dot( weight.times(delta_y) ).as2D() + val JtWdy = J.transposed().dot( weight.times(deltaY) ).as2D() - return arrayOf(JtWJ,JtWdy,Chi_sq,y_hat,J) + return arrayOf(JtWJ,JtWdy,chiSq,yHat,J) } -private fun lm_Broyden_J(p_old: MutableStructure2D, y_old: MutableStructure2D, J_input: MutableStructure2D, - p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { - var J = J_input.copyToTensor() +private fun lmBroydenJ(pOld: MutableStructure2D, yOld: MutableStructure2D, JInput: MutableStructure2D, + p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { + var J = JInput.copyToTensor() - val h = p.minus(p_old) - val increase = y.minus(y_old).minus( J.dot(h) ).dot(h.transposed()).div( (h.transposed().dot(h)).as2D()[0, 0] ) + val h = p.minus(pOld) + val increase = y.minus(yOld).minus( J.dot(h) ).dot(h.transposed()).div( (h.transposed().dot(h)).as2D()[0, 0] ) J = J.plus(increase) return J.as2D() } -private fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, - dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { +private fun lmFdJ(func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, + dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { // default: dp = 0.001 * ones(1,n) val m = length(y) // number of data points @@ -548,7 +514,7 @@ private fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D epsilon) { - val y1 = feval(func, t, p, settings.exampleNumber) + val y1 = evaluateFunction(func, t, p, settings.exampleNumber) settings.funcCalls += 1 if (dp[j, 0] < 0) { // backwards difference @@ -558,10 +524,9 @@ private fun lm_FD_J(func: (MutableStructure2D, MutableStructure2D Date: Wed, 7 Jun 2023 05:25:32 +0300 Subject: [PATCH 21/34] the input data is placed in a separate class, to which the documentation is written --- .../StaticLm/staticDifficultTest.kt | 20 +- .../StaticLm/staticEasyTest.kt | 17 +- .../StaticLm/staticMiddleTest.kt | 20 +- .../StreamingLm/streamLm.kt | 32 +-- .../LevenbergMarquardt/functionsToOptimize.kt | 14 +- .../core/LevenbergMarquardtAlgorithm.kt | 193 +++++++++++------- .../kmath/tensors/core/TestLmAlgorithm.kt | 45 ++-- 7 files changed, 197 insertions(+), 144 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt index 95a62e21e..e6f575262 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticDifficultTest.kt @@ -12,7 +12,8 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.lm +import space.kscience.kmath.tensors.core.LMInput +import space.kscience.kmath.tensors.core.levenbergMarquardt import kotlin.math.roundToInt fun main() { @@ -39,9 +40,7 @@ fun main() { var t = t_example val y_dat = y_hat - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 / Nparams * 1.0 - 0.085 } - ).as2D() + val weight = 1.0 / Nparams * 1.0 - 0.085 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() @@ -52,8 +51,7 @@ fun main() { val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-2, 11.0, 9.0, 1.0) // val opts = doubleArrayOf(3.0, 10000.0, 1e-6, 1e-6, 1e-6, 1e-6, 1e-3, 11.0, 9.0, 1.0) - val result = DoubleTensorAlgebra.lm( - ::funcDifficultForLm, + val inputData = LMInput(::funcDifficultForLm, p_init.as2D(), t, y_dat, @@ -61,10 +59,14 @@ fun main() { dp, p_min.as2D(), p_max.as2D(), - opts, + opts[1].toInt(), + doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), + doubleArrayOf(opts[6], opts[7], opts[8]), + opts[9].toInt(), 10, - 1 - ) + 1) + + val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) println("Parameters:") for (i in 0 until result.resultParameters.shape.component1()) { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt index 0b26838a0..507943031 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticEasyTest.kt @@ -12,14 +12,13 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcDifficultForLm import space.kscience.kmath.tensors.LevenbergMarquardt.funcEasyForLm import space.kscience.kmath.tensors.LevenbergMarquardt.getStartDataForFuncEasy import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.lm +import space.kscience.kmath.tensors.core.LMInput +import space.kscience.kmath.tensors.core.levenbergMarquardt import kotlin.math.roundToInt fun main() { val startedData = getStartDataForFuncEasy() - - val result = DoubleTensorAlgebra.lm( - ::funcEasyForLm, + val inputData = LMInput(::funcEasyForLm, DoubleTensorAlgebra.ones(ShapeND(intArrayOf(4, 1))).as2D(), startedData.t, startedData.y_dat, @@ -27,10 +26,14 @@ fun main() { startedData.dp, startedData.p_min, startedData.p_max, - startedData.opts, + startedData.opts[1].toInt(), + doubleArrayOf(startedData.opts[2], startedData.opts[3], startedData.opts[4], startedData.opts[5]), + doubleArrayOf(startedData.opts[6], startedData.opts[7], startedData.opts[8]), + startedData.opts[9].toInt(), 10, - startedData.example_number - ) + startedData.example_number) + + val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) println("Parameters:") for (i in 0 until result.resultParameters.shape.component1()) { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt index b60ea1897..0659db103 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StaticLm/staticMiddleTest.kt @@ -12,7 +12,8 @@ import space.kscience.kmath.tensors.LevenbergMarquardt.funcMiddleForLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.lm +import space.kscience.kmath.tensors.core.LMInput +import space.kscience.kmath.tensors.core.levenbergMarquardt import kotlin.math.roundToInt fun main() { val NData = 100 @@ -38,9 +39,7 @@ fun main() { var t = t_example val y_dat = y_hat - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 } - ).as2D() + val weight = 1.0 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() @@ -50,8 +49,7 @@ fun main() { p_min = p_min.div(1.0 / 50.0) val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) - val result = DoubleTensorAlgebra.lm( - ::funcMiddleForLm, + val inputData = LMInput(::funcMiddleForLm, p_init.as2D(), t, y_dat, @@ -59,10 +57,14 @@ fun main() { dp, p_min.as2D(), p_max.as2D(), - opts, + opts[1].toInt(), + doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), + doubleArrayOf(opts[6], opts[7], opts[8]), + opts[9].toInt(), 10, - 1 - ) + 1) + + val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) println("Parameters:") for (i in 0 until result.resultParameters.shape.component1()) { diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index 99fb97923..fe96b2fe9 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -11,7 +11,8 @@ import space.kscience.kmath.nd.* import space.kscience.kmath.tensors.LevenbergMarquardt.StartDataLm import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.zeros import space.kscience.kmath.tensors.core.DoubleTensorAlgebra -import space.kscience.kmath.tensors.core.lm +import space.kscience.kmath.tensors.core.LMInput +import space.kscience.kmath.tensors.core.levenbergMarquardt import kotlin.random.Random import kotlin.reflect.KFunction3 @@ -31,20 +32,23 @@ fun streamLm(lm_func: KFunction3, MutableStructure2D< var steps = numberOfLaunches val isEndless = (steps <= 0) + val inputData = LMInput(lm_func, + p_init, + t, + y_dat, + weight, + dp, + p_min, + p_max, + opts[1].toInt(), + doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), + doubleArrayOf(opts[6], opts[7], opts[8]), + opts[9].toInt(), + 10, + example_number) + while (isEndless || steps > 0) { - val result = DoubleTensorAlgebra.lm( - lm_func, - p_init, - t, - y_dat, - weight, - dp, - p_min, - p_max, - opts, - 10, - example_number - ) + val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) emit(result.resultParameters) delay(launchFrequencyInMs) p_init = result.resultParameters diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt index 537b86da3..7ccb37ed0 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/functionsToOptimize.kt @@ -24,7 +24,7 @@ public data class StartDataLm ( var p_init: MutableStructure2D, var t: MutableStructure2D, var y_dat: MutableStructure2D, - var weight: MutableStructure2D, + var weight: Double, var dp: MutableStructure2D, var p_min: MutableStructure2D, var p_max: MutableStructure2D, @@ -113,9 +113,7 @@ fun getStartDataForFuncDifficult(): StartDataLm { var t = t_example val y_dat = y_hat - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 / Nparams * 1.0 - 0.085 } - ).as2D() + val weight = 1.0 / Nparams * 1.0 - 0.085 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() @@ -154,9 +152,7 @@ fun getStartDataForFuncMiddle(): StartDataLm { } var t = t_example val y_dat = y_hat - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 } - ).as2D() + val weight = 1.0 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() @@ -202,9 +198,7 @@ fun getStartDataForFuncEasy(): StartDataLm { ShapeND(intArrayOf(100, 1)), lm_matx_y_dat ).as2D() - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 4.0 } - ).as2D() + val weight = 4.0 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index af41a7a00..ec9d92c88 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -19,21 +19,21 @@ import kotlin.math.pow import kotlin.reflect.KFunction3 /** - * Type of convergence achieved as a result of executing the Levenberg-Marquardt algorithm + * Type of convergence achieved as a result of executing the Levenberg-Marquardt algorithm. * * InGradient: gradient convergence achieved - * (max(J^T W dy) < epsilon1 = opts[2], + * (max(J^T W dy) < epsilon1, * where J - Jacobi matrix (dy^/dp) for the current approximation y^, - * W - weight matrix from input, dy = (y - y^(p))) + * W - weight matrix from input, dy = (y - y^(p))). * InParameters: convergence in parameters achieved - * (max(h_i / p_i) < epsilon2 = opts[3], - * where h_i - offset for parameter p_i on the current iteration) + * (max(h_i / p_i) < epsilon2, + * where h_i - offset for parameter p_i on the current iteration). * InReducedChiSquare: chi-squared convergence achieved - * (chi squared value divided by (m - n + 1) < epsilon2 = opts[4], - * where n - number of parameters, m - amount of points - * NoConvergence: the maximum number of iterations has been reached without reaching any convergence + * (chi squared value divided by (m - n + 1) < epsilon2, + * where n - number of parameters, m - amount of points). + * NoConvergence: the maximum number of iterations has been reached without reaching any convergence. */ -public enum class TypeOfConvergence{ +public enum class TypeOfConvergence { InGradient, InParameters, InReducedChiSquare, @@ -41,14 +41,14 @@ public enum class TypeOfConvergence{ } /** - * Class for the data obtained as a result of the execution of the Levenberg-Marquardt algorithm + * The data obtained as a result of the execution of the Levenberg-Marquardt algorithm. * - * iterations: number of completed iterations - * funcCalls: the number of evaluations of the input function during execution - * resultChiSq: chi squared value on final parameters - * resultLambda: final lambda parameter used to calculate the offset - * resultParameters: final parameters - * typeOfConvergence: type of convergence + * iterations: number of completed iterations. + * funcCalls: the number of evaluations of the input function during execution. + * resultChiSq: chi squared value on final parameters. + * resultLambda: final lambda parameter used to calculate the offset. + * resultParameters: final parameters. + * typeOfConvergence: type of convergence. */ public data class LMResultInfo ( var iterations:Int, @@ -59,26 +59,65 @@ public data class LMResultInfo ( var typeOfConvergence: TypeOfConvergence, ) -public fun DoubleTensorAlgebra.lm( - func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, - pInput: MutableStructure2D, tInput: MutableStructure2D, yDatInput: MutableStructure2D, - weightInput: MutableStructure2D, dpInput: MutableStructure2D, pMinInput: MutableStructure2D, - pMaxInput: MutableStructure2D, optsInput: DoubleArray, nargin: Int, exampleNumber: Int): LMResultInfo { - +/** + * Input data for the Levenberg-Marquardt function. + * + * func: function of n independent variables x, m parameters an example number, + * rotating a vector of n values y, in which each of the y_i is calculated at its x_i with the given parameters. + * startParameters: starting parameters. + * independentVariables: independent variables, for each of which the real value is known. + * realValues: real values obtained with given independent variables but unknown parameters. + * weight: measurement error for realValues (denominator in each term of sum of weighted squared errors). + * pDelta: delta when calculating the derivative with respect to parameters. + * minParameters: the lower bound of parameter values. + * maxParameters: upper limit of parameter values. + * maxIterations: maximum allowable number of iterations. + * epsilons: epsilon1 - convergence tolerance for gradient, + * epsilon2 - convergence tolerance for parameters, + * epsilon3 - convergence tolerance for reduced chi-square, + * epsilon4 - determines acceptance of a step. + * lambdas: lambda0 - starting lambda value for parameter offset count, + * lambdaUp - factor for increasing lambda, + * lambdaDown - factor for decreasing lambda. + * updateType: 1: Levenberg-Marquardt lambda update, + * 2: Quadratic update, + * 3: Nielsen's lambda update equations. + * nargin: a value that determines which options to use by default + * (<5 - use weight by default, <6 - use pDelta by default, <7 - use minParameters by default, + * <8 - use maxParameters by default, <9 - use updateType by default). + * exampleNumber: a parameter for a function with which you can choose its behavior. + */ +public data class LMInput ( + var func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, + var startParameters: MutableStructure2D, + var independentVariables: MutableStructure2D, + var realValues: MutableStructure2D, + var weight: Double, + var pDelta: MutableStructure2D, + var minParameters: MutableStructure2D, + var maxParameters: MutableStructure2D, + var maxIterations: Int, + var epsilons: DoubleArray, + var lambdas: DoubleArray, + var updateType: Int, + var nargin: Int, + var exampleNumber: Int +) +public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultInfo { val resultInfo = LMResultInfo(0, 0, 0.0, - 0.0, pInput, TypeOfConvergence.NoConvergence) + 0.0, inputData.startParameters, TypeOfConvergence.NoConvergence) val eps = 2.2204e-16 - val settings = LMSettings(0, 0, exampleNumber) + val settings = LMSettings(0, 0, inputData.exampleNumber) settings.funcCalls = 0 // running count of function evaluations - var p = pInput - val t = tInput + var p = inputData.startParameters + val t = inputData.independentVariables val Npar = length(p) // number of parameters - val Npnt = length(yDatInput) // number of data points + val Npnt = length(inputData.realValues) // number of data points var pOld = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters var yOld = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) var X2 = 1e-3 / eps // a really big initial Chi-sq value @@ -86,50 +125,55 @@ public fun DoubleTensorAlgebra.lm( var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix val DoF = Npnt - Npar // statistical degrees of freedom - var weight = weightInput - if (nargin < 5) { - weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((yDatInput.transpose().dot(yDatInput)).as1D()[0])).as2D() + var weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(inputData.weight)).as2D() + if (inputData.nargin < 5) { + weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((inputData.realValues.transpose().dot(inputData.realValues)).as1D()[0])).as2D() } - var dp = dpInput - if (nargin < 6) { + var dp = inputData.pDelta + if (inputData.nargin < 6) { dp = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(0.001)).as2D() } - var pMin = pMinInput - if (nargin < 7) { - pMin = p - pMin.abs() - pMin = pMin.div(-100.0).as2D() + var minParameters = inputData.minParameters + if (inputData.nargin < 7) { + minParameters = p + minParameters.abs() + minParameters = minParameters.div(-100.0).as2D() } - var pMax = pMaxInput - if (nargin < 8) { - pMax = p - pMax.abs() - pMax = pMax.div(100.0).as2D() + var maxParameters = inputData.maxParameters + if (inputData.nargin < 8) { + maxParameters = p + maxParameters.abs() + maxParameters = maxParameters.div(100.0).as2D() } - var opts = optsInput - if (nargin < 10) { - opts = doubleArrayOf(3.0, 10.0 * Npar, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) + var maxIterations = inputData.maxIterations + var epsilon1 = inputData.epsilons[0] // convergence tolerance for gradient + var epsilon2 = inputData.epsilons[1] // convergence tolerance for parameters + var epsilon3 = inputData.epsilons[2] // convergence tolerance for Chi-square + var epsilon4 = inputData.epsilons[3] // determines acceptance of a L-M step + var lambda0 = inputData.lambdas[0] // initial value of damping paramter, lambda + var lambdaUpFac = inputData.lambdas[1] // factor for increasing lambda + var lambdaDnFac = inputData.lambdas[2] // factor for decreasing lambda + var updateType = inputData.updateType // 1: Levenberg-Marquardt lambda update + // 2: Quadratic update + // 3: Nielsen's lambda update equations + if (inputData.nargin < 9) { + maxIterations = 10 * Npar + epsilon1 = 1e-3 + epsilon2 = 1e-3 + epsilon3 = 1e-1 + epsilon4 = 1e-1 + lambda0 = 1e-2 + lambdaUpFac = 11.0 + lambdaDnFac = 9.0 + updateType = 1 } - val prnt = opts[0] // >1 intermediate results; >2 plots - val maxIterations = opts[1].toInt() // maximum number of iterations - val epsilon1 = opts[2] // convergence tolerance for gradient - val epsilon2 = opts[3] // convergence tolerance for parameters - val epsilon3 = opts[4] // convergence tolerance for Chi-square - val epsilon4 = opts[5] // determines acceptance of a L-M step - val lambda0 = opts[6] // initial value of damping paramter, lambda - val lambdaUpFac = opts[7] // factor for increasing lambda - val lambdaDnFac = opts[8] // factor for decreasing lambda - val updateType = opts[9].toInt() // 1: Levenberg-Marquardt lambda update - // 2: Quadratic update - // 3: Nielsen's lambda update equations - - pMin = makeColumn(pMin) - pMax = makeColumn(pMax) + minParameters = makeColumn(minParameters) + maxParameters = makeColumn(maxParameters) if (length(makeColumn(dp)) == 1) { dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() @@ -146,7 +190,7 @@ public fun DoubleTensorAlgebra.lm( } // initialize Jacobian with finite difference calculation - var lmMatxAns = lmMatx(func, t, pOld, yOld, 1, J, p, yDatInput, weight, dp, settings) + var lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, 1, J, p, inputData.realValues, weight, dp, settings) var JtWJ = lmMatxAns[0] var JtWdy = lmMatxAns[1] X2 = lmMatxAns[2][0, 0] @@ -189,9 +233,9 @@ public fun DoubleTensorAlgebra.lm( } var pTry = (p + h).as2D() // update the [idx] elements - pTry = smallestElementComparison(largestElementComparison(pMin, pTry.as2D()), pMax) // apply constraints + pTry = smallestElementComparison(largestElementComparison(minParameters, pTry.as2D()), maxParameters) // apply constraints - var deltaY = yDatInput.minus(evaluateFunction(func, t, pTry, exampleNumber)) // residual error using p_try + var deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try for (i in 0 until deltaY.shape.component1()) { // floating point error; break for (j in 0 until deltaY.shape.component2()) { @@ -214,9 +258,9 @@ public fun DoubleTensorAlgebra.lm( val alpha = JtWdy.transpose().dot(h) / ((X2Try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h))) h = h.dot(alpha) pTry = p.plus(h).as2D() // update only [idx] elements - pTry = smallestElementComparison(largestElementComparison(pMin, pTry), pMax) // apply constraints + pTry = smallestElementComparison(largestElementComparison(minParameters, pTry), maxParameters) // apply constraints - deltaY = yDatInput.minus(evaluateFunction(func, t, pTry, exampleNumber)) // residual error using p_try + deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try settings.funcCalls += 1 X2Try = deltaY.as2D().transpose().dot(deltaY.times(weight)) // Chi-squared error criteria @@ -242,7 +286,7 @@ public fun DoubleTensorAlgebra.lm( yOld = yHat.copyToTensor().as2D() p = makeColumn(pTry) // accept p_try - lmMatxAns = lmMatx(func, t, pOld, yOld, dX2.toInt(), J, p, yDatInput, weight, dp, settings) + lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, dX2.toInt(), J, p, inputData.realValues, weight, dp, settings) // decrease lambda ==> Gauss-Newton method JtWJ = lmMatxAns[0] @@ -268,7 +312,7 @@ public fun DoubleTensorAlgebra.lm( } else { // it IS NOT better X2 = X2Old // do not accept p_try if (settings.iteration % (2 * Npar) == 0) { // rank-1 update of Jacobian - lmMatxAns = lmMatx(func, t, pOld, yOld, -1, J, p, yDatInput, weight, dp, settings) + lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, -1, J, p, inputData.realValues, weight, dp, settings) JtWJ = lmMatxAns[0] JtWdy = lmMatxAns[1] yHat = lmMatxAns[3] @@ -292,14 +336,13 @@ public fun DoubleTensorAlgebra.lm( } } - if (prnt > 1) { - val chiSq = X2 / DoF - resultInfo.iterations = settings.iteration - resultInfo.funcCalls = settings.funcCalls - resultInfo.resultChiSq = chiSq - resultInfo.resultLambda = lambda - resultInfo.resultParameters = p - } + val chiSq = X2 / DoF + resultInfo.iterations = settings.iteration + resultInfo.funcCalls = settings.funcCalls + resultInfo.resultChiSq = chiSq + resultInfo.resultLambda = lambda + resultInfo.resultParameters = p + if (abs(JtWdy).max() < epsilon1 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InGradient diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 114e5f879..4e1df3b08 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -105,9 +105,7 @@ class TestLmAlgorithm { ShapeND(intArrayOf(100, 1)), lm_matx_y_dat ).as2D() - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 4.0 } - ).as2D() + val weight = 4.0 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } @@ -123,7 +121,12 @@ class TestLmAlgorithm { val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) - val result = lm(::funcEasyForLm, p_init, t, y_dat, weight, dp, p_min, p_max, opts, 10, example_number) + val inputData = LMInput(::funcEasyForLm, p_init, t, y_dat, weight, dp, p_min, p_max, opts[1].toInt(), + doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), + doubleArrayOf(opts[6], opts[7], opts[8]), + opts[9].toInt(), 10, example_number) + + val result = levenbergMarquardt(inputData) assertEquals(13, result.iterations) assertEquals(31, result.funcCalls) assertEquals(0.9131368192633, (result.resultChiSq * 1e13).roundToLong() / 1e13) @@ -168,9 +171,7 @@ class TestLmAlgorithm { var t = t_example val y_dat = y_hat - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 } - ).as2D() + val weight = 1.0 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() @@ -180,8 +181,7 @@ class TestLmAlgorithm { p_min = p_min.div(1.0 / 50.0) val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) - val result = DoubleTensorAlgebra.lm( - ::funcMiddleForLm, + val inputData = LMInput(::funcMiddleForLm, p_init.as2D(), t, y_dat, @@ -189,10 +189,14 @@ class TestLmAlgorithm { dp, p_min.as2D(), p_max.as2D(), - opts, + opts[1].toInt(), + doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), + doubleArrayOf(opts[6], opts[7], opts[8]), + opts[9].toInt(), 10, - 1 - ) + 1) + + val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) } @Test @@ -220,9 +224,7 @@ class TestLmAlgorithm { var t = t_example val y_dat = y_hat - val weight = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(1, 1)), DoubleArray(1) { 1.0 / Nparams * 1.0 - 0.085 } - ).as2D() + val weight = 1.0 / Nparams * 1.0 - 0.085 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() @@ -232,8 +234,7 @@ class TestLmAlgorithm { p_min = p_min.div(1.0 / 50.0) val opts = doubleArrayOf(3.0, 7000.0, 1e-2, 1e-3, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) - val result = DoubleTensorAlgebra.lm( - ::funcDifficultForLm, + val inputData = LMInput(::funcDifficultForLm, p_init.as2D(), t, y_dat, @@ -241,9 +242,13 @@ class TestLmAlgorithm { dp, p_min.as2D(), p_max.as2D(), - opts, + opts[1].toInt(), + doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), + doubleArrayOf(opts[6], opts[7], opts[8]), + opts[9].toInt(), 10, - 1 - ) + 1) + + val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) } } \ No newline at end of file From 0655642933a67bf971c043b6583877ea07eb791e Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 06:00:58 +0300 Subject: [PATCH 22/34] add documentation to the main function levenbergMarquardt --- .../tensors/core/LevenbergMarquardtAlgorithm.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index ec9d92c88..b0caf987a 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -104,6 +104,21 @@ public data class LMInput ( var exampleNumber: Int ) + +/** + * Levenberg-Marquardt optimization. + * + * An optimization method that iteratively searches for the optimal function parameters + * that best describe the dataset. The 'input' is the function being optimized, a set of real data + * (calculated with independent variables, but with an unknown set of parameters), a set of + * independent variables, and variables for adjusting the algorithm, described in the documentation for the LMInput class. + * The function returns number of completed iterations, the number of evaluations of the input function during execution, + * chi squared value on final parameters, final lambda parameter used to calculate the offset, final parameters + * and type of convergence in the 'output'. + * + * @receiver the `input`. + * @return the 'output'. + */ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultInfo { val resultInfo = LMResultInfo(0, 0, 0.0, 0.0, inputData.startParameters, TypeOfConvergence.NoConvergence) From 346e2e97f24c27a896ff96b4689c9dae242e9bc7 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 06:14:05 +0300 Subject: [PATCH 23/34] add minor fixes --- .../core/LevenbergMarquardtAlgorithm.kt | 90 +++++++++---------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index b0caf987a..d9c282fb6 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -131,14 +131,14 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI var p = inputData.startParameters val t = inputData.independentVariables - val Npar = length(p) // number of parameters - val Npnt = length(inputData.realValues) // number of data points - var pOld = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters - var yOld = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) - var X2 = 1e-3 / eps // a really big initial Chi-sq value - var X2Old = 1e-3 / eps // a really big initial Chi-sq value - var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix - val DoF = Npnt - Npar // statistical degrees of freedom + val Npar = length(p) // number of parameters + val Npnt = length(inputData.realValues) // number of data points + var pOld = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters + var yOld = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) + var X2 = 1e-3 / eps // a really big initial Chi-sq value + var X2Old = 1e-3 / eps // a really big initial Chi-sq value + var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix + val DoF = Npnt - Npar // statistical degrees of freedom var weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(inputData.weight)).as2D() if (inputData.nargin < 5) { @@ -165,16 +165,15 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI } var maxIterations = inputData.maxIterations - var epsilon1 = inputData.epsilons[0] // convergence tolerance for gradient - var epsilon2 = inputData.epsilons[1] // convergence tolerance for parameters - var epsilon3 = inputData.epsilons[2] // convergence tolerance for Chi-square - var epsilon4 = inputData.epsilons[3] // determines acceptance of a L-M step - var lambda0 = inputData.lambdas[0] // initial value of damping paramter, lambda - var lambdaUpFac = inputData.lambdas[1] // factor for increasing lambda - var lambdaDnFac = inputData.lambdas[2] // factor for decreasing lambda - var updateType = inputData.updateType // 1: Levenberg-Marquardt lambda update - // 2: Quadratic update - // 3: Nielsen's lambda update equations + var epsilon1 = inputData.epsilons[0] + var epsilon2 = inputData.epsilons[1] + var epsilon3 = inputData.epsilons[2] + var epsilon4 = inputData.epsilons[3] + var lambda0 = inputData.lambdas[0] + var lambdaUpFac = inputData.lambdas[1] + var lambdaDnFac = inputData.lambdas[2] + var updateType = inputData.updateType + if (inputData.nargin < 9) { maxIterations = 10 * Npar epsilon1 = 1e-3 @@ -194,7 +193,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() } - var stop = false // termination flag + var stop = false // termination flag if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() @@ -218,39 +217,35 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI var lambda = 1.0 var nu = 1 - when (updateType) { - 1 -> lambda = lambda0 // Marquardt: init'l lambda - else -> { // Quadratic and Nielsen - lambda = lambda0 * (makeColumnFromDiagonal(JtWJ)).max()!! - nu = 2 - } + + if (updateType == 1) { + lambda = lambda0 // Marquardt: init'l lambda + } + else { + lambda = lambda0 * (makeColumnFromDiagonal(JtWJ)).max() + nu = 2 } X2Old = X2 // previous value of X2 var h: DoubleTensor - while (!stop && settings.iteration <= maxIterations) { //--- Start Main Loop + while (!stop && settings.iteration <= maxIterations) { settings.iteration += 1 // incremental change in parameters - h = when (updateType) { - 1 -> { // Marquardt - val solve = - solve(JtWJ.plus(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda)).as2D(), JtWdy) - solve.asDoubleTensor() - } - - else -> { // Quadratic and Nielsen - val solve = solve(JtWJ.plus(lmEye(Npar).div(1 / lambda)).as2D(), JtWdy) - solve.asDoubleTensor() - } + h = if (updateType == 1) { // Marquardt + val solve = solve(JtWJ.plus(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda)).as2D(), JtWdy) + solve.asDoubleTensor() + } else { // Quadratic and Nielsen + val solve = solve(JtWJ.plus(lmEye(Npar).div(1 / lambda)).as2D(), JtWdy) + solve.asDoubleTensor() } var pTry = (p + h).as2D() // update the [idx] elements - pTry = smallestElementComparison(largestElementComparison(minParameters, pTry.as2D()), maxParameters) // apply constraints + pTry = smallestElementComparison(largestElementComparison(minParameters, pTry.as2D()), maxParameters) // apply constraints - var deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try + var deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try for (i in 0 until deltaY.shape.component1()) { // floating point error; break for (j in 0 until deltaY.shape.component2()) { @@ -264,21 +259,20 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI settings.funcCalls += 1 val tmp = deltaY.times(weight) - var X2Try = deltaY.as2D().transpose().dot(tmp) // Chi-squared error criteria + var X2Try = deltaY.as2D().transpose().dot(tmp) // Chi-squared error criteria val alpha = 1.0 if (updateType == 2) { // Quadratic // One step of quadratic line update in the h direction for minimum X2 - val alpha = JtWdy.transpose().dot(h) / ((X2Try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h))) h = h.dot(alpha) pTry = p.plus(h).as2D() // update only [idx] elements pTry = smallestElementComparison(largestElementComparison(minParameters, pTry), maxParameters) // apply constraints - deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try + deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try settings.funcCalls += 1 - X2Try = deltaY.as2D().transpose().dot(deltaY.times(weight)) // Chi-squared error criteria + X2Try = deltaY.as2D().transpose().dot(deltaY.times(weight)) // Chi-squared error criteria } val rho = when (updateType) { // Nielsen @@ -287,7 +281,6 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI .dot(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] } - else -> { val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] @@ -303,7 +296,6 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, dX2.toInt(), J, p, inputData.realValues, weight, dp, settings) // decrease lambda ==> Gauss-Newton method - JtWJ = lmMatxAns[0] JtWdy = lmMatxAns[1] X2 = lmMatxAns[2][0, 0] @@ -519,7 +511,7 @@ private fun lmMatx(func: (MutableStructure2D, MutableStructure2D yDat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> { // default: dp = 0.001 - val Npar = length(p) // number of parameters + val Npar = length(p) // number of parameters val yHat = evaluateFunction(func, t, p, settings.exampleNumber) // evaluate model using parameters 'p' settings.funcCalls += 1 @@ -558,8 +550,8 @@ private fun lmFdJ(func: (MutableStructure2D, MutableStructure2D, dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { // default: dp = 0.001 * ones(1,n) - val m = length(y) // number of data points - val n = length(p) // number of parameters + val m = length(y) // number of data points + val n = length(p) // number of parameters val ps = p.copyToTensor().as2D() val J = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, n))).as2D() // initialize Jacobian to Zero @@ -568,7 +560,7 @@ private fun lmFdJ(func: (MutableStructure2D, MutableStructure2D, for (j in 0 until n) { del[j, 0] = dp[j, 0] * (1 + kotlin.math.abs(p[j, 0])) // parameter perturbation - p[j, 0] = ps[j, 0] + del[j, 0] // perturb parameter p(j) + p[j, 0] = ps[j, 0] + del[j, 0] // perturb parameter p(j) val epsilon = 0.0000001 if (kotlin.math.abs(del[j, 0]) > epsilon) { From f91b018d4f2a330697bc00dd9dd94f71a150dcb7 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 07:24:47 +0300 Subject: [PATCH 24/34] add assertEquals to middle and difficult test --- .../kmath/tensors/core/TestLmAlgorithm.kt | 144 +++++++++++------- 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 4e1df3b08..4b031eb11 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -22,63 +22,63 @@ class TestLmAlgorithm { companion object { fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() - var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) + var yHat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) if (exampleNumber == 1) { - y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( + yHat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) ) } else if (exampleNumber == 2) { val mt = t.max() - y_hat = (t.times(1.0 / mt)).times(p[0, 0]) + + yHat = (t.times(1.0 / mt)).times(p[0, 0]) + (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + (t.times(1.0 / mt)).pow(4).times(p[3, 0]) } else if (exampleNumber == 3) { - y_hat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) + yHat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) } - return y_hat.as2D() + return yHat.as2D() } fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() - var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + var yHat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) val mt = t.max() for(i in 0 until p.shape.component1()){ - y_hat += (t.times(1.0 / mt)).times(p[i, 0]) + yHat += (t.times(1.0 / mt)).times(p[i, 0]) } for(i in 0 until 5){ - y_hat = funcEasyForLm(y_hat.as2D(), p, exampleNumber).asDoubleTensor() + yHat = funcEasyForLm(yHat.as2D(), p, exampleNumber).asDoubleTensor() } - return y_hat.as2D() + return yHat.as2D() } fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { val m = t.shape.component1() - var y_hat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + var yHat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) val mt = t.max() for(i in 0 until p.shape.component1()){ - y_hat = y_hat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) + yHat = yHat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) } for(i in 0 until 4){ - y_hat = funcEasyForLm((y_hat.as2D() + t).as2D(), p, exampleNumber).asDoubleTensor() + yHat = funcEasyForLm((yHat.as2D() + t).as2D(), p, exampleNumber).asDoubleTensor() } - return y_hat.as2D() + return yHat.as2D() } } @Test fun testLMEasy() = DoubleTensorAlgebra { - val lm_matx_y_dat = doubleArrayOf( + val lmMatxYDat = doubleArrayOf( 19.6594, 18.6096, 17.6792, 17.2747, 16.3065, 17.1458, 16.0467, 16.7023, 15.7809, 15.9807, 14.7620, 15.1128, 16.0973, 15.1934, 15.8636, 15.4763, 15.6860, 15.1895, 15.3495, 16.6054, 16.2247, 15.9854, 16.1421, 17.0960, 16.7769, 17.1997, 17.2767, 17.5882, 17.5378, 16.7894, @@ -91,7 +91,7 @@ class TestLmAlgorithm { 14.7665, 13.3718, 15.0587, 13.8320, 14.7873, 13.6824, 14.2579, 14.2154, 13.5818, 13.8157 ) - var example_number = 1 + var exampleNumber = 1 val p_init = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(5.0, 2.0, 0.2, 10.0) ).as2D() @@ -101,8 +101,8 @@ class TestLmAlgorithm { t[i, 0] = t[i, 0] * (i + 1) } - val y_dat = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(100, 1)), lm_matx_y_dat + val yDat = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(100, 1)), lmMatxYDat ).as2D() val weight = 4.0 @@ -111,20 +111,16 @@ class TestLmAlgorithm { ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() - val p_min = BroadcastDoubleTensorAlgebra.fromArray( + val pMin = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(-50.0, -20.0, -2.0, -100.0) ).as2D() - val p_max = BroadcastDoubleTensorAlgebra.fromArray( + val pMax = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(50.0, 20.0, 2.0, 100.0) ).as2D() - val opts = doubleArrayOf(3.0, 100.0, 1e-3, 1e-3, 1e-1, 1e-1, 1e-2, 11.0, 9.0, 1.0) - - val inputData = LMInput(::funcEasyForLm, p_init, t, y_dat, weight, dp, p_min, p_max, opts[1].toInt(), - doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), - doubleArrayOf(opts[6], opts[7], opts[8]), - opts[9].toInt(), 10, example_number) + val inputData = LMInput(::funcEasyForLm, p_init, t, yDat, weight, dp, pMin, pMax, 100, + doubleArrayOf(1e-3, 1e-3, 1e-1, 1e-1), doubleArrayOf(1e-2, 11.0, 9.0), 1, 10, exampleNumber) val result = levenbergMarquardt(inputData) assertEquals(13, result.iterations) @@ -149,46 +145,46 @@ class TestLmAlgorithm { @Test fun TestLMMiddle() = DoubleTensorAlgebra { val NData = 100 - var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + val tExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() for (i in 0 until NData) { - t_example[i, 0] = t_example[i, 0] * (i + 1) + tExample[i, 0] = tExample[i, 0] * (i + 1) } val Nparams = 20 - var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + val pExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { - p_example[i, 0] = p_example[i, 0] + i - 25 + pExample[i, 0] = pExample[i, 0] + i - 25 } val exampleNumber = 1 - var y_hat = funcMiddleForLm(t_example, p_example, exampleNumber) + val yHat = funcMiddleForLm(tExample, pExample, exampleNumber) - var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + val pInit = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { - p_init[i, 0] = (p_example[i, 0] + 0.9) + pInit[i, 0] = (pExample[i, 0] + 0.9) } - var t = t_example - val y_dat = y_hat + val t = tExample + val yDat = yHat val weight = 1.0 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() - var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - p_min = p_min.div(1.0 / -50.0) - val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - p_min = p_min.div(1.0 / 50.0) + var pMin = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + pMin = pMin.div(1.0 / -50.0) + val pMax = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + pMin = pMin.div(1.0 / 50.0) val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) val inputData = LMInput(::funcMiddleForLm, - p_init.as2D(), + pInit.as2D(), t, - y_dat, + yDat, weight, dp, - p_min.as2D(), - p_max.as2D(), + pMin.as2D(), + pMax.as2D(), opts[1].toInt(), doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), doubleArrayOf(opts[6], opts[7], opts[8]), @@ -197,51 +193,67 @@ class TestLmAlgorithm { 1) val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) + + assertEquals(46, result.iterations) + assertEquals(113, result.funcCalls) + assertEquals(0.000005977, (result.resultChiSq * 1e9).roundToLong() / 1e9) + assertEquals(1.0 * 1e-7, (result.resultLambda * 1e13).roundToLong() / 1e13) + assertEquals(result.typeOfConvergence, TypeOfConvergence.InReducedChiSquare) + val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(Nparams, 1)), doubleArrayOf( -23.9717, -18.6686, -21.7971, + -20.9681, -22.086, -20.5859, -19.0384, -17.4957, -15.9991, -14.576, -13.2441, - + 12.0201, -10.9256, -9.9878, -9.2309, -8.6589, -8.2365, -7.8783, -7.4598, -6.8511)).as2D() + result.resultParameters = result.resultParameters.map { x -> (x * 1e4).roundToLong() / 1e4}.as2D() + val receivedParameters = zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + receivedParameters[i, 0] = result.resultParameters[i, 0] + assertEquals(expectedParameters[i, 0], result.resultParameters[i, 0]) + } } @Test fun TestLMDifficult() = DoubleTensorAlgebra { val NData = 200 - var t_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() + var tExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() for (i in 0 until NData) { - t_example[i, 0] = t_example[i, 0] * (i + 1) - 104 + tExample[i, 0] = tExample[i, 0] * (i + 1) - 104 } val Nparams = 15 - var p_example = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() + var pExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { - p_example[i, 0] = p_example[i, 0] + i - 25 + pExample[i, 0] = pExample[i, 0] + i - 25 } val exampleNumber = 1 - var y_hat = funcDifficultForLm(t_example, p_example, exampleNumber) + var yHat = funcDifficultForLm(tExample, pExample, exampleNumber) - var p_init = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + var pInit = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() for (i in 0 until Nparams) { - p_init[i, 0] = (p_example[i, 0] + 0.9) + pInit[i, 0] = (pExample[i, 0] + 0.9) } - var t = t_example - val y_dat = y_hat + var t = tExample + val yDat = yHat val weight = 1.0 / Nparams * 1.0 - 0.085 val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() - var p_min = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - p_min = p_min.div(1.0 / -50.0) - val p_max = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - p_min = p_min.div(1.0 / 50.0) + var pMin = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + pMin = pMin.div(1.0 / -50.0) + val pMax = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) + pMin = pMin.div(1.0 / 50.0) val opts = doubleArrayOf(3.0, 7000.0, 1e-2, 1e-3, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) val inputData = LMInput(::funcDifficultForLm, - p_init.as2D(), + pInit.as2D(), t, - y_dat, + yDat, weight, dp, - p_min.as2D(), - p_max.as2D(), + pMin.as2D(), + pMax.as2D(), opts[1].toInt(), doubleArrayOf(opts[2], opts[3], opts[4], opts[5]), doubleArrayOf(opts[6], opts[7], opts[8]), @@ -250,5 +262,19 @@ class TestLmAlgorithm { 1) val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) + + assertEquals(2375, result.iterations) + assertEquals(4858, result.funcCalls) + assertEquals(5.14347, (result.resultLambda * 1e5).roundToLong() / 1e5) + assertEquals(result.typeOfConvergence, TypeOfConvergence.InParameters) + val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( + ShapeND(intArrayOf(Nparams, 1)), doubleArrayOf(-23.6412, -16.7402, -21.5705, -21.0464, + -17.2852, -17.2959, -17.298, 0.9999, -17.2885, -17.3008, -17.2941, -17.2923, -17.2976, -17.3028, -17.2891)).as2D() + result.resultParameters = result.resultParameters.map { x -> (x * 1e4).roundToLong() / 1e4}.as2D() + val receivedParameters = zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() + for (i in 0 until Nparams) { + receivedParameters[i, 0] = result.resultParameters[i, 0] + assertEquals(expectedParameters[i, 0], result.resultParameters[i, 0]) + } } } \ No newline at end of file From ef4335bc410fd7fdd82e88bea960ede05a41e2be Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Wed, 7 Jun 2023 15:24:01 +0300 Subject: [PATCH 25/34] use function types for input func --- .../kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt | 2 +- .../kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index fe96b2fe9..f052279ae 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -16,7 +16,7 @@ import space.kscience.kmath.tensors.core.levenbergMarquardt import kotlin.random.Random import kotlin.reflect.KFunction3 -fun streamLm(lm_func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, +fun streamLm(lm_func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), startData: StartDataLm, launchFrequencyInMs: Long, numberOfLaunches: Int): Flow> = flow{ var example_number = startData.example_number diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index d9c282fb6..3cb485d7d 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -88,7 +88,7 @@ public data class LMResultInfo ( * exampleNumber: a parameter for a function with which you can choose its behavior. */ public data class LMInput ( - var func: KFunction3, MutableStructure2D, Int, MutableStructure2D>, + var func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), var startParameters: MutableStructure2D, var independentVariables: MutableStructure2D, var realValues: MutableStructure2D, From 009f93adbb5e88f9ec7c2c0c90a5f6856ae27d1f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 8 Jun 2023 09:28:26 +0300 Subject: [PATCH 26/34] Add rotation coversion test for XYZ --- .../kotlin/space/kscience/kmath/geometry/RotationTest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt index 6177382a2..c7c202a70 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt @@ -44,7 +44,9 @@ class RotationTest { @Test fun fromEuler() { val q = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.ZXY) - assertBufferEquals(DoubleBuffer(0.9818562, 0.0342708, 0.1060205, 0.1534393), q) + + val q1 = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.XYZ) + assertBufferEquals(DoubleBuffer(0.9818562, 0.0640713, 0.0911575, 0.1534393), q1) } } \ No newline at end of file From 5f2690309b16d27f1fbf10760ff1a8e609149583 Mon Sep 17 00:00:00 2001 From: Margarita Lashina Date: Tue, 13 Jun 2023 03:06:55 +0300 Subject: [PATCH 27/34] fix mistake in streaming version --- .../kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt index f052279ae..b2818ef2a 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/tensors/LevenbergMarquardt/StreamingLm/streamLm.kt @@ -51,8 +51,8 @@ fun streamLm(lm_func: (MutableStructure2D, MutableStructure2D, I val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) emit(result.resultParameters) delay(launchFrequencyInMs) - p_init = result.resultParameters - y_dat = generateNewYDat(y_dat, 0.1) + inputData.realValues = generateNewYDat(y_dat, 0.1) + inputData.startParameters = result.resultParameters if (!isEndless) steps -= 1 } } From e00c2a4e2b03c1548933aeb035d1f0006c7999ae Mon Sep 17 00:00:00 2001 From: Gleb Minaev <43728100+lounres@users.noreply.github.com> Date: Fri, 16 Jun 2023 16:00:48 +0300 Subject: [PATCH 28/34] Fix version of `matheclipse-core`. --- kmath-symja/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmath-symja/build.gradle.kts b/kmath-symja/build.gradle.kts index 8741de2ae..a996f3bec 100644 --- a/kmath-symja/build.gradle.kts +++ b/kmath-symja/build.gradle.kts @@ -10,7 +10,7 @@ plugins { description = "Symja integration module" dependencies { - api("org.matheclipse:matheclipse-core:2.0.0-SNAPSHOT") { + api("org.matheclipse:matheclipse-core:2.0.0") { // Incorrect transitive dependencies exclude("org.apfloat", "apfloat") exclude("org.hipparchus", "hipparchus-clustering") From 1e2a8a40e5334a6dc2a8d5900ef04b7597666d36 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 28 Jul 2023 20:39:05 +0300 Subject: [PATCH 29/34] levenbergMarquardt cleanup --- build.gradle.kts | 2 +- .../core/LevenbergMarquardtAlgorithm.kt | 314 +++++++++++------- .../kmath/tensors/core/TestLmAlgorithm.kt | 224 +++++++------ 3 files changed, 313 insertions(+), 227 deletions(-) rename kmath-tensors/src/{commonTest => jvmTest}/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt (52%) diff --git a/build.gradle.kts b/build.gradle.kts index aed79909c..7dbe87445 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ allprojects { } group = "space.kscience" - version = "0.3.1" + version = "0.3.2-dev-1" } subprojects { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index 3cb485d7d..72752f855 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -5,18 +5,13 @@ package space.kscience.kmath.tensors.core +import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.linear.transpose import space.kscience.kmath.nd.* -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.div -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.dot -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.minus -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.times -import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.transposed -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus +import kotlin.math.abs import kotlin.math.max import kotlin.math.min import kotlin.math.pow -import kotlin.reflect.KFunction3 /** * Type of convergence achieved as a result of executing the Levenberg-Marquardt algorithm. @@ -50,8 +45,8 @@ public enum class TypeOfConvergence { * resultParameters: final parameters. * typeOfConvergence: type of convergence. */ -public data class LMResultInfo ( - var iterations:Int, +public data class LMResultInfo( + var iterations: Int, var funcCalls: Int, var resultChiSq: Double, var resultLambda: Double, @@ -87,7 +82,7 @@ public data class LMResultInfo ( * <8 - use maxParameters by default, <9 - use updateType by default). * exampleNumber: a parameter for a function with which you can choose its behavior. */ -public data class LMInput ( +public data class LMInput( var func: (MutableStructure2D, MutableStructure2D, Int) -> (MutableStructure2D), var startParameters: MutableStructure2D, var independentVariables: MutableStructure2D, @@ -101,7 +96,7 @@ public data class LMInput ( var lambdas: DoubleArray, var updateType: Int, var nargin: Int, - var exampleNumber: Int + var exampleNumber: Int, ) @@ -120,8 +115,10 @@ public data class LMInput ( * @return the 'output'. */ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultInfo { - val resultInfo = LMResultInfo(0, 0, 0.0, - 0.0, inputData.startParameters, TypeOfConvergence.NoConvergence) + val resultInfo = LMResultInfo( + 0, 0, 0.0, + 0.0, inputData.startParameters, TypeOfConvergence.NoConvergence + ) val eps = 2.2204e-16 @@ -131,18 +128,21 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI var p = inputData.startParameters val t = inputData.independentVariables - val Npar = length(p) // number of parameters - val Npnt = length(inputData.realValues) // number of data points - var pOld = zeros(ShapeND(intArrayOf(Npar, 1))).as2D() // previous set of parameters - var yOld = zeros(ShapeND(intArrayOf(Npnt, 1))).as2D() // previous model, y_old = y_hat(t;p_old) - var X2 = 1e-3 / eps // a really big initial Chi-sq value - var X2Old = 1e-3 / eps // a really big initial Chi-sq value - var J = zeros(ShapeND(intArrayOf(Npnt, Npar))).as2D() // Jacobian matrix - val DoF = Npnt - Npar // statistical degrees of freedom + val nPar = length(p) // number of parameters + val nPoints = length(inputData.realValues) // number of data points + var pOld = zeros(ShapeND(intArrayOf(nPar, 1))).as2D() // previous set of parameters + var yOld = zeros(ShapeND(intArrayOf(nPoints, 1))).as2D() // previous model, y_old = y_hat(t;p_old) + var x2: Double // a really big initial Chi-sq value + var x2Old: Double // a really big initial Chi-sq value + var jacobian = zeros(ShapeND(intArrayOf(nPoints, nPar))).as2D() // Jacobian matrix + val dof = nPoints - nPar // statistical degrees of freedom var weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf(inputData.weight)).as2D() - if (inputData.nargin < 5) { - weight = fromArray(ShapeND(intArrayOf(1, 1)), doubleArrayOf((inputData.realValues.transpose().dot(inputData.realValues)).as1D()[0])).as2D() + if (inputData.nargin < 5) { + weight = fromArray( + ShapeND(intArrayOf(1, 1)), + doubleArrayOf((inputData.realValues.transpose().dot(inputData.realValues)).as1D()[0]) + ).as2D() } var dp = inputData.pDelta @@ -169,13 +169,13 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI var epsilon2 = inputData.epsilons[1] var epsilon3 = inputData.epsilons[2] var epsilon4 = inputData.epsilons[3] - var lambda0 = inputData.lambdas[0] + var lambda0 = inputData.lambdas[0] var lambdaUpFac = inputData.lambdas[1] var lambdaDnFac = inputData.lambdas[2] var updateType = inputData.updateType if (inputData.nargin < 9) { - maxIterations = 10 * Npar + maxIterations = 10 * nPar epsilon1 = 1e-3 epsilon2 = 1e-3 epsilon3 = 1e-1 @@ -190,28 +190,27 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI maxParameters = makeColumn(maxParameters) if (length(makeColumn(dp)) == 1) { - dp = ones(ShapeND(intArrayOf(Npar, 1))).div(1 / dp[0, 0]).as2D() + dp = ones(ShapeND(intArrayOf(nPar, 1))).div(1 / dp[0, 0]).as2D() } var stop = false // termination flag if (weight.shape.component1() == 1 || variance(weight) == 0.0) { // identical weights vector - weight = ones(ShapeND(intArrayOf(Npnt, 1))).div(1 / kotlin.math.abs(weight[0, 0])).as2D() - } - else { + weight = ones(ShapeND(intArrayOf(nPoints, 1))).div(1 / abs(weight[0, 0])).as2D() + } else { weight = makeColumn(weight) weight.abs() } // initialize Jacobian with finite difference calculation - var lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, 1, J, p, inputData.realValues, weight, dp, settings) - var JtWJ = lmMatxAns[0] - var JtWdy = lmMatxAns[1] - X2 = lmMatxAns[2][0, 0] + var lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, 1, jacobian, p, inputData.realValues, weight, dp, settings) + var jtWJ = lmMatxAns[0] + var jtWdy = lmMatxAns[1] + x2 = lmMatxAns[2][0, 0] var yHat = lmMatxAns[3] - J = lmMatxAns[4] + jacobian = lmMatxAns[4] - if ( abs(JtWdy).max() < epsilon1 ) { + if (abs(jtWdy).max() < epsilon1) { stop = true } @@ -219,14 +218,13 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI var nu = 1 if (updateType == 1) { - lambda = lambda0 // Marquardt: init'l lambda - } - else { - lambda = lambda0 * (makeColumnFromDiagonal(JtWJ)).max() + lambda = lambda0 // Marquardt: init'l lambda + } else { + lambda = lambda0 * (makeColumnFromDiagonal(jtWJ)).max() nu = 2 } - X2Old = X2 // previous value of X2 + x2Old = x2 // previous value of X2 var h: DoubleTensor @@ -235,20 +233,31 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI // incremental change in parameters h = if (updateType == 1) { // Marquardt - val solve = solve(JtWJ.plus(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda)).as2D(), JtWdy) + val solve = + solve((jtWJ + makeMatrixWithDiagonal(makeColumnFromDiagonal(jtWJ)) / (1 / lambda)).as2D(), jtWdy) solve.asDoubleTensor() } else { // Quadratic and Nielsen - val solve = solve(JtWJ.plus(lmEye(Npar).div(1 / lambda)).as2D(), JtWdy) + val solve = solve(jtWJ.plus(lmEye(nPar) * lambda).as2D(), jtWdy) solve.asDoubleTensor() } var pTry = (p + h).as2D() // update the [idx] elements - pTry = smallestElementComparison(largestElementComparison(minParameters, pTry.as2D()), maxParameters) // apply constraints + pTry = smallestElementComparison( + largestElementComparison(minParameters, pTry), + maxParameters + ) // apply constraints - var deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try + var deltaY = inputData.realValues.minus( + evaluateFunction( + inputData.func, + t, + pTry, + inputData.exampleNumber + ) + ) // residual error using p_try - for (i in 0 until deltaY.shape.component1()) { // floating point error; break - for (j in 0 until deltaY.shape.component2()) { + for (i in 0 until deltaY.shape[0]) { // floating point error; break + for (j in 0 until deltaY.shape[1]) { if (deltaY[i, j] == Double.POSITIVE_INFINITY || deltaY[i, j] == Double.NEGATIVE_INFINITY) { stop = true break @@ -258,49 +267,72 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI settings.funcCalls += 1 - val tmp = deltaY.times(weight) - var X2Try = deltaY.as2D().transpose().dot(tmp) // Chi-squared error criteria +// val tmp = deltaY.times(weight) + var X2Try = deltaY.as2D().transpose().dot(deltaY.times(weight)) // Chi-squared error criteria val alpha = 1.0 if (updateType == 2) { // Quadratic // One step of quadratic line update in the h direction for minimum X2 - val alpha = JtWdy.transpose().dot(h) / ((X2Try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h))) - h = h.dot(alpha) - pTry = p.plus(h).as2D() // update only [idx] elements - pTry = smallestElementComparison(largestElementComparison(minParameters, pTry), maxParameters) // apply constraints + val alpha = (jtWdy.transpose() dot h) / ((X2Try - x2) / 2.0 + 2 * (jtWdy.transpose() dot h)) + h = h dot alpha + pTry = (p + h).as2D() // update only [idx] elements + pTry = smallestElementComparison( + largestElementComparison(minParameters, pTry), + maxParameters + ) // apply constraints - deltaY = inputData.realValues.minus(evaluateFunction(inputData.func, t, pTry, inputData.exampleNumber)) // residual error using p_try + deltaY = inputData.realValues.minus( + evaluateFunction( + inputData.func, + t, + pTry, + inputData.exampleNumber + ) + ) // residual error using p_try settings.funcCalls += 1 - X2Try = deltaY.as2D().transpose().dot(deltaY.times(weight)) // Chi-squared error criteria + X2Try = deltaY.as2D().transpose() dot deltaY * weight // Chi-squared error criteria } val rho = when (updateType) { // Nielsen 1 -> { val tmp = h.transposed() - .dot(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) - X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + .dot((makeMatrixWithDiagonal(makeColumnFromDiagonal(jtWJ)) * lambda dot h) + jtWdy) + (x2 - X2Try)[0, 0] / abs(tmp.as2D())[0, 0] } + else -> { - val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) - X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] + val tmp = h.transposed().dot((h * lambda) + jtWdy) + x2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] } } if (rho > epsilon4) { // it IS significantly better - val dX2 = X2.minus(X2Old) - X2Old = X2 + val dX2 = x2.minus(x2Old) + x2Old = x2 pOld = p.copyToTensor().as2D() yOld = yHat.copyToTensor().as2D() p = makeColumn(pTry) // accept p_try - lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, dX2.toInt(), J, p, inputData.realValues, weight, dp, settings) + lmMatxAns = lmMatx( + inputData.func, + t, + pOld, + yOld, + dX2.toInt(), + jacobian, + p, + inputData.realValues, + weight, + dp, + settings + ) // decrease lambda ==> Gauss-Newton method - JtWJ = lmMatxAns[0] - JtWdy = lmMatxAns[1] - X2 = lmMatxAns[2][0, 0] + jtWJ = lmMatxAns[0] + jtWdy = lmMatxAns[1] + x2 = lmMatxAns[2][0, 0] yHat = lmMatxAns[3] - J = lmMatxAns[4] + jacobian = lmMatxAns[4] lambda = when (updateType) { 1 -> { // Levenberg @@ -317,13 +349,14 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI } } } else { // it IS NOT better - X2 = X2Old // do not accept p_try - if (settings.iteration % (2 * Npar) == 0) { // rank-1 update of Jacobian - lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, -1, J, p, inputData.realValues, weight, dp, settings) - JtWJ = lmMatxAns[0] - JtWdy = lmMatxAns[1] + x2 = x2Old // do not accept p_try + if (settings.iteration % (2 * nPar) == 0) { // rank-1 update of Jacobian + lmMatxAns = + lmMatx(inputData.func, t, pOld, yOld, -1, jacobian, p, inputData.realValues, weight, dp, settings) + jtWJ = lmMatxAns[0] + jtWdy = lmMatxAns[1] yHat = lmMatxAns[3] - J = lmMatxAns[4] + jacobian = lmMatxAns[4] } // increase lambda ==> gradient descent method @@ -333,7 +366,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI } 2 -> { // Quadratic - lambda + kotlin.math.abs(((X2Try.as2D()[0, 0] - X2) / 2) / alpha) + lambda + abs(((X2Try.as2D()[0, 0] - x2) / 2) / alpha) } else -> { // Nielsen @@ -343,7 +376,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI } } - val chiSq = X2 / DoF + val chiSq = x2 / dof resultInfo.iterations = settings.iteration resultInfo.funcCalls = settings.funcCalls resultInfo.resultChiSq = chiSq @@ -351,7 +384,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI resultInfo.resultParameters = p - if (abs(JtWdy).max() < epsilon1 && settings.iteration > 2) { + if (abs(jtWdy).max() < epsilon1 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InGradient stop = true } @@ -359,7 +392,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI resultInfo.typeOfConvergence = TypeOfConvergence.InParameters stop = true } - if (X2 / DoF < epsilon3 && settings.iteration > 2) { + if (x2 / dof < epsilon3 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InReducedChiSquare stop = true } @@ -371,10 +404,10 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI return resultInfo } -private data class LMSettings ( - var iteration:Int, +private data class LMSettings( + var iteration: Int, var funcCalls: Int, - var exampleNumber:Int + var exampleNumber: Int, ) /* matrix -> column of all elements */ @@ -390,14 +423,14 @@ private fun makeColumn(tensor: MutableStructure2D): MutableStructure2D) : Int { +private fun length(column: MutableStructure2D): Int { return column.shape.component1() } private fun MutableStructure2D.abs() { - for (i in 0 until this.shape.component1()) { - for (j in 0 until this.shape.component2()) { - this[i, j] = kotlin.math.abs(this[i, j]) + for (i in 0 until this.shape[0]) { + for (j in 0 until this.shape[1]) { + this[i, j] = abs(this[i, j]) } } } @@ -413,7 +446,7 @@ private fun abs(input: MutableStructure2D): MutableStructure2D { ).as2D() for (i in 0 until tensor.shape.component1()) { for (j in 0 until tensor.shape.component2()) { - tensor[i, j] = kotlin.math.abs(input[i, j]) + tensor[i, j] = abs(input[i, j]) } } return tensor @@ -441,21 +474,23 @@ private fun lmEye(size: Int): MutableStructure2D { return makeMatrixWithDiagonal(column) } -private fun largestElementComparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { +private fun largestElementComparison( + a: MutableStructure2D, + b: MutableStructure2D, +): MutableStructure2D { val aSizeX = a.shape.component1() val aSizeY = a.shape.component2() val bSizeX = b.shape.component1() val bSizeY = b.shape.component2() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(aSizeX, bSizeX), max(aSizeY, bSizeY)))).as2D() + val tensor = + BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(aSizeX, bSizeX), max(aSizeY, bSizeY)))).as2D() for (i in 0 until tensor.shape.component1()) { for (j in 0 until tensor.shape.component2()) { if (i < aSizeX && i < bSizeX && j < aSizeY && j < bSizeY) { tensor[i, j] = max(a[i, j], b[i, j]) - } - else if (i < aSizeX && j < aSizeY) { + } else if (i < aSizeX && j < aSizeY) { tensor[i, j] = a[i, j] - } - else { + } else { tensor[i, j] = b[i, j] } } @@ -463,21 +498,23 @@ private fun largestElementComparison(a: MutableStructure2D, b: MutableSt return tensor } -private fun smallestElementComparison(a: MutableStructure2D, b: MutableStructure2D): MutableStructure2D { +private fun smallestElementComparison( + a: MutableStructure2D, + b: MutableStructure2D, +): MutableStructure2D { val aSizeX = a.shape.component1() val aSizeY = a.shape.component2() val bSizeX = b.shape.component1() val bSizeY = b.shape.component2() - val tensor = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(aSizeX, bSizeX), max(aSizeY, bSizeY)))).as2D() + val tensor = + BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(max(aSizeX, bSizeX), max(aSizeY, bSizeY)))).as2D() for (i in 0 until tensor.shape.component1()) { for (j in 0 until tensor.shape.component2()) { if (i < aSizeX && i < bSizeX && j < aSizeY && j < bSizeY) { tensor[i, j] = min(a[i, j], b[i, j]) - } - else if (i < aSizeX && j < aSizeY) { + } else if (i < aSizeX && j < aSizeY) { tensor[i, j] = a[i, j] - } - else { + } else { tensor[i, j] = b[i, j] } } @@ -485,10 +522,13 @@ private fun smallestElementComparison(a: MutableStructure2D, b: MutableS return tensor } -private fun getZeroIndices(column: MutableStructure2D, epsilon: Double = 0.000001): MutableStructure2D? { +private fun getZeroIndices( + column: MutableStructure2D, + epsilon: Double = 0.000001, +): MutableStructure2D? { var idx = emptyArray() for (i in 0 until column.shape.component1()) { - if (kotlin.math.abs(column[i, 0]) > epsilon) { + if (abs(column[i, 0]) > epsilon) { idx += (i + 1.0) } } @@ -498,18 +538,27 @@ private fun getZeroIndices(column: MutableStructure2D, epsilon: Double = return null } -private fun evaluateFunction(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int) - : MutableStructure2D -{ +private fun evaluateFunction( + func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int, +) + : MutableStructure2D { return func(t, p, exampleNumber) } -private fun lmMatx(func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, - t: MutableStructure2D, pOld: MutableStructure2D, yOld: MutableStructure2D, - dX2: Int, JInput: MutableStructure2D, p: MutableStructure2D, - yDat: MutableStructure2D, weight: MutableStructure2D, dp:MutableStructure2D, settings:LMSettings) : Array> -{ +private fun lmMatx( + func: (MutableStructure2D, MutableStructure2D, Int) -> MutableStructure2D, + t: MutableStructure2D, + pOld: MutableStructure2D, + yOld: MutableStructure2D, + dX2: Int, + JInput: MutableStructure2D, + p: MutableStructure2D, + yDat: MutableStructure2D, + weight: MutableStructure2D, + dp: MutableStructure2D, + settings: LMSettings, +): Array> = with(DoubleTensorAlgebra) { // default: dp = 0.001 val Npar = length(p) // number of parameters @@ -520,63 +569,70 @@ private fun lmMatx(func: (MutableStructure2D, MutableStructure2D J = if (settings.iteration % (2 * Npar) == 0 || dX2 > 0) { lmFdJ(func, t, p, yHat, dp, settings).as2D() // finite difference - } - else { + } else { lmBroydenJ(pOld, yOld, J, p, yHat).as2D() // rank-1 update } val deltaY = yDat.minus(yHat) - val chiSq = deltaY.transposed().dot( deltaY.times(weight) ).as2D() - val JtWJ = J.transposed().dot ( J.times( weight.dot(BroadcastDoubleTensorAlgebra.ones(ShapeND(intArrayOf(1, Npar)))) ) ).as2D() - val JtWdy = J.transposed().dot( weight.times(deltaY) ).as2D() + val chiSq = deltaY.transposed().dot(deltaY.times(weight)).as2D() + val JtWJ = + (J.transposed() dot J * (weight dot ones(ShapeND(intArrayOf(1, Npar))))).as2D() + val JtWdy = (J.transposed() dot weight * deltaY).as2D() - return arrayOf(JtWJ,JtWdy,chiSq,yHat,J) + return arrayOf(JtWJ, JtWdy, chiSq, yHat, J) } -private fun lmBroydenJ(pOld: MutableStructure2D, yOld: MutableStructure2D, JInput: MutableStructure2D, - p: MutableStructure2D, y: MutableStructure2D): MutableStructure2D { +private fun lmBroydenJ( + pOld: MutableStructure2D, yOld: MutableStructure2D, JInput: MutableStructure2D, + p: MutableStructure2D, y: MutableStructure2D, +): MutableStructure2D = with(DoubleTensorAlgebra) { var J = JInput.copyToTensor() val h = p.minus(pOld) - val increase = y.minus(yOld).minus( J.dot(h) ).dot(h.transposed()).div( (h.transposed().dot(h)).as2D()[0, 0] ) + val increase = ((y - yOld - (J dot h)) dot h.transposed()) / (h.transposed() dot h)[0, 0] J = J.plus(increase) return J.as2D() } -private fun lmFdJ(func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, - t: MutableStructure2D, p: MutableStructure2D, y: MutableStructure2D, - dp: MutableStructure2D, settings: LMSettings): MutableStructure2D { +@OptIn(PerformancePitfall::class) +private fun lmFdJ( + func: (MutableStructure2D, MutableStructure2D, exampleNumber: Int) -> MutableStructure2D, + t: MutableStructure2D, + p: MutableStructure2D, + y: MutableStructure2D, + dp: MutableStructure2D, + settings: LMSettings, +): MutableStructure2D = with(DoubleTensorAlgebra) { // default: dp = 0.001 * ones(1,n) val m = length(y) // number of data points val n = length(p) // number of parameters - val ps = p.copyToTensor().as2D() - val J = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, n))).as2D() // initialize Jacobian to Zero - val del = BroadcastDoubleTensorAlgebra.zeros(ShapeND(intArrayOf(n, 1))).as2D() + val ps = p.copyToTensor() + val J = zero(m, n) // initialize Jacobian to Zero + val del = zero(n, 1) for (j in 0 until n) { - del[j, 0] = dp[j, 0] * (1 + kotlin.math.abs(p[j, 0])) // parameter perturbation + del[j, 0] = dp[j, 0] * (1 + abs(p[j, 0])) // parameter perturbation p[j, 0] = ps[j, 0] + del[j, 0] // perturb parameter p(j) val epsilon = 0.0000001 - if (kotlin.math.abs(del[j, 0]) > epsilon) { + if (abs(del[j, 0]) > epsilon) { val y1 = evaluateFunction(func, t, p, settings.exampleNumber) settings.funcCalls += 1 if (dp[j, 0] < 0) { // backwards difference - for (i in 0 until J.shape.component1()) { - J[i, j] = (y1.as2D().minus(y).as2D())[i, 0] / del[j, 0] + for (i in 0 until J.shape.first()) { + J[i, j] = (y1 - y)[i, 0] / del[j, 0] } - } - else { + } else { // Do tests for it p[j, 0] = ps[j, 0] - del[j, 0] // central difference, additional func call - for (i in 0 until J.shape.component1()) { - J[i, j] = (y1.as2D().minus(evaluateFunction(func, t, p, settings.exampleNumber)).as2D())[i, 0] / (2 * del[j, 0]) + for (i in 0 until J.shape.first()) { + J[i, j] = (y1 - evaluateFunction(func, t, p, settings.exampleNumber))[i, 0] / (2 * del[j, 0]) } settings.funcCalls += 1 } diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt similarity index 52% rename from kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt rename to kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 4b031eb11..4c039190a 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -5,77 +5,83 @@ package space.kscience.kmath.tensors.core -import space.kscience.kmath.nd.MutableStructure2D -import space.kscience.kmath.nd.ShapeND -import space.kscience.kmath.nd.as2D -import space.kscience.kmath.nd.component1 +import space.kscience.kmath.PerformancePitfall +import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.max -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.plus -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.pow -import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.times -import kotlin.math.roundToLong +import space.kscience.kmath.structures.DoubleBuffer import kotlin.test.Test import kotlin.test.assertEquals +@PerformancePitfall class TestLmAlgorithm { companion object { - fun funcEasyForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { + fun funcEasyForLm( + t: MutableStructure2D, + p: MutableStructure2D, + exampleNumber: Int, + ): MutableStructure2D = with(DoubleTensorAlgebra) { val m = t.shape.component1() - var yHat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(m, 1))) + val yHat = when (exampleNumber) { + 1 -> exp((t * (-1.0 / p[1, 0]))) * p[0, 0] + (t * p[2, 0]) * exp((t * (-1.0 / p[3, 0]))) - if (exampleNumber == 1) { - yHat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))).times(p[0, 0]) + t.times(p[2, 0]).times( - DoubleTensorAlgebra.exp((t.times(-1.0 / p[3, 0]))) - ) - } - else if (exampleNumber == 2) { - val mt = t.max() - yHat = (t.times(1.0 / mt)).times(p[0, 0]) + - (t.times(1.0 / mt)).pow(2).times(p[1, 0]) + - (t.times(1.0 / mt)).pow(3).times(p[2, 0]) + - (t.times(1.0 / mt)).pow(4).times(p[3, 0]) - } - else if (exampleNumber == 3) { - yHat = DoubleTensorAlgebra.exp((t.times(-1.0 / p[1, 0]))) - .times(p[0, 0]) + DoubleTensorAlgebra.sin((t.times(1.0 / p[3, 0]))).times(p[2, 0]) + 2 -> { + val mt = t.max() + (t * (1.0 / mt)) * p[0, 0] + + (t * (1.0 / mt)).pow(2) * p[1, 0] + + (t * (1.0 / mt)).pow(3) * p[2, 0] + + (t * (1.0 / mt)).pow(4) * p[3, 0] + } + + 3 -> exp(t * (-1.0 / p[1, 0])) * p[0, 0] + + sin((t * (1.0 / p[3, 0]))) * p[2, 0] + + else -> zeros(ShapeND(intArrayOf(m, 1))) } return yHat.as2D() } - fun funcMiddleForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { + fun funcMiddleForLm( + t: MutableStructure2D, + p: MutableStructure2D, + exampleNumber: Int, + ): MutableStructure2D = with(DoubleTensorAlgebra) { val m = t.shape.component1() - var yHat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + var yHat = zeros(ShapeND(intArrayOf(m, 1))) val mt = t.max() - for(i in 0 until p.shape.component1()){ - yHat += (t.times(1.0 / mt)).times(p[i, 0]) + for (i in 0 until p.shape.component1()) { + yHat.plusAssign(t * (1.0 / mt) * p[i, 0]) } - for(i in 0 until 5){ + for (i in 0 until 5) { yHat = funcEasyForLm(yHat.as2D(), p, exampleNumber).asDoubleTensor() } return yHat.as2D() } - fun funcDifficultForLm(t: MutableStructure2D, p: MutableStructure2D, exampleNumber: Int): MutableStructure2D { + fun funcDifficultForLm( + t: MutableStructure2D, + p: MutableStructure2D, + exampleNumber: Int, + ): MutableStructure2D = with(DoubleTensorAlgebra) { val m = t.shape.component1() - var yHat = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf (m, 1))) + var yHat = zeros(ShapeND(intArrayOf(m, 1))) val mt = t.max() - for(i in 0 until p.shape.component1()){ - yHat = yHat.plus( (t.times(1.0 / mt)).times(p[i, 0]) ) + for (i in 0 until p.shape.component1()) { + yHat = yHat + (t * (1.0 / mt)) * p[i, 0] } - for(i in 0 until 4){ + for (i in 0 until 4) { yHat = funcEasyForLm((yHat.as2D() + t).as2D(), p, exampleNumber).asDoubleTensor() } return yHat.as2D() } } + @Test fun testLMEasy() = DoubleTensorAlgebra { val lmMatxYDat = doubleArrayOf( @@ -91,12 +97,12 @@ class TestLmAlgorithm { 14.7665, 13.3718, 15.0587, 13.8320, 14.7873, 13.6824, 14.2579, 14.2154, 13.5818, 13.8157 ) - var exampleNumber = 1 - val p_init = BroadcastDoubleTensorAlgebra.fromArray( + val exampleNumber = 1 + val pInit = fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(5.0, 2.0, 0.2, 10.0) ).as2D() - var t = ones(ShapeND(intArrayOf(100, 1))).as2D() + val t = ones(ShapeND(intArrayOf(100, 1))).as2D() for (i in 0 until 100) { t[i, 0] = t[i, 0] * (i + 1) } @@ -119,22 +125,26 @@ class TestLmAlgorithm { ShapeND(intArrayOf(4, 1)), doubleArrayOf(50.0, 20.0, 2.0, 100.0) ).as2D() - val inputData = LMInput(::funcEasyForLm, p_init, t, yDat, weight, dp, pMin, pMax, 100, - doubleArrayOf(1e-3, 1e-3, 1e-1, 1e-1), doubleArrayOf(1e-2, 11.0, 9.0), 1, 10, exampleNumber) + val inputData = LMInput( + Companion::funcEasyForLm, pInit, t, yDat, weight, dp, pMin, pMax, 100, + doubleArrayOf(1e-3, 1e-3, 1e-1, 1e-1), doubleArrayOf(1e-2, 11.0, 9.0), 1, 10, exampleNumber + ) val result = levenbergMarquardt(inputData) assertEquals(13, result.iterations) assertEquals(31, result.funcCalls) - assertEquals(0.9131368192633, (result.resultChiSq * 1e13).roundToLong() / 1e13) - assertEquals(3.7790980 * 1e-7, (result.resultLambda * 1e13).roundToLong() / 1e13) + assertEquals(0.9131368192633, result.resultChiSq, 1e-13) + assertEquals(3.7790980 * 1e-7, result.resultLambda, 1e-13) assertEquals(result.typeOfConvergence, TypeOfConvergence.InParameters) val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(4, 1)), doubleArrayOf(20.527230909086, 9.833627103230, 0.997571256572, 50.174445822506) ).as2D() - result.resultParameters = result.resultParameters.map { x -> (x * 1e12).toLong() / 1e12}.as2D() + result.resultParameters = result.resultParameters.map { x -> (x * 1e12).toLong() / 1e12 }.as2D() val receivedParameters = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(4, 1)), doubleArrayOf(result.resultParameters[0, 0], result.resultParameters[1, 0], - result.resultParameters[2, 0], result.resultParameters[3, 0]) + ShapeND(intArrayOf(4, 1)), doubleArrayOf( + result.resultParameters[0, 0], result.resultParameters[1, 0], + result.resultParameters[2, 0], result.resultParameters[3, 0] + ) ).as2D() assertEquals(expectedParameters[0, 0], receivedParameters[0, 0]) assertEquals(expectedParameters[1, 0], receivedParameters[1, 0]) @@ -143,25 +153,25 @@ class TestLmAlgorithm { } @Test - fun TestLMMiddle() = DoubleTensorAlgebra { - val NData = 100 - val tExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() - for (i in 0 until NData) { + fun testLMMiddle() = DoubleTensorAlgebra { + val nData = 100 + val tExample = one(nData, 1).as2D() + for (i in 0 until nData) { tExample[i, 0] = tExample[i, 0] * (i + 1) } - val Nparams = 20 - val pExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() - for (i in 0 until Nparams) { + val nParams = 20 + val pExample = one(nParams, 1).as2D() + for (i in 0 until nParams) { pExample[i, 0] = pExample[i, 0] + i - 25 } val exampleNumber = 1 - val yHat = funcMiddleForLm(tExample, pExample, exampleNumber) + val yHat = funcMiddleForLm(tExample, pExample, exampleNumber) - val pInit = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() - for (i in 0 until Nparams) { + val pInit = zeros(ShapeND(intArrayOf(nParams, 1))).as2D() + for (i in 0 until nParams) { pInit[i, 0] = (pExample[i, 0] + 0.9) } @@ -171,13 +181,14 @@ class TestLmAlgorithm { val dp = BroadcastDoubleTensorAlgebra.fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() - var pMin = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - pMin = pMin.div(1.0 / -50.0) - val pMax = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - pMin = pMin.div(1.0 / 50.0) + var pMin = ones(ShapeND(intArrayOf(nParams, 1))) + pMin = pMin * (-50.0) + val pMax = ones(ShapeND(intArrayOf(nParams, 1))) + pMin = pMin * 50.0 val opts = doubleArrayOf(3.0, 7000.0, 1e-5, 1e-5, 1e-5, 1e-5, 1e-5, 11.0, 9.0, 1.0) - val inputData = LMInput(::funcMiddleForLm, + val inputData = LMInput( + Companion::funcMiddleForLm, pInit.as2D(), t, yDat, @@ -190,63 +201,67 @@ class TestLmAlgorithm { doubleArrayOf(opts[6], opts[7], opts[8]), opts[9].toInt(), 10, - 1) + 1 + ) val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) assertEquals(46, result.iterations) assertEquals(113, result.funcCalls) - assertEquals(0.000005977, (result.resultChiSq * 1e9).roundToLong() / 1e9) - assertEquals(1.0 * 1e-7, (result.resultLambda * 1e13).roundToLong() / 1e13) + assertEquals(0.000005977, result.resultChiSq, 1e-9) + assertEquals(1.0 * 1e-7, result.resultLambda, 1e-13) assertEquals(result.typeOfConvergence, TypeOfConvergence.InReducedChiSquare) - val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(Nparams, 1)), doubleArrayOf( -23.9717, -18.6686, -21.7971, + val expectedParameters = fromArray( + ShapeND(intArrayOf(nParams, 1)), doubleArrayOf( + -23.9717, -18.6686, -21.7971, -20.9681, -22.086, -20.5859, -19.0384, -17.4957, -15.9991, -14.576, -13.2441, - - 12.0201, -10.9256, -9.9878, -9.2309, -8.6589, -8.2365, -7.8783, -7.4598, -6.8511)).as2D() - result.resultParameters = result.resultParameters.map { x -> (x * 1e4).roundToLong() / 1e4}.as2D() - val receivedParameters = zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() - for (i in 0 until Nparams) { + 12.0201, -10.9256, -9.9878, -9.2309, -8.6589, -8.2365, -7.8783, -7.4598, -6.8511 + ) + ) + val receivedParameters = zero(nParams, 1) + for (i in 0 until nParams) { receivedParameters[i, 0] = result.resultParameters[i, 0] - assertEquals(expectedParameters[i, 0], result.resultParameters[i, 0]) + assertEquals(expectedParameters[i, 0], result.resultParameters[i, 0], 1e-2) } } @Test fun TestLMDifficult() = DoubleTensorAlgebra { - val NData = 200 - var tExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(NData, 1))).as2D() - for (i in 0 until NData) { + val nData = 200 + val tExample = ones(ShapeND(intArrayOf(nData, 1))).as2D() + for (i in 0 until nData) { tExample[i, 0] = tExample[i, 0] * (i + 1) - 104 } - val Nparams = 15 - var pExample = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))).as2D() - for (i in 0 until Nparams) { + val nParams = 15 + val pExample = ones(ShapeND(intArrayOf(nParams, 1))).as2D() + for (i in 0 until nParams) { pExample[i, 0] = pExample[i, 0] + i - 25 } val exampleNumber = 1 - var yHat = funcDifficultForLm(tExample, pExample, exampleNumber) + val yHat = funcDifficultForLm(tExample, pExample, exampleNumber) - var pInit = DoubleTensorAlgebra.zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() - for (i in 0 until Nparams) { + val pInit = zeros(ShapeND(intArrayOf(nParams, 1))).as2D() + for (i in 0 until nParams) { pInit[i, 0] = (pExample[i, 0] + 0.9) } - var t = tExample + val t = tExample val yDat = yHat - val weight = 1.0 / Nparams * 1.0 - 0.085 - val dp = BroadcastDoubleTensorAlgebra.fromArray( + val weight = 1.0 / nParams * 1.0 - 0.085 + val dp = fromArray( ShapeND(intArrayOf(1, 1)), DoubleArray(1) { -0.01 } ).as2D() - var pMin = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - pMin = pMin.div(1.0 / -50.0) - val pMax = DoubleTensorAlgebra.ones(ShapeND(intArrayOf(Nparams, 1))) - pMin = pMin.div(1.0 / 50.0) + var pMin = ones(ShapeND(intArrayOf(nParams, 1))) + pMin = pMin * (-50.0) + val pMax = ones(ShapeND(intArrayOf(nParams, 1))) + pMin = pMin * (50.0) val opts = doubleArrayOf(3.0, 7000.0, 1e-2, 1e-3, 1e-2, 1e-2, 1e-2, 11.0, 9.0, 1.0) - val inputData = LMInput(::funcDifficultForLm, + val inputData = LMInput( + Companion::funcDifficultForLm, pInit.as2D(), t, yDat, @@ -259,22 +274,37 @@ class TestLmAlgorithm { doubleArrayOf(opts[6], opts[7], opts[8]), opts[9].toInt(), 10, - 1) + 1 + ) val result = DoubleTensorAlgebra.levenbergMarquardt(inputData) assertEquals(2375, result.iterations) assertEquals(4858, result.funcCalls) - assertEquals(5.14347, (result.resultLambda * 1e5).roundToLong() / 1e5) + assertEquals(5.14347, result.resultLambda, 1e-5) assertEquals(result.typeOfConvergence, TypeOfConvergence.InParameters) - val expectedParameters = BroadcastDoubleTensorAlgebra.fromArray( - ShapeND(intArrayOf(Nparams, 1)), doubleArrayOf(-23.6412, -16.7402, -21.5705, -21.0464, - -17.2852, -17.2959, -17.298, 0.9999, -17.2885, -17.3008, -17.2941, -17.2923, -17.2976, -17.3028, -17.2891)).as2D() - result.resultParameters = result.resultParameters.map { x -> (x * 1e4).roundToLong() / 1e4}.as2D() - val receivedParameters = zeros(ShapeND(intArrayOf(Nparams, 1))).as2D() - for (i in 0 until Nparams) { + val expectedParameters = DoubleBuffer( + -23.6412, + -16.7402, + -21.5705, + -21.0464, + -17.2852, + -17.2959, + -17.298, + 0.9999, + -17.2885, + -17.3008, + -17.2941, + -17.2923, + -17.2976, + -17.3028, + -17.2891 + ) + + val receivedParameters = zeros(ShapeND(intArrayOf(nParams, 1))).as2D() + for (i in 0 until nParams) { receivedParameters[i, 0] = result.resultParameters[i, 0] - assertEquals(expectedParameters[i, 0], result.resultParameters[i, 0]) + assertEquals(expectedParameters[i], result.resultParameters[i, 0], 1e-2) } } } \ No newline at end of file From 976714475e5a3eb2ff0175b2655ac0d794ecea71 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 28 Jul 2023 20:56:31 +0300 Subject: [PATCH 30/34] levenbergMarquardt cleanup --- .../core/LevenbergMarquardtAlgorithm.kt | 10 ++-- .../kmath/tensors/core/internal/linUtils.kt | 49 ++++++++++--------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt index 72752f855..fc87ad1f3 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/LevenbergMarquardtAlgorithm.kt @@ -120,8 +120,6 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI 0.0, inputData.startParameters, TypeOfConvergence.NoConvergence ) - val eps = 2.2204e-16 - val settings = LMSettings(0, 0, inputData.exampleNumber) settings.funcCalls = 0 // running count of function evaluations @@ -214,7 +212,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI stop = true } - var lambda = 1.0 + var lambda: Double var nu = 1 if (updateType == 1) { @@ -273,8 +271,8 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI val alpha = 1.0 if (updateType == 2) { // Quadratic // One step of quadratic line update in the h direction for minimum X2 - val alpha = (jtWdy.transpose() dot h) / ((X2Try - x2) / 2.0 + 2 * (jtWdy.transpose() dot h)) - h = h dot alpha + val alphaTensor = (jtWdy.transpose() dot h) / ((X2Try - x2) / 2.0 + 2 * (jtWdy.transpose() dot h)) + h = h dot alphaTensor pTry = (p + h).as2D() // update only [idx] elements pTry = smallestElementComparison( largestElementComparison(minParameters, pTry), @@ -388,7 +386,7 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI resultInfo.typeOfConvergence = TypeOfConvergence.InGradient stop = true } - if ((abs(h.as2D()).div(abs(p) + 1e-12)).max() < epsilon2 && settings.iteration > 2) { + if ((abs(h.as2D()) / (abs(p) + 1e-12)).max() < epsilon2 && settings.iteration > 2) { resultInfo.typeOfConvergence = TypeOfConvergence.InParameters stop = true } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index 086c69e49..7a96001c6 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -7,7 +7,10 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.* +import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.asBuffer +import space.kscience.kmath.structures.indices import space.kscience.kmath.tensors.core.* import kotlin.math.abs import kotlin.math.max @@ -329,14 +332,16 @@ private fun SIGN(a: Double, b: Double): Double { return -abs(a) } -internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2D, w: BufferedTensor, - v: MutableStructure2D, iterations: Int, epsilon: Double) { +internal fun MutableStructure2D.svdGolubKahanHelper( + u: MutableStructure2D, w: BufferedTensor, + v: MutableStructure2D, iterations: Int, epsilon: Double, +) { val shape = this.shape val m = shape.component1() val n = shape.component2() - var f = 0.0 + var f: Double val rv1 = DoubleArray(n) - var s = 0.0 + var s: Double var scale = 0.0 var anorm = 0.0 var g = 0.0 @@ -362,10 +367,10 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 s += this[k, i] * this[k, i] } f = this[i, i] - if (f >= 0) { - g = (-1) * abs(sqrt(s)) + g = if (f >= 0) { + -abs(sqrt(s)) } else { - g = abs(sqrt(s)) + abs(sqrt(s)) } val h = f * g - s this[i, i] = f - g @@ -402,10 +407,10 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 s += this[i, k] * this[i, k] } f = this[i, l] - if (f >= 0) { - g = (-1) * abs(sqrt(s)) + g = if (f >= 0) { + -abs(sqrt(s)) } else { - g = abs(sqrt(s)) + abs(sqrt(s)) } val h = f * g - s this[i, l] = f - g @@ -457,7 +462,7 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 for (i in min(n, m) - 1 downTo 0) { l = i + 1 - g = wBuffer[wStart + i] + g = wBuffer[wStart + i] for (j in l until n) { this[i, j] = 0.0 } @@ -484,13 +489,13 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 this[i, i] += 1.0 } - var flag = 0 + var flag: Int var nm = 0 - var c = 0.0 - var h = 0.0 - var y = 0.0 - var z = 0.0 - var x = 0.0 + var c: Double + var h: Double + var y: Double + var z: Double + var x: Double for (k in n - 1 downTo 0) { for (its in 1 until iterations) { flag = 1 @@ -531,7 +536,7 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 } } - z = wBuffer[wStart + k] + z = wBuffer[wStart + k] if (l == k) { if (z < 0.0) { wBuffer[wStart + k] = -z @@ -541,9 +546,9 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 break } - x = wBuffer[wStart + l] + x = wBuffer[wStart + l] nm = k - 1 - y = wBuffer[wStart + nm] + y = wBuffer[wStart + nm] g = rv1[nm] h = rv1[k] f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y) @@ -552,7 +557,7 @@ internal fun MutableStructure2D.svdGolubKahanHelper(u: MutableStructure2 c = 1.0 s = 1.0 - var i = 0 + var i: Int for (j in l until nm + 1) { i = j + 1 g = rv1[i] From 62f1c59d73ab26c2b1a64ea24e2ab04781d367c4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 12 Aug 2023 10:46:43 +0300 Subject: [PATCH 31/34] Fix Median statistics. Update algebra naming. Add integer fields --- .../ExpressionsInterpretersBenchmark.kt | 10 +-- .../kscience/kmath/benchmarks/DotBenchmark.kt | 14 +-- .../ExpressionsInterpretersBenchmark.kt | 10 +-- .../kmath/benchmarks/JafamaBenchmark.kt | 4 +- .../kmath/benchmarks/NDFieldBenchmark.kt | 10 +-- .../benchmarks/TensorAlgebraBenchmark.kt | 4 +- .../kmath/benchmarks/ViktorBenchmark.kt | 4 +- .../kmath/benchmarks/ViktorLogBenchmark.kt | 4 +- .../space/kscience/kmath/ast/expressions.kt | 4 +- .../kscience/kmath/ast/kotlingradSupport.kt | 8 +- .../space/kscience/kmath/ast/symjaSupport.kt | 6 +- .../kscience/kmath/functions/integrate.kt | 4 +- .../kscience/kmath/functions/interpolate.kt | 6 +- .../kmath/functions/interpolateSquare.kt | 6 +- .../kmath/operations/mixedNDOperations.kt | 4 +- .../kscience/kmath/structures/ComplexND.kt | 4 +- .../kscience/kmath/structures/NDField.kt | 10 +-- .../kmath/structures/StreamDoubleFieldND.kt | 24 +++--- .../kscience/kmath/structures/buffers.kt | 4 +- .../TestCompilerConsistencyWithInterpreter.kt | 12 +-- .../kmath/ast/TestCompilerOperations.kt | 26 +++--- .../kmath/ast/TestCompilerVariables.kt | 12 +-- .../space/kscience/kmath/ast/TestFolding.kt | 16 ++-- .../space/kscience/kmath/ast/TestParser.kt | 4 +- .../kmath/ast/TestParserPrecedence.kt | 4 +- .../kotlin/space/kscience/kmath/ast/utils.kt | 16 ++-- .../kmath/wasm/internal/WasmBuilder.kt | 4 +- .../kotlin/space/kscience/kmath/wasm/wasm.kt | 16 ++-- .../kotlin/space/kscience/kmath/ast/utils.kt | 20 ++--- .../kscience/kmath/wasm/TestWasmSpecific.kt | 12 +-- .../kotlin/space/kscience/kmath/asm/asm.kt | 24 +++--- .../kmath/asm/internal/PrimitiveAsmBuilder.kt | 6 +- .../kotlin/space/kscience/kmath/ast/utils.kt | 20 ++--- .../kscience/kmath/commons/linear/CMMatrix.kt | 14 +-- .../commons/integration/IntegrationTest.kt | 2 +- .../commons/optimization/OptimizeTest.kt | 4 +- .../kscience/kmath/expressions/DSAlgebra.kt | 2 +- .../FunctionalExpressionAlgebra.kt | 4 +- .../kmath/linear/DoubleLinearSpace.kt | 16 ++-- .../kscience/kmath/linear/LupDecomposition.kt | 4 +- .../space/kscience/kmath/misc/sorting.kt | 9 ++ .../space/kscience/kmath/nd/DoubleFieldND.kt | 22 ++--- .../space/kscience/kmath/nd/IntRingND.kt | 10 +-- .../space/kscience/kmath/nd/ShortRingND.kt | 8 +- .../kmath/operations/BufferAlgebra.kt | 8 +- .../kmath/operations/DoubleBufferOps.kt | 16 ++-- .../kmath/operations/integerFields.kt | 85 +++++++++++++++++++ .../kscience/kmath/operations/numbers.kt | 33 ++++--- .../kscience/kmath/structures/BufferView.kt | 6 ++ .../kscience/kmath/expressions/DSTest.kt | 8 +- .../kmath/expressions/ExpressionFieldTest.kt | 8 +- .../kmath/expressions/InterpretTest.kt | 4 +- .../kmath/expressions/SimpleAutoDiffTest.kt | 20 ++--- .../kscience/kmath/nd/NdOperationsTest.kt | 4 +- .../kmath/operations/DoubleFieldTest.kt | 6 +- .../kscience/kmath/structures/NDFieldTest.kt | 6 +- .../kmath/structures/NumberNDFieldTest.kt | 6 +- .../kscience/kmath/dimensions/Wrappers.kt | 8 +- .../space/kscience/kmath/ejml/_generated.kt | 46 +++++----- .../space/kscience/kmath/real/RealMatrix.kt | 6 +- .../space/kscience/kmath/real/realND.kt | 6 +- .../kmath/integration/SimpsonIntegrator.kt | 4 +- .../kmath/integration/SplineIntegrator.kt | 6 +- .../kmath/interpolation/SplineInterpolator.kt | 4 +- .../kmath/integration/GaussIntegralTest.kt | 6 +- .../kmath/integration/SimpsonIntegralTest.kt | 6 +- .../kmath/integration/SplineIntegralTest.kt | 8 +- .../interpolation/LinearInterpolatorTest.kt | 4 +- .../interpolation/SplineInterpolatorTest.kt | 4 +- .../kscience/kmath/geometry/rotations3D.kt | 6 +- .../space/kscience/kmath/histogram/Counter.kt | 4 +- .../histogram/UniformHistogramGroupND.kt | 4 +- .../kmath/histogram/UniformHistogram1DTest.kt | 12 +-- .../kmath/histogram/TreeHistogramTest.kt | 4 +- .../kmath/kotlingrad/AdaptingTests.kt | 28 +++--- .../kmath/multik/MultikDoubleAlgebra.kt | 6 +- .../kmath/multik/MultikFloatAlgebra.kt | 6 +- .../kscience/kmath/multik/MultikIntAlgebra.kt | 6 +- .../kmath/multik/MultikLongAlgebra.kt | 6 +- .../kmath/multik/MultikShortAlgebra.kt | 6 +- .../kscience/kmath/multik/MultikNDTest.kt | 4 +- .../kscience/kmath/nd4j/Nd4jArrayAlgebra.kt | 36 ++++---- .../kscience/kmath/nd4j/Nd4jTensorAlgebra.kt | 8 +- .../kmath/nd4j/Nd4jArrayAlgebraTest.kt | 12 +-- .../kmath/optimization/QowOptimizer.kt | 4 +- .../kmath/distributions/NormalDistribution.kt | 2 +- .../kmath/series/VarianceRatioTest.kt | 2 +- .../kotlin/space/kscience/kmath/stat/Mean.kt | 36 +++----- .../space/kscience/kmath/stat/Median.kt | 29 +++++-- .../space/kscience/kmath/stat/Statistic.kt | 4 +- .../kmath/stat/TestBasicStatistics.kt | 31 +++++++ .../kscience/kmath/stat/StatisticTest.kt | 8 +- .../tensorflow/DoubleTensorFlowAlgebra.kt | 12 +-- .../kmath/tensorflow/DoubleTensorFlowOps.kt | 8 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 33 +++---- .../kmath/tensors/core/IntTensorAlgebra.kt | 24 +++--- .../kscience/kmath/viktor/ViktorFieldOpsND.kt | 28 +++--- 97 files changed, 632 insertions(+), 482 deletions(-) create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt create mode 100644 kmath-stat/src/commonTest/kotlin/space/kscience/kmath/stat/TestBasicStatistics.kt diff --git a/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt index cb07e489a..a3cfce52f 100644 --- a/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ b/benchmarks/src/jsMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -11,7 +11,7 @@ import kotlinx.benchmark.Scope import kotlinx.benchmark.State import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.* -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.invoke import kotlin.math.sin @@ -84,7 +84,7 @@ class ExpressionsInterpretersBenchmark { private val x by symbol private const val times = 1_000_000 - private val functional = DoubleField.expression { + private val functional = Float64Field.expression { val x = bindSymbol(Symbol.x) x * number(2.0) + 2.0 / x - 16.0 / sin(x) } @@ -93,10 +93,10 @@ class ExpressionsInterpretersBenchmark { x * 2.0 + number(2.0) / x - number(16.0) / sin(x) } - private val mst = node.toExpression(DoubleField) + private val mst = node.toExpression(Float64Field) @OptIn(UnstableKMathAPI::class) - private val wasm = node.wasmCompileToExpression(DoubleField) - private val estree = node.estreeCompileToExpression(DoubleField) + private val wasm = node.wasmCompileToExpression(Float64Field) + private val estree = node.estreeCompileToExpression(Float64Field) private val raw = Expression { args -> val x = args[x]!! diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt index 7cbe83113..5784c32b1 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/DotBenchmark.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.ejml.EjmlLinearSpaceDDRM import space.kscience.kmath.linear.invoke import space.kscience.kmath.linear.linearSpace -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.tensorflow.produceWithTF import space.kscience.kmath.tensors.core.DoubleTensorAlgebra @@ -27,10 +27,10 @@ internal class DotBenchmark { const val dim = 1000 //creating invertible matrix - val matrix1 = DoubleField.linearSpace.buildMatrix(dim, dim) { _, _ -> + val matrix1 = Float64Field.linearSpace.buildMatrix(dim, dim) { _, _ -> random.nextDouble() } - val matrix2 = DoubleField.linearSpace.buildMatrix(dim, dim) { _, _ -> + val matrix2 = Float64Field.linearSpace.buildMatrix(dim, dim) { _, _ -> random.nextDouble() } @@ -45,7 +45,7 @@ internal class DotBenchmark { @Benchmark fun tfDot(blackhole: Blackhole) { blackhole.consume( - DoubleField.produceWithTF { + Float64Field.produceWithTF { matrix1 dot matrix1 } ) @@ -72,7 +72,7 @@ internal class DotBenchmark { } @Benchmark - fun tensorDot(blackhole: Blackhole) = with(DoubleField.tensorAlgebra) { + fun tensorDot(blackhole: Blackhole) = with(Float64Field.tensorAlgebra) { blackhole.consume(matrix1 dot matrix2) } @@ -82,12 +82,12 @@ internal class DotBenchmark { } @Benchmark - fun bufferedDot(blackhole: Blackhole) = with(DoubleField.linearSpace) { + fun bufferedDot(blackhole: Blackhole) = with(Float64Field.linearSpace) { blackhole.consume(matrix1 dot matrix2) } @Benchmark - fun doubleDot(blackhole: Blackhole) = with(DoubleField.linearSpace) { + fun doubleDot(blackhole: Blackhole) = with(Float64Field.linearSpace) { blackhole.consume(matrix1 dot matrix2) } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt index 4df5f372f..def90ba22 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ExpressionsInterpretersBenchmark.kt @@ -12,7 +12,7 @@ import kotlinx.benchmark.State import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.Algebra -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.operations.invoke import kotlin.math.sin @@ -100,7 +100,7 @@ internal class ExpressionsInterpretersBenchmark { private val x by symbol private const val times = 1_000_000 - private val functional = DoubleField.expression { + private val functional = Float64Field.expression { val x = bindSymbol(Symbol.x) x * number(2.0) + 2.0 / x - 16.0 / sin(x) } @@ -109,12 +109,12 @@ internal class ExpressionsInterpretersBenchmark { x * 2.0 + number(2.0) / x - number(16.0) / sin(x) } - private val mst = node.toExpression(DoubleField) + private val mst = node.toExpression(Float64Field) - private val asmPrimitive = node.compileToExpression(DoubleField) + private val asmPrimitive = node.compileToExpression(Float64Field) private val xIdx = asmPrimitive.indexer.indexOf(x) - private val asmGeneric = node.compileToExpression(DoubleField as Algebra) + private val asmGeneric = node.compileToExpression(Float64Field as Algebra) private val raw = Expression { args -> val x = args[x]!! diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt index 041f7e92a..355f54c65 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/JafamaBenchmark.kt @@ -11,7 +11,7 @@ import org.openjdk.jmh.annotations.Scope import org.openjdk.jmh.annotations.State import space.kscience.kmath.jafama.JafamaDoubleField import space.kscience.kmath.jafama.StrictJafamaDoubleField -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -26,7 +26,7 @@ internal class JafamaBenchmark { @Benchmark fun core(blackhole: Blackhole) = invokeBenchmarks(blackhole) { x -> - DoubleField { x * power(x, 4) * exp(x) / cos(x) + sin(x) } + Float64Field { x * power(x, 4) * exp(x) / cos(x) + sin(x) } } @Benchmark diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt index fb8d845e8..ec7f940a0 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/NDFieldBenchmark.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlinx.multik.ndarray.data.DataType import space.kscience.kmath.UnsafeKMathAPI import space.kscience.kmath.nd.* import space.kscience.kmath.nd4j.nd4j -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.tensors.core.DoubleTensor import space.kscience.kmath.tensors.core.one import space.kscience.kmath.tensors.core.tensorAlgebra @@ -86,9 +86,9 @@ internal class NDFieldBenchmark { private const val dim = 1000 private const val n = 100 private val shape = ShapeND(dim, dim) - private val specializedField = DoubleField.ndAlgebra - private val genericField = BufferedFieldOpsND(DoubleField) - private val nd4jField = DoubleField.nd4j - private val viktorField = DoubleField.viktorAlgebra + private val specializedField = Float64Field.ndAlgebra + private val genericField = BufferedFieldOpsND(Float64Field) + private val nd4jField = Float64Field.nd4j + private val viktorField = Float64Field.viktorAlgebra } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/TensorAlgebraBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/TensorAlgebraBenchmark.kt index c4382374a..8bdb101f6 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/TensorAlgebraBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/TensorAlgebraBenchmark.kt @@ -12,7 +12,7 @@ import kotlinx.benchmark.State import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.linear.matrix import space.kscience.kmath.linear.symmetric -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.tensors.core.symEigJacobi import space.kscience.kmath.tensors.core.symEigSvd import space.kscience.kmath.tensors.core.tensorAlgebra @@ -24,7 +24,7 @@ internal class TensorAlgebraBenchmark { private val random = Random(12224) private const val dim = 30 - private val matrix = DoubleField.linearSpace.matrix(dim, dim).symmetric { _, _ -> random.nextDouble() } + private val matrix = Float64Field.linearSpace.matrix(dim, dim).symmetric { _, _ -> random.nextDouble() } } @Benchmark diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt index 90f3cb765..b424491f6 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorBenchmark.kt @@ -14,7 +14,7 @@ import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.one -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.viktor.ViktorFieldND @State(Scope.Benchmark) @@ -52,7 +52,7 @@ internal class ViktorBenchmark { private val shape = ShapeND(dim, dim) // automatically build context most suited for given type. - private val doubleField = DoubleField.ndAlgebra + private val doubleField = Float64Field.ndAlgebra private val viktorField = ViktorFieldND(dim, dim) } } diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt index 4ec4605ed..b6a485821 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/ViktorLogBenchmark.kt @@ -13,7 +13,7 @@ import org.jetbrains.bio.viktor.F64Array import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.one -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.viktor.ViktorFieldND @State(Scope.Benchmark) @@ -52,7 +52,7 @@ internal class ViktorLogBenchmark { private val shape = ShapeND(dim, dim) // automatically build context most suited for given type. - private val doubleField = DoubleField.ndAlgebra + private val doubleField = Float64Field.ndAlgebra private val viktorField = ViktorFieldND(dim, dim) } } diff --git a/examples/src/main/kotlin/space/kscience/kmath/ast/expressions.kt b/examples/src/main/kotlin/space/kscience/kmath/ast/expressions.kt index cacb6683e..784fed63e 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/ast/expressions.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/ast/expressions.kt @@ -8,13 +8,13 @@ package space.kscience.kmath.ast import space.kscience.kmath.asm.compileToExpression import space.kscience.kmath.expressions.MstExtendedField import space.kscience.kmath.expressions.Symbol.Companion.x -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke fun main() { val expr = MstExtendedField { x * 2.0 + number(2.0) / x - number(16.0) + asinh(x) / sin(x) - }.compileToExpression(DoubleField) + }.compileToExpression(Float64Field) val m = DoubleArray(expr.indexer.symbols.size) val xIdx = expr.indexer.indexOf(x) diff --git a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt index b443e639d..f7b169e0b 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/ast/kotlingradSupport.kt @@ -5,12 +5,12 @@ package space.kscience.kmath.ast +import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.toExpression import space.kscience.kmath.kotlingrad.toKotlingradExpression -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field /** * In this example, *x2 − 4 x − 44* function is differentiated with Kotlin∇, and the @@ -19,9 +19,9 @@ import space.kscience.kmath.operations.DoubleField fun main() { val actualDerivative = "x^2-4*x-44" .parseMath() - .toKotlingradExpression(DoubleField) + .toKotlingradExpression(Float64Field) .derivative(x) - val expectedDerivative = "2*x-4".parseMath().toExpression(DoubleField) + val expectedDerivative = "2*x-4".parseMath().toExpression(Float64Field) check(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0)) } diff --git a/examples/src/main/kotlin/space/kscience/kmath/ast/symjaSupport.kt b/examples/src/main/kotlin/space/kscience/kmath/ast/symjaSupport.kt index 92ee1781b..9a4300f82 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/ast/symjaSupport.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/ast/symjaSupport.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.derivative import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.toExpression -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.symja.toSymjaExpression /** @@ -19,9 +19,9 @@ import space.kscience.kmath.symja.toSymjaExpression fun main() { val actualDerivative = "x^2-4*x-44" .parseMath() - .toSymjaExpression(DoubleField) + .toSymjaExpression(Float64Field) .derivative(x) - val expectedDerivative = "2*x-4".parseMath().toExpression(DoubleField) + val expectedDerivative = "2*x-4".parseMath().toExpression(Float64Field) check(actualDerivative(x to 123.0) == expectedDerivative(x to 123.0)) } diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt index e8534d002..493a89387 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/integrate.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.complex.algebra import space.kscience.kmath.integration.gaussIntegrator import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.value -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.math.pow fun main() { @@ -21,7 +21,7 @@ fun main() { val function: Function1D = { x -> 3 * x.pow(2) + 2 * x + 1 } //get the result of the integration - val result = DoubleField.gaussIntegrator.integrate(0.0..10.0, function = function) + val result = Float64Field.gaussIntegrator.integrate(0.0..10.0, function = function) //the value is nullable because in some cases the integration could not succeed println(result.value) diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt index b4ce6ad2d..9bde80a87 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.functions import space.kscience.kmath.interpolation.SplineInterpolator import space.kscience.kmath.interpolation.interpolatePolynomials -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.DoubleBuffer import space.kscience.plotly.Plotly import space.kscience.plotly.UnstablePlotlyAPI @@ -25,10 +25,10 @@ fun main() { } val polynomial: PiecewisePolynomial = SplineInterpolator( - DoubleField, ::DoubleBuffer + Float64Field, ::DoubleBuffer ).interpolatePolynomials(data) - val function = polynomial.asFunction(DoubleField, 0.0) + val function = polynomial.asFunction(Float64Field, 0.0) val cmInterpolate = org.apache.commons.math3.analysis.interpolation.SplineInterpolator().interpolate( data.map { it.first }.toDoubleArray(), diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt index 7bcd96990..e621cda08 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolateSquare.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.functions import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.interpolation.splineInterpolator -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.real.map import space.kscience.kmath.real.step import space.kscience.plotly.Plotly @@ -28,9 +28,9 @@ fun main() { val xs = 0.0..100.0 step 0.5 val ys = xs.map(function) - val polynomial: PiecewisePolynomial = DoubleField.splineInterpolator.interpolatePolynomials(xs, ys) + val polynomial: PiecewisePolynomial = Float64Field.splineInterpolator.interpolatePolynomials(xs, ys) - val polyFunction = polynomial.asFunction(DoubleField, 0.0) + val polyFunction = polynomial.asFunction(Float64Field, 0.0) Plotly.plot { scatter { diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt index 4a5d783e1..6094d4d4a 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt @@ -15,13 +15,13 @@ import space.kscience.kmath.viktor.ViktorStructureND import space.kscience.kmath.viktor.viktorAlgebra fun main() { - val viktorStructure: ViktorStructureND = DoubleField.viktorAlgebra.structureND(ShapeND(2, 2)) { (i, j) -> + val viktorStructure: ViktorStructureND = Float64Field.viktorAlgebra.structureND(ShapeND(2, 2)) { (i, j) -> if (i == j) 2.0 else 0.0 } val cmMatrix: Structure2D = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0) - val res: DoubleBufferND = DoubleField.ndAlgebra { + val res: DoubleBufferND = Float64Field.ndAlgebra { exp(viktorStructure) + 2.0 * cmMatrix } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt index 86d7c0d89..f71f248d9 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/ComplexND.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.as2D import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.structureND -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import kotlin.system.measureTimeMillis @@ -21,7 +21,7 @@ fun main() { val dim = 1000 val n = 1000 - val realField = DoubleField.ndAlgebra(dim, dim) + val realField = Float64Field.ndAlgebra(dim, dim) val complexField: ComplexFieldND = ComplexField.ndAlgebra(dim, dim) val realTime = measureTimeMillis { diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt index ba8f047a8..375d4e182 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/NDField.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.GlobalScope import org.nd4j.linalg.factory.Nd4j import space.kscience.kmath.nd.* import space.kscience.kmath.nd4j.nd4j -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.viktor.ViktorFieldND import kotlin.contracts.InvocationKind @@ -33,15 +33,15 @@ fun main() { // specialized nd-field for Double. It works as generic Double field as well. - val doubleField = DoubleField.ndAlgebra + val doubleField = Float64Field.ndAlgebra //A generic field. It should be used for objects, not primitives. - val genericField = BufferedFieldOpsND(DoubleField) + val genericField = BufferedFieldOpsND(Float64Field) // Nd4j specialized field. - val nd4jField = DoubleField.nd4j + val nd4jField = Float64Field.nd4j //viktor field val viktorField = ViktorFieldND(dim, dim) //parallel processing based on Java Streams - val parallelField = DoubleField.ndStreaming(dim, dim) + val parallelField = Float64Field.ndStreaming(dim, dim) measureAndPrint("Boxing addition") { genericField { diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index 2ce2c21a6..e46e50821 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -7,8 +7,8 @@ package space.kscience.kmath.structures import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.NumbersAddOps import java.util.* import java.util.stream.IntStream @@ -17,12 +17,12 @@ import java.util.stream.IntStream * A demonstration implementation of NDField over Real using Java [java.util.stream.DoubleStream] for parallel * execution. */ -class StreamDoubleFieldND(override val shape: ShapeND) : FieldND, +class StreamDoubleFieldND(override val shape: ShapeND) : FieldND, NumbersAddOps>, ExtendedField> { private val strides = ColumnStrides(shape) - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field override val zero: BufferND by lazy { structureND(shape) { zero } } override val one: BufferND by lazy { structureND(shape) { one } } @@ -43,10 +43,10 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } } - override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): BufferND { + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> val index = strides.index(offset) - DoubleField.initializer(index) + Float64Field.initializer(index) }.toArray() return BufferND(strides, array.asBuffer()) @@ -54,18 +54,18 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND.map( - transform: DoubleField.(Double) -> Double, + transform: Float64Field.(Double) -> Double, ): BufferND { - val array = Arrays.stream(buffer.array).parallel().map { DoubleField.transform(it) }.toArray() + val array = Arrays.stream(buffer.array).parallel().map { Float64Field.transform(it) }.toArray() return BufferND(strides, array.asBuffer()) } @OptIn(PerformancePitfall::class) override fun StructureND.mapIndexed( - transform: DoubleField.(index: IntArray, Double) -> Double, + transform: Float64Field.(index: IntArray, Double) -> Double, ): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> - DoubleField.transform( + Float64Field.transform( strides.index(offset), buffer.array[offset] ) @@ -78,10 +78,10 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND, right: StructureND, - transform: DoubleField.(Double, Double) -> Double, + transform: Float64Field.(Double, Double) -> Double, ): BufferND { val array = IntStream.range(0, strides.linearSize).parallel().mapToDouble { offset -> - DoubleField.transform(left.buffer.array[offset], right.buffer.array[offset]) + Float64Field.transform(left.buffer.array[offset], right.buffer.array[offset]) }.toArray() return BufferND(strides, array.asBuffer()) } @@ -111,4 +111,4 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND): BufferND = arg.map { atanh(it) } } -fun DoubleField.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(ShapeND(shape)) +fun Float64Field.ndStreaming(vararg shape: Int): StreamDoubleFieldND = StreamDoubleFieldND(ShapeND(shape)) diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt index 2ac0dc6a4..8785fa6f2 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/buffers.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.structures -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.buffer import space.kscience.kmath.operations.bufferAlgebra import space.kscience.kmath.operations.withSize @@ -17,7 +17,7 @@ inline fun MutableBuffer.Companion.same( fun main() { - with(DoubleField.bufferAlgebra.withSize(5)) { + with(Float64Field.bufferAlgebra.withSize(5)) { println(number(2.0) + buffer(1, 2, 3, 4, 5)) } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt index 3400db0f8..77f6a191d 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerConsistencyWithInterpreter.kt @@ -9,8 +9,8 @@ import space.kscience.kmath.expressions.MstField import space.kscience.kmath.expressions.MstRing import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.interpret -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals @@ -32,8 +32,8 @@ internal class TestCompilerConsistencyWithInterpreter { } assertEquals( - mst.interpret(IntRing, x to 3), - mst.compile(IntRing, x to 3), + mst.interpret(Int32Ring, x to 3), + mst.compile(Int32Ring, x to 3), ) } @@ -48,8 +48,8 @@ internal class TestCompilerConsistencyWithInterpreter { } assertEquals( - mst.interpret(DoubleField, x to 2.0), - mst.compile(DoubleField, x to 2.0), + mst.interpret(Float64Field, x to 2.0), + mst.compile(Float64Field, x to 2.0), ) } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt index bf56b80a6..30bb7291d 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerOperations.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.ast import space.kscience.kmath.expressions.MstExtendedField import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals @@ -16,73 +16,73 @@ import kotlin.test.assertEquals internal class TestCompilerOperations { @Test fun testUnaryPlus() = runCompilerTest { - val expr = MstExtendedField { +x }.compileToExpression(DoubleField) + val expr = MstExtendedField { +x }.compileToExpression(Float64Field) assertEquals(2.0, expr(x to 2.0)) } @Test fun testUnaryMinus() = runCompilerTest { - val expr = MstExtendedField { -x }.compileToExpression(DoubleField) + val expr = MstExtendedField { -x }.compileToExpression(Float64Field) assertEquals(-2.0, expr(x to 2.0)) } @Test fun testAdd() = runCompilerTest { - val expr = MstExtendedField { x + x }.compileToExpression(DoubleField) + val expr = MstExtendedField { x + x }.compileToExpression(Float64Field) assertEquals(4.0, expr(x to 2.0)) } @Test fun testSine() = runCompilerTest { - val expr = MstExtendedField { sin(x) }.compileToExpression(DoubleField) + val expr = MstExtendedField { sin(x) }.compileToExpression(Float64Field) assertEquals(0.0, expr(x to 0.0)) } @Test fun testCosine() = runCompilerTest { - val expr = MstExtendedField { cos(x) }.compileToExpression(DoubleField) + val expr = MstExtendedField { cos(x) }.compileToExpression(Float64Field) assertEquals(1.0, expr(x to 0.0)) } @Test fun testTangent() = runCompilerTest { - val expr = MstExtendedField { tan(x) }.compileToExpression(DoubleField) + val expr = MstExtendedField { tan(x) }.compileToExpression(Float64Field) assertEquals(0.0, expr(x to 0.0)) } @Test fun testArcSine() = runCompilerTest { - val expr = MstExtendedField { asin(x) }.compileToExpression(DoubleField) + val expr = MstExtendedField { asin(x) }.compileToExpression(Float64Field) assertEquals(0.0, expr(x to 0.0)) } @Test fun testArcCosine() = runCompilerTest { - val expr = MstExtendedField { acos(x) }.compileToExpression(DoubleField) + val expr = MstExtendedField { acos(x) }.compileToExpression(Float64Field) assertEquals(0.0, expr(x to 1.0)) } @Test fun testAreaHyperbolicSine() = runCompilerTest { - val expr = MstExtendedField { asinh(x) }.compileToExpression(DoubleField) + val expr = MstExtendedField { asinh(x) }.compileToExpression(Float64Field) assertEquals(0.0, expr(x to 0.0)) } @Test fun testSubtract() = runCompilerTest { - val expr = MstExtendedField { x - x }.compileToExpression(DoubleField) + val expr = MstExtendedField { x - x }.compileToExpression(Float64Field) assertEquals(0.0, expr(x to 2.0)) } @Test fun testDivide() = runCompilerTest { - val expr = MstExtendedField { x / x }.compileToExpression(DoubleField) + val expr = MstExtendedField { x / x }.compileToExpression(Float64Field) assertEquals(1.0, expr(x to 2.0)) } @Test fun testPower() = runCompilerTest { - val expr = MstExtendedField { x pow 2 }.compileToExpression(DoubleField) + val expr = MstExtendedField { x pow 2 }.compileToExpression(Float64Field) assertEquals(4.0, expr(x to 2.0)) } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.kt index f23d36240..3dbdf063a 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestCompilerVariables.kt @@ -9,8 +9,8 @@ import space.kscience.kmath.expressions.MstRing import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.y import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals @@ -19,25 +19,25 @@ import kotlin.test.assertFailsWith internal class TestCompilerVariables { @Test fun testNoVariables() = runCompilerTest { - val expr = "0".parseMath().compileToExpression(DoubleField) + val expr = "0".parseMath().compileToExpression(Float64Field) assertEquals(0.0, expr(), 0.0001) } @Test fun testOneVariable() = runCompilerTest { - val expr = MstRing { x }.compileToExpression(IntRing) + val expr = MstRing { x }.compileToExpression(Int32Ring) assertEquals(1, expr(x to 1)) } @Test fun testTwoVariables() = runCompilerTest { - val expr = "y+x/y+x".parseMath().compileToExpression(DoubleField) + val expr = "y+x/y+x".parseMath().compileToExpression(Float64Field) assertEquals(8.0, expr(x to 4.0, y to 2.0)) } @Test fun testUndefinedVariableFails() = runCompilerTest { - val expr = MstRing { x }.compileToExpression(IntRing) + val expr = MstRing { x }.compileToExpression(Int32Ring) assertFailsWith { expr() } } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt index 95b804455..d67d965ce 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt @@ -6,8 +6,8 @@ package space.kscience.kmath.ast import space.kscience.kmath.operations.ByteRing -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.pi import kotlin.test.Test import kotlin.test.assertEquals @@ -17,31 +17,31 @@ internal class TestFolding { @Test fun foldUnary() = assertEquals( -1, - ("-(1)".parseMath().evaluateConstants(IntRing) as? TypedMst.Constant ?: fail()).value, + ("-(1)".parseMath().evaluateConstants(Int32Ring) as? TypedMst.Constant ?: fail()).value, ) @Test fun foldDeepUnary() = assertEquals( 1, - ("-(-(1))".parseMath().evaluateConstants(IntRing) as? TypedMst.Constant ?: fail()).value, + ("-(-(1))".parseMath().evaluateConstants(Int32Ring) as? TypedMst.Constant ?: fail()).value, ) @Test fun foldBinary() = assertEquals( 2, - ("1*2".parseMath().evaluateConstants(IntRing) as? TypedMst.Constant ?: fail()).value, + ("1*2".parseMath().evaluateConstants(Int32Ring) as? TypedMst.Constant ?: fail()).value, ) @Test fun foldDeepBinary() = assertEquals( 10, - ("1*2*5".parseMath().evaluateConstants(IntRing) as? TypedMst.Constant ?: fail()).value, + ("1*2*5".parseMath().evaluateConstants(Int32Ring) as? TypedMst.Constant ?: fail()).value, ) @Test fun foldSymbol() = assertEquals( - DoubleField.pi, - ("pi".parseMath().evaluateConstants(DoubleField) as? TypedMst.Constant ?: fail()).value, + Float64Field.pi, + ("pi".parseMath().evaluateConstants(Float64Field) as? TypedMst.Constant ?: fail()).value, ) @Test diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt index d3c203903..784dcece9 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParser.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.complex.Complex import space.kscience.kmath.complex.ComplexField import space.kscience.kmath.expressions.interpret import space.kscience.kmath.operations.Algebra -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.test.Test import kotlin.test.assertEquals @@ -32,7 +32,7 @@ internal class TestParser { @Test fun evaluateMstUnary() { val mst = "sin(0)".parseMath() - val res = mst.interpret(DoubleField) + val res = mst.interpret(Float64Field) assertEquals(0.0, res) } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt index 4b3631663..504ec9bd0 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestParserPrecedence.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.ast import space.kscience.kmath.expressions.interpret -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.test.Test import kotlin.test.assertEquals @@ -36,6 +36,6 @@ internal class TestParserPrecedence { fun test8(): Unit = assertEquals(18.0, "2*2^3+2".parseMath().interpret(f)) private companion object { - private val f = DoubleField + private val f = Float64Field } } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt index fe035c69f..27b2f2598 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -8,17 +8,17 @@ package space.kscience.kmath.ast import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring internal interface CompilerTestContext { - fun MST.compileToExpression(algebra: IntRing): Expression - fun MST.compile(algebra: IntRing, arguments: Map): Int - fun MST.compile(algebra: IntRing, vararg arguments: Pair): Int = compile(algebra, mapOf(*arguments)) - fun MST.compileToExpression(algebra: DoubleField): Expression - fun MST.compile(algebra: DoubleField, arguments: Map): Double + fun MST.compileToExpression(algebra: Int32Ring): Expression + fun MST.compile(algebra: Int32Ring, arguments: Map): Int + fun MST.compile(algebra: Int32Ring, vararg arguments: Pair): Int = compile(algebra, mapOf(*arguments)) + fun MST.compileToExpression(algebra: Float64Field): Expression + fun MST.compile(algebra: Float64Field, arguments: Map): Double - fun MST.compile(algebra: DoubleField, vararg arguments: Pair): Double = + fun MST.compile(algebra: Float64Field, vararg arguments: Pair): Double = compile(algebra, mapOf(*arguments)) } diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt index 1908f0659..4917f6f1b 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/internal/WasmBuilder.kt @@ -86,7 +86,7 @@ internal sealed class WasmBuilder>( @UnstableKMathAPI internal class DoubleWasmBuilder(target: TypedMst) : - WasmBuilder(f64, DoubleField, target) { + WasmBuilder(f64, Float64Field, target) { override val instance by lazy { object : DoubleExpression { override val indexer = SimpleSymbolIndexer(keys) @@ -131,7 +131,7 @@ internal class DoubleWasmBuilder(target: TypedMst) : } @UnstableKMathAPI -internal class IntWasmBuilder(target: TypedMst) : WasmBuilder(i32, IntRing, target) { +internal class IntWasmBuilder(target: TypedMst) : WasmBuilder(i32, Int32Ring, target) { override val instance by lazy { object : IntExpression { override val indexer = SimpleSymbolIndexer(keys) diff --git a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt index acb26f918..f3249adf7 100644 --- a/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt +++ b/kmath-ast/src/jsMain/kotlin/space/kscience/kmath/wasm/wasm.kt @@ -11,8 +11,8 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.ast.TypedMst import space.kscience.kmath.ast.evaluateConstants import space.kscience.kmath.expressions.* -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.wasm.internal.DoubleWasmBuilder import space.kscience.kmath.wasm.internal.IntWasmBuilder @@ -22,7 +22,7 @@ import space.kscience.kmath.wasm.internal.IntWasmBuilder * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compileToExpression(algebra: IntRing): IntExpression { +public fun MST.compileToExpression(algebra: Int32Ring): IntExpression { val typed = evaluateConstants(algebra) return if (typed is TypedMst.Constant) object : IntExpression { @@ -39,7 +39,7 @@ public fun MST.compileToExpression(algebra: IntRing): IntExpression { * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: IntRing, arguments: Map): Int = +public fun MST.compile(algebra: Int32Ring, arguments: Map): Int = compileToExpression(algebra)(arguments) @@ -49,7 +49,7 @@ public fun MST.compile(algebra: IntRing, arguments: Map): Int = * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: IntRing, vararg arguments: Pair): Int = +public fun MST.compile(algebra: Int32Ring, vararg arguments: Pair): Int = compileToExpression(algebra)(*arguments) /** @@ -58,7 +58,7 @@ public fun MST.compile(algebra: IntRing, vararg arguments: Pair): I * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compileToExpression(algebra: DoubleField): Expression { +public fun MST.compileToExpression(algebra: Float64Field): Expression { val typed = evaluateConstants(algebra) return if (typed is TypedMst.Constant) object : DoubleExpression { @@ -76,7 +76,7 @@ public fun MST.compileToExpression(algebra: DoubleField): Expression { * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: DoubleField, arguments: Map): Double = +public fun MST.compile(algebra: Float64Field, arguments: Map): Double = compileToExpression(algebra)(arguments) @@ -86,5 +86,5 @@ public fun MST.compile(algebra: DoubleField, arguments: Map): Do * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: DoubleField, vararg arguments: Pair): Double = +public fun MST.compile(algebra: Float64Field, vararg arguments: Pair): Double = compileToExpression(algebra)(*arguments) diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt index 7c397d5a0..6d826541c 100644 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -11,8 +11,8 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.Symbol -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import kotlin.contracts.InvocationKind import kotlin.contracts.contract import space.kscience.kmath.estree.compile as estreeCompile @@ -21,20 +21,20 @@ import space.kscience.kmath.wasm.compile as wasmCompile import space.kscience.kmath.wasm.compileToExpression as wasmCompileToExpression private object WasmCompilerTestContext : CompilerTestContext { - override fun MST.compileToExpression(algebra: IntRing): Expression = wasmCompileToExpression(algebra) - override fun MST.compile(algebra: IntRing, arguments: Map): Int = wasmCompile(algebra, arguments) - override fun MST.compileToExpression(algebra: DoubleField): Expression = wasmCompileToExpression(algebra) + override fun MST.compileToExpression(algebra: Int32Ring): Expression = wasmCompileToExpression(algebra) + override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = wasmCompile(algebra, arguments) + override fun MST.compileToExpression(algebra: Float64Field): Expression = wasmCompileToExpression(algebra) - override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + override fun MST.compile(algebra: Float64Field, arguments: Map): Double = wasmCompile(algebra, arguments) } private object ESTreeCompilerTestContext : CompilerTestContext { - override fun MST.compileToExpression(algebra: IntRing): Expression = estreeCompileToExpression(algebra) - override fun MST.compile(algebra: IntRing, arguments: Map): Int = estreeCompile(algebra, arguments) - override fun MST.compileToExpression(algebra: DoubleField): Expression = estreeCompileToExpression(algebra) + override fun MST.compileToExpression(algebra: Int32Ring): Expression = estreeCompileToExpression(algebra) + override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = estreeCompile(algebra, arguments) + override fun MST.compileToExpression(algebra: Float64Field): Expression = estreeCompileToExpression(algebra) - override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + override fun MST.compile(algebra: Float64Field, arguments: Map): Double = estreeCompile(algebra, arguments) } diff --git a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt index 132f9f1bd..c7e898713 100644 --- a/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt +++ b/kmath-ast/src/jsTest/kotlin/space/kscience/kmath/wasm/TestWasmSpecific.kt @@ -10,8 +10,8 @@ import space.kscience.kmath.expressions.MstExtendedField import space.kscience.kmath.expressions.MstRing import space.kscience.kmath.expressions.invoke import space.kscience.kmath.expressions.symbol -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals @@ -20,20 +20,20 @@ import kotlin.test.assertEquals internal class TestWasmSpecific { @Test fun int() { - val res = MstRing { number(100000000) + number(10000000) }.compile(IntRing) + val res = MstRing { number(100000000) + number(10000000) }.compile(Int32Ring) assertEquals(110000000, res) } @Test fun real() { - val res = MstExtendedField { number(100000000) + number(2).pow(10) }.compile(DoubleField) + val res = MstExtendedField { number(100000000) + number(2).pow(10) }.compile(Float64Field) assertEquals(100001024.0, res) } @Test fun argsPassing() { val res = MstExtendedField { y + x.pow(10) }.compile( - DoubleField, + Float64Field, x to 2.0, y to 100000000.0, ) @@ -43,7 +43,7 @@ internal class TestWasmSpecific { @Test fun powFunction() { - val expr = MstExtendedField { x.pow(1.0 / 6.0) }.compileToExpression(DoubleField) + val expr = MstExtendedField { x.pow(1.0 / 6.0) }.compileToExpression(Float64Field) assertEquals(0.9730585187140817, expr(x to 0.8488554755054833)) } diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt index 7094d0442..97fe91ee4 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt @@ -13,9 +13,9 @@ import space.kscience.kmath.ast.TypedMst import space.kscience.kmath.ast.evaluateConstants import space.kscience.kmath.expressions.* import space.kscience.kmath.operations.Algebra -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing -import space.kscience.kmath.operations.LongRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.operations.Int64Ring /** * Compiles given MST to an Expression using AST compiler. @@ -91,7 +91,7 @@ public inline fun MST.compile(algebra: Algebra, vararg argu * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compileToExpression(algebra: IntRing): IntExpression { +public fun MST.compileToExpression(algebra: Int32Ring): IntExpression { val typed = evaluateConstants(algebra) return if (typed is TypedMst.Constant) object : IntExpression { @@ -108,7 +108,7 @@ public fun MST.compileToExpression(algebra: IntRing): IntExpression { * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: IntRing, arguments: Map): Int = +public fun MST.compile(algebra: Int32Ring, arguments: Map): Int = compileToExpression(algebra)(arguments) /** @@ -117,7 +117,7 @@ public fun MST.compile(algebra: IntRing, arguments: Map): Int = * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: IntRing, vararg arguments: Pair): Int = +public fun MST.compile(algebra: Int32Ring, vararg arguments: Pair): Int = compileToExpression(algebra)(*arguments) @@ -127,7 +127,7 @@ public fun MST.compile(algebra: IntRing, vararg arguments: Pair): I * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compileToExpression(algebra: LongRing): LongExpression { +public fun MST.compileToExpression(algebra: Int64Ring): LongExpression { val typed = evaluateConstants(algebra) return if (typed is TypedMst.Constant) object : LongExpression { @@ -144,7 +144,7 @@ public fun MST.compileToExpression(algebra: LongRing): LongExpression { * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: LongRing, arguments: Map): Long = +public fun MST.compile(algebra: Int64Ring, arguments: Map): Long = compileToExpression(algebra)(arguments) @@ -154,7 +154,7 @@ public fun MST.compile(algebra: LongRing, arguments: Map): Long = * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: LongRing, vararg arguments: Pair): Long = +public fun MST.compile(algebra: Int64Ring, vararg arguments: Pair): Long = compileToExpression(algebra)(*arguments) @@ -164,7 +164,7 @@ public fun MST.compile(algebra: LongRing, vararg arguments: Pair): * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compileToExpression(algebra: DoubleField): DoubleExpression { +public fun MST.compileToExpression(algebra: Float64Field): DoubleExpression { val typed = evaluateConstants(algebra) return if (typed is TypedMst.Constant) object : DoubleExpression { @@ -182,7 +182,7 @@ public fun MST.compileToExpression(algebra: DoubleField): DoubleExpression { * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: DoubleField, arguments: Map): Double = +public fun MST.compile(algebra: Float64Field, arguments: Map): Double = compileToExpression(algebra)(arguments) /** @@ -191,5 +191,5 @@ public fun MST.compile(algebra: DoubleField, arguments: Map): Do * @author Iaroslav Postovalov */ @UnstableKMathAPI -public fun MST.compile(algebra: DoubleField, vararg arguments: Pair): Double = +public fun MST.compile(algebra: Float64Field, vararg arguments: Pair): Double = compileToExpression(algebra)(*arguments) diff --git a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt index a3e5b7522..bb8143206 100644 --- a/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/space/kscience/kmath/asm/internal/PrimitiveAsmBuilder.kt @@ -382,7 +382,7 @@ internal sealed class PrimitiveAsmBuilder>( @UnstableKMathAPI internal class DoubleAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder( - DoubleField, + Float64Field, java.lang.Double::class.java, java.lang.Double.TYPE, DoubleExpression::class.java, @@ -457,7 +457,7 @@ internal class DoubleAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder< @UnstableKMathAPI internal class IntAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder( - IntRing, + Int32Ring, Integer::class.java, Integer.TYPE, IntExpression::class.java, @@ -487,7 +487,7 @@ internal class IntAsmBuilder(target: TypedMst) : @UnstableKMathAPI internal class LongAsmBuilder(target: TypedMst) : PrimitiveAsmBuilder( - LongRing, + Int64Ring, java.lang.Long::class.java, java.lang.Long.TYPE, LongExpression::class.java, diff --git a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt index be890273d..e04e2e5bd 100644 --- a/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt +++ b/kmath-ast/src/jvmTest/kotlin/space/kscience/kmath/ast/utils.kt @@ -10,34 +10,34 @@ import space.kscience.kmath.expressions.Expression import space.kscience.kmath.expressions.MST import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.operations.Algebra -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import kotlin.contracts.InvocationKind import kotlin.contracts.contract import space.kscience.kmath.asm.compile as asmCompile import space.kscience.kmath.asm.compileToExpression as asmCompileToExpression private object GenericAsmCompilerTestContext : CompilerTestContext { - override fun MST.compileToExpression(algebra: IntRing): Expression = + override fun MST.compileToExpression(algebra: Int32Ring): Expression = asmCompileToExpression(algebra as Algebra) - override fun MST.compile(algebra: IntRing, arguments: Map): Int = + override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = asmCompile(algebra as Algebra, arguments) - override fun MST.compileToExpression(algebra: DoubleField): Expression = + override fun MST.compileToExpression(algebra: Float64Field): Expression = asmCompileToExpression(algebra as Algebra) - override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + override fun MST.compile(algebra: Float64Field, arguments: Map): Double = asmCompile(algebra as Algebra, arguments) } @OptIn(UnstableKMathAPI::class) private object PrimitiveAsmCompilerTestContext : CompilerTestContext { - override fun MST.compileToExpression(algebra: IntRing): Expression = asmCompileToExpression(algebra) - override fun MST.compile(algebra: IntRing, arguments: Map): Int = asmCompile(algebra, arguments) - override fun MST.compileToExpression(algebra: DoubleField): Expression = asmCompileToExpression(algebra) + override fun MST.compileToExpression(algebra: Int32Ring): Expression = asmCompileToExpression(algebra) + override fun MST.compile(algebra: Int32Ring, arguments: Map): Int = asmCompile(algebra, arguments) + override fun MST.compileToExpression(algebra: Float64Field): Expression = asmCompileToExpression(algebra) - override fun MST.compile(algebra: DoubleField, arguments: Map): Double = + override fun MST.compile(algebra: Float64Field, arguments: Map): Double = asmCompile(algebra, arguments) } diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt index d19bd1be0..449aa79ce 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt @@ -9,7 +9,7 @@ import org.apache.commons.math3.linear.* import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* import space.kscience.kmath.nd.StructureFeature -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.DoubleBuffer import kotlin.reflect.KClass @@ -35,15 +35,15 @@ public value class CMVector(public val origin: RealVector) : Point { public fun RealVector.toPoint(): CMVector = CMVector(this) -public object CMLinearSpace : LinearSpace { - override val elementAlgebra: DoubleField get() = DoubleField +public object CMLinearSpace : LinearSpace { + override val elementAlgebra: Float64Field get() = Float64Field override fun buildMatrix( rows: Int, columns: Int, - initializer: DoubleField.(i: Int, j: Int) -> Double, + initializer: Float64Field.(i: Int, j: Int) -> Double, ): CMMatrix { - val array = Array(rows) { i -> DoubleArray(columns) { j -> DoubleField.initializer(i, j) } } + val array = Array(rows) { i -> DoubleArray(columns) { j -> Float64Field.initializer(i, j) } } return CMMatrix(Array2DRowRealMatrix(array)) } @@ -65,8 +65,8 @@ public object CMLinearSpace : LinearSpace { internal fun RealMatrix.wrap(): CMMatrix = CMMatrix(this) internal fun RealVector.wrap(): CMVector = CMVector(this) - override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): Point = - ArrayRealVector(DoubleArray(size) { DoubleField.initializer(it) }).wrap() + override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Point = + ArrayRealVector(DoubleArray(size) { Float64Field.initializer(it) }).wrap() override fun Matrix.plus(other: Matrix): CMMatrix = toCM().origin.add(other.toCM().origin).wrap() diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt index 6541736ce..9a249b29b 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/integration/IntegrationTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.integration.integrate import space.kscience.kmath.integration.value -import space.kscience.kmath.operations.DoubleField.sin +import space.kscience.kmath.operations.Float64Field.sin import kotlin.math.PI import kotlin.math.abs import kotlin.test.assertTrue diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index d2e86bb40..bf687aa72 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -14,7 +14,7 @@ import space.kscience.kmath.expressions.Symbol.Companion.y import space.kscience.kmath.expressions.autodiff import space.kscience.kmath.expressions.symbol import space.kscience.kmath.operations.DoubleBufferOps.Companion.map -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.optimization.* import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.chiSquaredExpression @@ -24,7 +24,7 @@ import kotlin.test.Test @OptIn(UnstableKMathAPI::class) internal class OptimizeTest { - val normal = DSFieldExpression(DoubleField) { + val normal = DSFieldExpression(Float64Field) { exp(-bindSymbol(x).pow(2) / 2) + exp(-bindSymbol(y).pow(2) / 2) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt index 8c7cb0cf1..192509993 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/DSAlgebra.kt @@ -464,4 +464,4 @@ public class DSFieldProcessor>( } @UnstableKMathAPI -public val Double.Companion.autodiff: DSFieldProcessor get() = DSFieldProcessor(DoubleField) \ No newline at end of file +public val Double.Companion.autodiff: DSFieldProcessor get() = DSFieldProcessor(Float64Field) \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt index 1054d1aa1..8e37f3d32 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/FunctionalExpressionAlgebra.kt @@ -191,6 +191,6 @@ public inline fun > A.expressionInExtendedField( block: FunctionalExpressionExtendedField.() -> Expression, ): Expression = FunctionalExpressionExtendedField(this).block() -public inline fun DoubleField.expression( - block: FunctionalExpressionExtendedField.() -> Expression, +public inline fun Float64Field.expression( + block: FunctionalExpressionExtendedField.() -> Expression, ): Expression = FunctionalExpressionExtendedField(this).block() diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt index 940af4a86..4f631336c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt @@ -8,25 +8,25 @@ package space.kscience.kmath.linear import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleBufferOps -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.DoubleBuffer -public object DoubleLinearSpace : LinearSpace { +public object DoubleLinearSpace : LinearSpace { - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field override fun buildMatrix( rows: Int, columns: Int, - initializer: DoubleField.(i: Int, j: Int) -> Double + initializer: Float64Field.(i: Int, j: Int) -> Double ): Matrix = DoubleFieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) -> - DoubleField.initializer(i, j) + Float64Field.initializer(i, j) }.as2D() - override fun buildVector(size: Int, initializer: DoubleField.(Int) -> Double): DoubleBuffer = - DoubleBuffer(size) { DoubleField.initializer(it) } + override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): DoubleBuffer = + DoubleBuffer(size) { Float64Field.initializer(it) } override fun Matrix.unaryMinus(): Matrix = DoubleFieldOpsND { asND().map { -it }.as2D() @@ -105,4 +105,4 @@ public object DoubleLinearSpace : LinearSpace { } -public val DoubleField.linearSpace: DoubleLinearSpace get() = DoubleLinearSpace +public val Float64Field.linearSpace: DoubleLinearSpace get() = DoubleLinearSpace diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt index 650e7be5c..4bb67bce9 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt @@ -155,7 +155,7 @@ public inline fun > LinearSpace>.lup( noinline checkSingular: (T) -> Boolean, ): LupDecomposition = lup(MutableBuffer.Companion::auto, matrix, checkSingular) -public fun LinearSpace.lup( +public fun LinearSpace.lup( matrix: Matrix, singularityThreshold: Double = 1e-11, ): LupDecomposition = @@ -226,5 +226,5 @@ public fun , F : Field> LinearSpace.lupSolver( override fun inverse(matrix: Matrix): Matrix = solve(matrix, one(matrix.rowNum, matrix.colNum)) } -public fun LinearSpace.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver = +public fun LinearSpace.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver = lupSolver(::DoubleBuffer) { it < singularityThreshold } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/sorting.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/sorting.kt index 31b8c0037..f369f0614 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/sorting.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/misc/sorting.kt @@ -65,6 +65,15 @@ public fun > Buffer.sortedByDescending(selector: (V) -> public fun Buffer.indicesSortedWith(comparator: Comparator): IntArray = permSortIndicesWith { i1, i2 -> comparator.compare(get(i1), get(i2)) } +/** + * Create virtual zero-copy buffer with elements sorted by [comparator] + */ +@OptIn(UnstableKMathAPI::class) +public fun Buffer.sortedWith(comparator: Comparator): Buffer { + val permutations = indicesSortedWith(comparator) + return VirtualBuffer(size) { this[permutations[it]] } +} + private fun Buffer.permSortIndicesWith(comparator: Comparator): IntArray { if (size < 2) return IntArray(size) { 0 } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt index 265d1eec8..2f47f0b37 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt @@ -29,7 +29,7 @@ public class DoubleBufferND( } -public sealed class DoubleFieldOpsND : BufferedFieldOpsND(DoubleField.bufferAlgebra), +public sealed class DoubleFieldOpsND : BufferedFieldOpsND(Float64Field.bufferAlgebra), ScaleOperations>, ExtendedFieldOps> { @OptIn(PerformancePitfall::class) @@ -63,18 +63,18 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D } @OptIn(PerformancePitfall::class) - override fun StructureND.map(transform: DoubleField.(Double) -> Double): BufferND = - mapInline(toBufferND()) { DoubleField.transform(it) } + override fun StructureND.map(transform: Float64Field.(Double) -> Double): BufferND = + mapInline(toBufferND()) { Float64Field.transform(it) } @OptIn(PerformancePitfall::class) override fun zip( left: StructureND, right: StructureND, - transform: DoubleField.(Double, Double) -> Double, - ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> DoubleField.transform(l, r) } + transform: Float64Field.(Double, Double) -> Double, + ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> Float64Field.transform(l, r) } - override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleBufferND { + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): DoubleBufferND { val indexer = indexerBuilder(shape) return DoubleBufferND( indexer, @@ -190,7 +190,7 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND(D @OptIn(UnstableKMathAPI::class) public class DoubleFieldND(override val shape: ShapeND) : - DoubleFieldOpsND(), FieldND, NumbersAddOps>, + DoubleFieldOpsND(), FieldND, NumbersAddOps>, ExtendedField> { override fun power(arg: StructureND, pow: UInt): DoubleBufferND = mapInline(arg.toBufferND()) { @@ -229,16 +229,16 @@ public class DoubleFieldND(override val shape: ShapeND) : } } -public val DoubleField.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND +public val Float64Field.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND -public fun DoubleField.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(ShapeND(shape)) -public fun DoubleField.ndAlgebra(shape: ShapeND): DoubleFieldND = DoubleFieldND(shape) +public fun Float64Field.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(ShapeND(shape)) +public fun Float64Field.ndAlgebra(shape: ShapeND): DoubleFieldND = DoubleFieldND(shape) /** * Produce a context for n-dimensional operations inside this real field */ @UnstableKMathAPI -public inline fun DoubleField.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R { +public inline fun Float64Field.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return DoubleFieldND(ShapeND(shape)).run(action) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt index 1491950d6..4ce2b80e6 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.nd import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.NumbersAddOps import space.kscience.kmath.operations.bufferAlgebra import space.kscience.kmath.structures.IntBuffer @@ -18,9 +18,9 @@ public class IntBufferND( override val buffer: IntBuffer, ) : MutableBufferND(indexes, buffer) -public sealed class IntRingOpsND : BufferedRingOpsND(IntRing.bufferAlgebra) { +public sealed class IntRingOpsND : BufferedRingOpsND(Int32Ring.bufferAlgebra) { - override fun structureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntBufferND { + override fun structureND(shape: ShapeND, initializer: Int32Ring.(IntArray) -> Int): IntBufferND { val indexer = indexerBuilder(shape) return IntBufferND( indexer, @@ -36,7 +36,7 @@ public sealed class IntRingOpsND : BufferedRingOpsND(IntRing.buffe @OptIn(UnstableKMathAPI::class) public class IntRingND( override val shape: ShapeND -) : IntRingOpsND(), RingND, NumbersAddOps> { +) : IntRingOpsND(), RingND, NumbersAddOps> { override fun number(value: Number): BufferND { val int = value.toInt() // minimize conversions @@ -44,7 +44,7 @@ public class IntRingND( } } -public inline fun IntRing.withNdAlgebra(vararg shape: Int, action: IntRingND.() -> R): R { +public inline fun Int32Ring.withNdAlgebra(vararg shape: Int, action: IntRingND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return IntRingND(ShapeND(shape)).run(action) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt index 1b4647146..12f59dc72 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt @@ -6,20 +6,20 @@ package space.kscience.kmath.nd import space.kscience.kmath.UnstableKMathAPI +import space.kscience.kmath.operations.Int16Ring import space.kscience.kmath.operations.NumbersAddOps -import space.kscience.kmath.operations.ShortRing import space.kscience.kmath.operations.bufferAlgebra import kotlin.contracts.InvocationKind import kotlin.contracts.contract -public sealed class ShortRingOpsND : BufferedRingOpsND(ShortRing.bufferAlgebra) { +public sealed class ShortRingOpsND : BufferedRingOpsND(Int16Ring.bufferAlgebra) { public companion object : ShortRingOpsND() } @OptIn(UnstableKMathAPI::class) public class ShortRingND( override val shape: ShapeND -) : ShortRingOpsND(), RingND, NumbersAddOps> { +) : ShortRingOpsND(), RingND, NumbersAddOps> { override fun number(value: Number): BufferND { val short @@ -28,7 +28,7 @@ public class ShortRingND( } } -public inline fun ShortRing.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R { +public inline fun Int16Ring.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } return ShortRingND(ShapeND(shape)).run(action) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt index af0bc4d9b..0363f39d3 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/BufferAlgebra.kt @@ -142,11 +142,11 @@ public open class BufferRingOps>( super.binaryOperationFunction(operation) } -public val IntRing.bufferAlgebra: BufferRingOps - get() = BufferRingOps(IntRing) +public val Int32Ring.bufferAlgebra: BufferRingOps + get() = BufferRingOps(Int32Ring) -public val ShortRing.bufferAlgebra: BufferRingOps - get() = BufferRingOps(ShortRing) +public val Int16Ring.bufferAlgebra: BufferRingOps + get() = BufferRingOps(Int16Ring) public open class BufferFieldOps>( elementAlgebra: A, diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt index 74b41be9d..e15263bef 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt @@ -14,32 +14,32 @@ import kotlin.math.sqrt /** * [ExtendedFieldOps] over [DoubleBuffer]. */ -public abstract class DoubleBufferOps : BufferAlgebra, ExtendedFieldOps>, +public abstract class DoubleBufferOps : BufferAlgebra, ExtendedFieldOps>, Norm, Double> { - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field override val elementBufferFactory: MutableBufferFactory get() = elementAlgebra.bufferFactory @Suppress("OVERRIDE_BY_INLINE") @OptIn(UnstableKMathAPI::class) - final override inline fun Buffer.map(block: DoubleField.(Double) -> Double): DoubleBuffer = - DoubleArray(size) { DoubleField.block(getDouble(it)) }.asBuffer() + final override inline fun Buffer.map(block: Float64Field.(Double) -> Double): DoubleBuffer = + DoubleArray(size) { Float64Field.block(getDouble(it)) }.asBuffer() @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE") - final override inline fun Buffer.mapIndexed(block: DoubleField.(index: Int, arg: Double) -> Double): DoubleBuffer = - DoubleBuffer(size) { DoubleField.block(it, getDouble(it)) } + final override inline fun Buffer.mapIndexed(block: Float64Field.(index: Int, arg: Double) -> Double): DoubleBuffer = + DoubleBuffer(size) { Float64Field.block(it, getDouble(it)) } @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE") final override inline fun Buffer.zip( other: Buffer, - block: DoubleField.(left: Double, right: Double) -> Double, + block: Float64Field.(left: Double, right: Double) -> Double, ): DoubleBuffer { require(size == other.size) { "Incompatible buffer sizes. left: ${size}, right: ${other.size}" } - return DoubleBuffer(size) { DoubleField.block(getDouble(it), other.getDouble(it)) } + return DoubleBuffer(size) { Float64Field.block(getDouble(it), other.getDouble(it)) } } override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt new file mode 100644 index 000000000..a318dea53 --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2018-2023 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.operations + +import space.kscience.kmath.operations.Int16Field.div +import space.kscience.kmath.operations.Int32Field.div +import space.kscience.kmath.operations.Int64Field.div +import space.kscience.kmath.structures.* +import kotlin.math.roundToInt +import kotlin.math.roundToLong + + +/** + * A [Int16] field with integer division and scale. The division operation is done according to [Short.div] rules. + * + * Scaling is done according to [Double.roundToInt] rules. + * + * All results are converted to Int16. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +public object Int16Field : Field, Norm, NumericAlgebra { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::ShortBuffer) + override val zero: Int16 get() = 0 + override val one: Int16 get() = 1 + + override fun number(value: Number): Int16 = value.toShort() + override fun add(left: Int16, right: Int16): Int16 = (left + right).toShort() + override fun multiply(left: Int16, right: Int16): Int16 = (left * right).toShort() + override fun norm(arg: Int16): Int16 = abs(arg) + + override fun scale(a: Int16, value: Double): Int16 = (a*value).roundToInt().toShort() + + override fun divide(left: Int16, right: Int16): Int16 = (left / right).toShort() + + override fun Int16.unaryMinus(): Int16 = (-this).toShort() +} + +/** + * A [Int32] field with integer division and scale. The division operation is done according to [Int.div] rules. + * + * Scaling is done according to [Double.roundToInt] rules. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +public object Int32Field : Field, Norm, NumericAlgebra { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::IntBuffer) + override val zero: Int get() = 0 + override val one: Int get() = 1 + + override fun number(value: Number): Int = value.toInt() + override fun add(left: Int, right: Int): Int = left + right + override fun multiply(left: Int, right: Int): Int = left * right + override fun norm(arg: Int): Int = abs(arg) + + override fun scale(a: Int, value: Double): Int = (a*value).roundToInt() + + override fun divide(left: Int, right: Int): Int = left / right + + override fun Int.unaryMinus(): Int = -this +} + +/** + * A [Int64] field with integer division and scale. The division operation is done according to [Long.div] rules. + * + * Scaling is done according to [Double.roundToLong] rules. + */ +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +public object Int64Field : Field, Norm, NumericAlgebra { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::LongBuffer) + override val zero: Int64 get() = 0L + override val one: Int64 get() = 1L + + override fun number(value: Number): Int64 = value.toLong() + override fun add(left: Int64, right: Int64): Int64 = left + right + override fun multiply(left: Int64, right: Int64): Int64 = left * right + override fun norm(arg: Int64): Int64 = abs(arg) + + override fun scale(a: Int64, value: Double): Int64 = (a*value).roundToLong() + + override fun divide(left: Int64, right: Int64): Int64 = left / right + + override fun Int64.unaryMinus(): Int64 = -this +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index 224ca1daf..17b3a72cc 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -2,7 +2,6 @@ * Copyright 2018-2022 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. */ -@file:Suppress("NOTHING_TO_INLINE") package space.kscience.kmath.operations import space.kscience.kmath.structures.* @@ -67,7 +66,7 @@ public interface ExtendedField : ExtendedFieldOps, Field, NumericAlgebr * A field for [Double] without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -public object DoubleField : ExtendedField, Norm, ScaleOperations { +public object Float64Field : ExtendedField, Norm, ScaleOperations { override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::DoubleBuffer) override inline val zero: Double get() = 0.0 @@ -121,13 +120,15 @@ public object DoubleField : ExtendedField, Norm, ScaleOp override inline fun Double.div(arg: Double): Double = this / arg } -public val Double.Companion.algebra: DoubleField get() = DoubleField +public typealias DoubleField = Float64Field + +public val Double.Companion.algebra: Float64Field get() = Float64Field /** * A field for [Float] without boxing. Does not produce appropriate field element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -public object FloatField : ExtendedField, Norm { +public object Float32Field : ExtendedField, Norm { override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::FloatBuffer) override inline val zero: Float get() = 0.0f @@ -177,13 +178,15 @@ public object FloatField : ExtendedField, Norm { override inline fun Float.div(arg: Float): Float = this / arg } -public val Float.Companion.algebra: FloatField get() = FloatField +public typealias FloatField = Float32Field + +public val Float.Companion.algebra: Float32Field get() = Float32Field /** * A field for [Int] without boxing. Does not produce corresponding ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -public object IntRing : Ring, Norm, NumericAlgebra { +public object Int32Ring : Ring, Norm, NumericAlgebra { override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::IntBuffer) override inline val zero: Int get() = 0 @@ -200,13 +203,15 @@ public object IntRing : Ring, Norm, NumericAlgebra { override inline fun Int.times(arg: Int): Int = this * arg } -public val Int.Companion.algebra: IntRing get() = IntRing +public typealias IntRing = Int32Ring + +public val Int.Companion.algebra: Int32Ring get() = Int32Ring /** * A field for [Short] without boxing. Does not produce appropriate ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -public object ShortRing : Ring, Norm, NumericAlgebra { +public object Int16Ring : Ring, Norm, NumericAlgebra { override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::ShortBuffer) override inline val zero: Short get() = 0 @@ -223,7 +228,9 @@ public object ShortRing : Ring, Norm, NumericAlgebra override inline fun Short.times(arg: Short): Short = (this * arg).toShort() } -public val Short.Companion.algebra: ShortRing get() = ShortRing +public typealias ShortRing = Int16Ring + +public val Short.Companion.algebra: Int16Ring get() = Int16Ring /** * A field for [Byte] without boxing. Does not produce appropriate ring element. @@ -249,10 +256,10 @@ public object ByteRing : Ring, Norm, NumericAlgebra { public val Byte.Companion.algebra: ByteRing get() = ByteRing /** - * A field for [Double] without boxing. Does not produce appropriate ring element. + * A field for [Double] without boxing. Does not produce an appropriate ring element. */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -public object LongRing : Ring, Norm, NumericAlgebra { +public object Int64Ring : Ring, Norm, NumericAlgebra { override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::LongBuffer) override inline val zero: Long get() = 0L @@ -269,4 +276,6 @@ public object LongRing : Ring, Norm, NumericAlgebra { override inline fun Long.times(arg: Long): Long = (this * arg) } -public val Long.Companion.algebra: LongRing get() = LongRing +public typealias LongRing = Int64Ring + +public val Long.Companion.algebra: Int64Ring get() = Int64Ring diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt index 02fd2600d..f538fdd01 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/BufferView.kt @@ -96,6 +96,12 @@ public fun Buffer.slice(range: IntRange): BufferView = if (this is Buf ) } +/** + * Dynamically create a range from the initial range + */ +@UnstableKMathAPI +public inline fun Buffer.slice(rangeBuilder: IntRange.() -> IntRange): BufferView = slice(rangeBuilder(indices)) + /** * Resize original buffer to a given range using given [range], filling additional segments with [defaultValue]. * Range left border could be negative to designate adding new blank segment to the beginning of the buffer diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/DSTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/DSTest.kt index 871119f48..71bbc325c 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/DSTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/DSTest.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.expressions import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.test.Test @@ -18,10 +18,10 @@ import kotlin.test.assertFails internal inline fun diff( order: Int, vararg parameters: Pair, - block: DSField.() -> Unit, + block: DSField.() -> Unit, ) { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - DSField(DoubleField, order, mapOf(*parameters)).block() + DSField(Float64Field, order, mapOf(*parameters)).block() } internal class DSTest { @@ -44,7 +44,7 @@ internal class DSTest { @Test fun dsExpressionTest() { - val f = DSFieldExpression(DoubleField) { + val f = DSFieldExpression(Float64Field) { val x by binding val y by binding x.pow(2) + 2 * x * y + y.pow(2) + 1 diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt index def9f91a6..153090d35 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/ExpressionFieldTest.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.expressions -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFails @@ -15,7 +15,7 @@ class ExpressionFieldTest { @Test fun testExpression() { - val expression = with(FunctionalExpressionField(DoubleField)) { + val expression = with(FunctionalExpressionField(Float64Field)) { val x by binding x * x + 2 * x + one } @@ -31,7 +31,7 @@ class ExpressionFieldTest { return x * x + 2 * x + one } - val expression = FunctionalExpressionField(DoubleField).expression() + val expression = FunctionalExpressionField(Float64Field).expression() assertEquals(expression(x to 1.0), 4.0) } @@ -42,7 +42,7 @@ class ExpressionFieldTest { x * x + 2 * x + one } - val expression = FunctionalExpressionField(DoubleField).expressionBuilder() + val expression = FunctionalExpressionField(Float64Field).expressionBuilder() assertEquals(expression(x to 1.0), 4.0) } } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt index 83f00ce6c..6533af344 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/InterpretTest.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.expressions import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.operations.BooleanAlgebra -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import kotlin.test.Test import kotlin.test.assertEquals @@ -19,7 +19,7 @@ internal class InterpretTest { fun interpretation() { val expr = MstField { x * 2.0 + number(2.0) / x - 16.0 - }.toExpression(DoubleField) + }.toExpression(Float64Field) assertEquals(-10.69, expr(x to 2.2), 0.02) } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt index 1618296be..c0c5fc321 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/expressions/SimpleAutoDiffTest.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.expressions -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.bindSymbol import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.asBuffer @@ -21,19 +21,19 @@ internal class SimpleAutoDiffTest { fun dx( xBinding: Pair, - body: SimpleAutoDiffField.(x: AutoDiffValue) -> AutoDiffValue, - ): DerivationResult = DoubleField.simpleAutoDiff(xBinding) { body(bindSymbol(xBinding.first)) } + body: SimpleAutoDiffField.(x: AutoDiffValue) -> AutoDiffValue, + ): DerivationResult = Float64Field.simpleAutoDiff(xBinding) { body(bindSymbol(xBinding.first)) } fun dxy( xBinding: Pair, yBinding: Pair, - body: SimpleAutoDiffField.(x: AutoDiffValue, y: AutoDiffValue) -> AutoDiffValue, - ): DerivationResult = DoubleField.simpleAutoDiff(xBinding, yBinding) { + body: SimpleAutoDiffField.(x: AutoDiffValue, y: AutoDiffValue) -> AutoDiffValue, + ): DerivationResult = Float64Field.simpleAutoDiff(xBinding, yBinding) { body(bindSymbol(xBinding.first), bindSymbol(yBinding.first)) } - fun diff(block: SimpleAutoDiffField.() -> AutoDiffValue): SimpleAutoDiffExpression { - return SimpleAutoDiffExpression(DoubleField, block) + fun diff(block: SimpleAutoDiffField.() -> AutoDiffValue): SimpleAutoDiffExpression { + return SimpleAutoDiffExpression(Float64Field, block) } val x by symbol @@ -42,7 +42,7 @@ internal class SimpleAutoDiffTest { @Test fun testPlusX2() { - val y = DoubleField.simpleAutoDiff(x to 3.0) { + val y = Float64Field.simpleAutoDiff(x to 3.0) { // diff w.r.t this x at 3 val x = bindSymbol(x) x + x @@ -65,7 +65,7 @@ internal class SimpleAutoDiffTest { @Test fun testPlus() { // two variables - val z = DoubleField.simpleAutoDiff(x to 2.0, y to 3.0) { + val z = Float64Field.simpleAutoDiff(x to 2.0, y to 3.0) { val x = bindSymbol(x) val y = bindSymbol(y) x + y @@ -78,7 +78,7 @@ internal class SimpleAutoDiffTest { @Test fun testMinus() { // two variables - val z = DoubleField.simpleAutoDiff(x to 7.0, y to 3.0) { + val z = Float64Field.simpleAutoDiff(x to 7.0, y to 3.0) { val x = bindSymbol(x) val y = bindSymbol(y) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt index e909a2aea..ada7f4244 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/nd/NdOperationsTest.kt @@ -5,14 +5,14 @@ package space.kscience.kmath.nd -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.test.Test import kotlin.test.assertEquals class NdOperationsTest { @Test fun roll() { - val structure = DoubleField.ndAlgebra.structureND(5, 5) { index -> + val structure = Float64Field.ndAlgebra.structureND(5, 5) { index -> index.sumOf { it.toDouble() } } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt index 688daa7fe..2f0ee28e7 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/operations/DoubleFieldTest.kt @@ -11,16 +11,16 @@ import kotlin.test.assertEquals internal class DoubleFieldTest { @Test - fun verify() = FieldVerifier(DoubleField, 42.0, 66.0, 2.0, 5).verify() + fun verify() = FieldVerifier(Float64Field, 42.0, 66.0, 2.0, 5).verify() @Test fun testSqrt() { - val sqrt = DoubleField { sqrt(25 * one) } + val sqrt = Float64Field { sqrt(25 * one) } assertEquals(5.0, sqrt) } @Test - fun testPow() = DoubleField { + fun testPow() = Float64Field { val num = 5 * one assertEquals(5.0, power(num, 1), 0.01) assertEquals(25.0, power(num, 2), 0.01) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt index 566145621..d6c6adedd 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NDFieldTest.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.structures import space.kscience.kmath.nd.get import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.structureND -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.testutils.FieldVerifier import kotlin.test.Test @@ -17,12 +17,12 @@ import kotlin.test.assertEquals internal class NDFieldTest { @Test fun verify() { - (DoubleField.ndAlgebra(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } + (Float64Field.ndAlgebra(12, 32)) { FieldVerifier(this, one + 3, one - 23, one * 12, 6.66) } } @Test fun testStrides() { - val ndArray = DoubleField.ndAlgebra.structureND(10, 10) { (it[0] + it[1]).toDouble() } + val ndArray = Float64Field.ndAlgebra.structureND(10, 10) { (it[0] + it[1]).toDouble() } assertEquals(ndArray[5, 5], 10.0) } } diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt index 993fb089f..4ee6d50cb 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/structures/NumberNDFieldTest.kt @@ -11,7 +11,7 @@ import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.get import space.kscience.kmath.nd.ndAlgebra import space.kscience.kmath.nd.structureND -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.invoke @@ -25,7 +25,7 @@ import kotlin.test.assertEquals @OptIn(PerformancePitfall::class) @Suppress("UNUSED_VARIABLE") class NumberNDFieldTest { - val algebra = DoubleField.ndAlgebra + val algebra = Float64Field.ndAlgebra val array1 = algebra.structureND(3, 3) { (i, j) -> (i + j).toDouble() } val array2 = algebra.structureND(3, 3) { (i, j) -> (i - j).toDouble() } @@ -94,7 +94,7 @@ class NumberNDFieldTest { @Test fun testInternalContext() { algebra { - (DoubleField.ndAlgebra(array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } } + (Float64Field.ndAlgebra(array1.shape)) { with(L2Norm) { 1 + norm(array1) + exp(array2) } } } } } diff --git a/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt b/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt index dde2d4fcf..6cade1d5d 100644 --- a/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt +++ b/kmath-dimensions/src/commonMain/kotlin/space/kscience/kmath/dimensions/Wrappers.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.dimensions import space.kscience.kmath.linear.* import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.Structure2D -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.algebra import kotlin.jvm.JvmInline @@ -150,7 +150,7 @@ public value class DMatrixContext>(public val context: context.run { (this@transpose as Matrix).transpose() }.coerce() public companion object { - public val real: DMatrixContext = DMatrixContext(Double.algebra.linearSpace) + public val real: DMatrixContext = DMatrixContext(Double.algebra.linearSpace) } } @@ -158,12 +158,12 @@ public value class DMatrixContext>(public val context: /** * A square unit matrix */ -public inline fun DMatrixContext.one(): DMatrix = +public inline fun DMatrixContext.one(): DMatrix = produce { i, j -> if (i == j) 1.0 else 0.0 } -public inline fun DMatrixContext.zero(): DMatrix = +public inline fun DMatrixContext.zero(): DMatrix = produce { _, _ -> 0.0 } diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index 8ad7f7293..f48ab4c19 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -19,12 +19,12 @@ import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC +import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix -import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.FloatField +import space.kscience.kmath.operations.Float32Field +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.FloatBuffer @@ -71,11 +71,11 @@ public class EjmlFloatMatrix(override val origin: M) : EjmlMatr * [EjmlLinearSpace] implementation based on [CommonOps_DDRM], [DecompositionFactory_DDRM] operations and * [DMatrixRMaj] matrices. */ -public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { +public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { /** - * The [DoubleField] reference. + * The [Float64Field] reference. */ - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field @Suppress("UNCHECKED_CAST") override fun Matrix.toEjml(): EjmlDoubleMatrix = when { @@ -94,7 +94,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace Double, + initializer: Float64Field.(i: Int, j: Int) -> Double, ): EjmlDoubleMatrix = DMatrixRMaj(rows, columns).also { (0 until rows).forEach { row -> (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } @@ -103,7 +103,7 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace Double, + initializer: Float64Field.(Int) -> Double, ): EjmlDoubleVector = EjmlDoubleVector(DMatrixRMaj(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } }) @@ -307,11 +307,11 @@ public object EjmlLinearSpaceDDRM : EjmlLinearSpace() { +public object EjmlLinearSpaceFDRM : EjmlLinearSpace() { /** - * The [FloatField] reference. + * The [Float32Field] reference. */ - override val elementAlgebra: FloatField get() = FloatField + override val elementAlgebra: Float32Field get() = Float32Field @Suppress("UNCHECKED_CAST") override fun Matrix.toEjml(): EjmlFloatMatrix = when { @@ -330,7 +330,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace Float, + initializer: Float32Field.(i: Int, j: Int) -> Float, ): EjmlFloatMatrix = FMatrixRMaj(rows, columns).also { (0 until rows).forEach { row -> (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } @@ -339,7 +339,7 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace Float, + initializer: Float32Field.(Int) -> Float, ): EjmlFloatVector = EjmlFloatVector(FMatrixRMaj(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } }) @@ -543,11 +543,11 @@ public object EjmlLinearSpaceFDRM : EjmlLinearSpace() { +public object EjmlLinearSpaceDSCC : EjmlLinearSpace() { /** - * The [DoubleField] reference. + * The [Float64Field] reference. */ - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field @Suppress("UNCHECKED_CAST") override fun Matrix.toEjml(): EjmlDoubleMatrix = when { @@ -566,7 +566,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace Double, + initializer: Float64Field.(i: Int, j: Int) -> Double, ): EjmlDoubleMatrix = DMatrixSparseCSC(rows, columns).also { (0 until rows).forEach { row -> (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } @@ -575,7 +575,7 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace Double, + initializer: Float64Field.(Int) -> Double, ): EjmlDoubleVector = EjmlDoubleVector(DMatrixSparseCSC(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } }) @@ -774,11 +774,11 @@ public object EjmlLinearSpaceDSCC : EjmlLinearSpace() { +public object EjmlLinearSpaceFSCC : EjmlLinearSpace() { /** - * The [FloatField] reference. + * The [Float32Field] reference. */ - override val elementAlgebra: FloatField get() = FloatField + override val elementAlgebra: Float32Field get() = Float32Field @Suppress("UNCHECKED_CAST") override fun Matrix.toEjml(): EjmlFloatMatrix = when { @@ -797,7 +797,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace Float, + initializer: Float32Field.(i: Int, j: Int) -> Float, ): EjmlFloatMatrix = FMatrixSparseCSC(rows, columns).also { (0 until rows).forEach { row -> (0 until columns).forEach { col -> it[row, col] = elementAlgebra.initializer(row, col) } @@ -806,7 +806,7 @@ public object EjmlLinearSpaceFSCC : EjmlLinearSpace Float, + initializer: Float32Field.(Int) -> Float, ): EjmlFloatVector = EjmlFloatVector(FMatrixSparseCSC(size, 1).also { (0 until it.numRows).forEach { row -> it[row, 0] = elementAlgebra.initializer(row) } }) diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt index 40e4a91f1..f86b9ef5f 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt @@ -11,7 +11,7 @@ package space.kscience.kmath.real import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.asIterable import space.kscience.kmath.structures.Buffer @@ -32,11 +32,11 @@ import kotlin.math.pow public typealias RealMatrix = Matrix -public fun realMatrix(rowNum: Int, colNum: Int, initializer: DoubleField.(i: Int, j: Int) -> Double): RealMatrix = +public fun realMatrix(rowNum: Int, colNum: Int, initializer: Float64Field.(i: Int, j: Int) -> Double): RealMatrix = Double.algebra.linearSpace.buildMatrix(rowNum, colNum, initializer) @OptIn(UnstableKMathAPI::class) -public fun realMatrix(rowNum: Int, colNum: Int): MatrixBuilder = +public fun realMatrix(rowNum: Int, colNum: Int): MatrixBuilder = Double.algebra.linearSpace.matrix(rowNum, colNum) public fun Array.toMatrix(): RealMatrix { diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt index 2c06b76b7..13b579144 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt @@ -6,14 +6,14 @@ package space.kscience.kmath.real import space.kscience.kmath.nd.BufferND -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.DoubleBuffer /** * Map one [BufferND] using function without indices. */ -public inline fun BufferND.mapInline(crossinline transform: DoubleField.(Double) -> Double): BufferND { - val array = DoubleArray(indices.linearSize) { offset -> DoubleField.transform(buffer[offset]) } +public inline fun BufferND.mapInline(crossinline transform: Float64Field.(Double) -> Double): BufferND { + val array = DoubleArray(indices.linearSize) { offset -> Float64Field.transform(buffer[offset]) } return BufferND(indices, DoubleBuffer(array)) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt index 73a3cc25b..35ce82351 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SimpsonIntegrator.kt @@ -6,8 +6,8 @@ package space.kscience.kmath.integration import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.Field +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.operations.sum @@ -105,4 +105,4 @@ public object DoubleSimpsonIntegrator : UnivariateIntegrator { } } -public val DoubleField.simpsonIntegrator: DoubleSimpsonIntegrator get() = DoubleSimpsonIntegrator \ No newline at end of file +public val Float64Field.simpsonIntegrator: DoubleSimpsonIntegrator get() = DoubleSimpsonIntegrator \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt index 993812b29..319e86cd0 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt @@ -85,7 +85,7 @@ public class SplineIntegrator>( public object DoubleSplineIntegrator : UnivariateIntegrator { override fun process(integrand: UnivariateIntegrand): UnivariateIntegrand { val range = integrand.getFeature()?.range ?: 0.0..1.0 - val interpolator: PolynomialInterpolator = SplineInterpolator(DoubleField, ::DoubleBuffer) + val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, ::DoubleBuffer) val nodes: Buffer = integrand.getFeature()?.nodes ?: run { val numPoints = integrand.getFeature()?.maxCalls ?: 100 @@ -95,12 +95,12 @@ public object DoubleSplineIntegrator : UnivariateIntegrator { val values = nodes.mapToBuffer(::DoubleBuffer) { integrand.function(it) } val polynomials = interpolator.interpolatePolynomials(nodes, values) - val res = polynomials.integrate(DoubleField, range) + val res = polynomials.integrate(Float64Field, range) return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size) } } @Suppress("unused") @UnstableKMathAPI -public inline val DoubleField.splineIntegrator: UnivariateIntegrator +public inline val Float64Field.splineIntegrator: UnivariateIntegrator get() = DoubleSplineIntegrator \ No newline at end of file diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt index a3cc17954..76082af74 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -9,8 +9,8 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.data.XYColumnarData import space.kscience.kmath.functions.PiecewisePolynomial import space.kscience.kmath.functions.Polynomial -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.Field +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.MutableBufferFactory @@ -79,5 +79,5 @@ public fun > Field.splineInterpolator( bufferFactory: MutableBufferFactory, ): SplineInterpolator = SplineInterpolator(this, bufferFactory) -public val DoubleField.splineInterpolator: SplineInterpolator +public val Float64Field.splineInterpolator: SplineInterpolator get() = SplineInterpolator(this, ::DoubleBuffer) \ No newline at end of file diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt index 7424f3566..dd8069335 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/GaussIntegralTest.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.integration import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.math.PI import kotlin.math.sin import kotlin.test.Test @@ -16,7 +16,7 @@ import kotlin.test.assertEquals class GaussIntegralTest { @Test fun gaussSin() { - val res = DoubleField.gaussIntegrator.integrate(0.0..2 * PI) { x -> + val res = Float64Field.gaussIntegrator.integrate(0.0..2 * PI) { x -> sin(x) } assertEquals(0.0, res.value, 1e-2) @@ -24,7 +24,7 @@ class GaussIntegralTest { @Test fun gaussUniform() { - val res = DoubleField.gaussIntegrator.integrate(35.0..100.0) { x -> + val res = Float64Field.gaussIntegrator.integrate(35.0..100.0) { x -> if(x in 30.0..50.0){ 1.0 } else { diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SimpsonIntegralTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SimpsonIntegralTest.kt index 7b699ebbc..f5a79cdea 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SimpsonIntegralTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SimpsonIntegralTest.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.integration import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.math.PI import kotlin.math.sin import kotlin.test.Test @@ -16,7 +16,7 @@ import kotlin.test.assertEquals class SimpsonIntegralTest { @Test fun gaussSin() { - val res = DoubleField.simpsonIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x -> + val res = Float64Field.simpsonIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x -> sin(x) } assertEquals(0.0, res.value, 1e-2) @@ -24,7 +24,7 @@ class SimpsonIntegralTest { @Test fun gaussUniform() { - val res = DoubleField.simpsonIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x -> + val res = Float64Field.simpsonIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x -> if (x in 30.0..50.0) { 1.0 } else { diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt index b17d21abf..6b62afbd3 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/integration/SplineIntegralTest.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.integration import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.functions.Polynomial import space.kscience.kmath.functions.integrate -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.math.PI import kotlin.math.sin import kotlin.test.Test @@ -20,13 +20,13 @@ class SplineIntegralTest { @Test fun integratePolynomial(){ val polynomial = Polynomial(1.0, 2.0, 3.0) - val integral = polynomial.integrate(DoubleField,1.0..2.0) + val integral = polynomial.integrate(Float64Field,1.0..2.0) assertEquals(11.0, integral, 0.001) } @Test fun gaussSin() { - val res = DoubleField.splineIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x -> + val res = Float64Field.splineIntegrator.integrate(0.0..2 * PI, IntegrandMaxCalls(5)) { x -> sin(x) } assertEquals(0.0, res.value, 1e-2) @@ -34,7 +34,7 @@ class SplineIntegralTest { @Test fun gaussUniform() { - val res = DoubleField.splineIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x -> + val res = Float64Field.splineIntegrator.integrate(35.0..100.0, IntegrandMaxCalls(20)) { x -> if(x in 30.0..50.0){ 1.0 } else { diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt index c0ca6c484..24a664c26 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/LinearInterpolatorTest.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.interpolation -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.test.Test import kotlin.test.assertEquals @@ -20,7 +20,7 @@ internal class LinearInterpolatorTest { ) //val polynomial: PiecewisePolynomial = DoubleField.linearInterpolator.interpolatePolynomials(data) - val function = DoubleField.linearInterpolator.interpolate(data) + val function = Float64Field.linearInterpolator.interpolate(data) assertEquals(null, function(-1.0)) assertEquals(0.5, function(0.5)) assertEquals(2.0, function(1.5)) diff --git a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt index 851a8ab7d..774b6196e 100644 --- a/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt +++ b/kmath-functions/src/commonTest/kotlin/space/kscience/kmath/interpolation/SplineInterpolatorTest.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.interpolation -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.math.PI import kotlin.math.sin import kotlin.test.Test @@ -21,7 +21,7 @@ internal class SplineInterpolatorTest { //val polynomial: PiecewisePolynomial = DoubleField.splineInterpolator.interpolatePolynomials(data) - val function = DoubleField.splineInterpolator.interpolate(data, Double.NaN) + val function = Float64Field.splineInterpolator.interpolate(data, Double.NaN) assertEquals(Double.NaN, function(-1.0)) assertEquals(sin(0.5), function(0.5), 0.1) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt index 1f3850c7c..688386e3b 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt @@ -14,7 +14,7 @@ import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.linear.matrix -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.math.pow import kotlin.math.sqrt @@ -66,7 +66,7 @@ public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, composition: Quaterni public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, matrix: Matrix): DoubleVector3D { require(matrix.colNum == 3 && matrix.rowNum == 3) { "Square 3x3 rotation matrix is required" } - return with(DoubleField.linearSpace) { matrix.dot(vector).asVector3D() } + return with(Float64Field.linearSpace) { matrix.dot(vector).asVector3D() } } /** @@ -74,7 +74,7 @@ public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, matrix: Matrix = DoubleField.linearSpace, + linearSpace: LinearSpace = Float64Field.linearSpace, ): Matrix { val s = QuaternionField.norm(this).pow(-2) return linearSpace.matrix(3, 3)( diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt index 43ed24c70..3d948e1a7 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Counter.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.histogram import kotlinx.atomicfu.atomic import kotlinx.atomicfu.getAndUpdate -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Group /** @@ -18,7 +18,7 @@ public interface Counter { public val value: T public companion object { - public fun ofDouble(): ObjectCounter = ObjectCounter(DoubleField) + public fun ofDouble(): ObjectCounter = ObjectCounter(Float64Field) public fun of(group: Group): ObjectCounter = ObjectCounter(group) } } diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt index 61ce450a7..fe9c3ae01 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt @@ -128,7 +128,7 @@ public fun > Histogram.Companion.uniformNDFromRanges( public fun Histogram.Companion.uniformDoubleNDFromRanges( vararg ranges: ClosedFloatingPointRange, -): UniformHistogramGroupND = +): UniformHistogramGroupND = uniformNDFromRanges(DoubleFieldOpsND, *ranges, bufferFactory = ::DoubleBuffer) @@ -163,5 +163,5 @@ public fun > Histogram.Companion.uniformNDFromRanges( public fun Histogram.Companion.uniformDoubleNDFromRanges( vararg ranges: Pair, Int>, -): UniformHistogramGroupND = +): UniformHistogramGroupND = uniformNDFromRanges(DoubleFieldOpsND, *ranges, bufferFactory = ::DoubleBuffer) \ No newline at end of file diff --git a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt index fa129fad6..bda9d2bea 100644 --- a/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt +++ b/kmath-histograms/src/commonTest/kotlin/space/kscience/kmath/histogram/UniformHistogram1DTest.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.distributions.NormalDistribution -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.nextBuffer import kotlin.native.concurrent.ThreadLocal @@ -22,7 +22,7 @@ internal class UniformHistogram1DTest { @Test fun normal() = runTest { val distribution = NormalDistribution(0.0, 1.0) - with(Histogram.uniform1D(DoubleField, 0.1)) { + with(Histogram.uniform1D(Float64Field, 0.1)) { val h1 = produce(distribution.nextBuffer(generator, 10000)) val h2 = produce(distribution.nextBuffer(generator, 50000)) @@ -35,16 +35,16 @@ internal class UniformHistogram1DTest { @Test fun rebinDown() = runTest { - val h1 = Histogram.uniform1D(DoubleField, 0.01).produce(generator.nextDoubleBuffer(10000)) - val h2 = Histogram.uniform1D(DoubleField, 0.03).produceFrom(h1) + val h1 = Histogram.uniform1D(Float64Field, 0.01).produce(generator.nextDoubleBuffer(10000)) + val h2 = Histogram.uniform1D(Float64Field, 0.03).produceFrom(h1) assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt()) } @Test fun rebinUp() = runTest { - val h1 = Histogram.uniform1D(DoubleField, 0.03).produce(generator.nextDoubleBuffer(10000)) - val h2 = Histogram.uniform1D(DoubleField, 0.01).produceFrom(h1) + val h1 = Histogram.uniform1D(Float64Field, 0.03).produce(generator.nextDoubleBuffer(10000)) + val h2 = Histogram.uniform1D(Float64Field, 0.01).produceFrom(h1) assertEquals(10000, h2.bins.sumOf { it.binValue }.toInt()) } diff --git a/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt b/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt index b7c1f34ba..00b94c11f 100644 --- a/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt +++ b/kmath-histograms/src/jvmTest/kotlin/space/kscience/kmath/histogram/TreeHistogramTest.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.histogram import org.junit.jupiter.api.Test import space.kscience.kmath.UnstableKMathAPI -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.real.step import kotlin.random.Random import kotlin.test.assertEquals @@ -19,7 +19,7 @@ class TreeHistogramTest { @Test fun normalFill() { val random = Random(123) - val histogram = Histogram.custom1D(DoubleField, 0.0..1.0 step 0.1).produce { + val histogram = Histogram.custom1D(Float64Field, 0.0..1.0 step 0.1).produce { repeat(100_000) { putValue(random.nextDouble()) } diff --git a/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt b/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt index ccd89f063..8d282a58a 100644 --- a/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt +++ b/kmath-kotlingrad/src/test/kotlin/space/kscience/kmath/kotlingrad/AdaptingTests.kt @@ -12,7 +12,7 @@ import space.kscience.kmath.ast.parseMath import space.kscience.kmath.expressions.MstNumericAlgebra import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.invoke -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -22,8 +22,8 @@ import kotlin.test.fail internal class AdaptingTests { @Test fun symbol() { - assertEquals(x.identity, x.toSVar>().name) - val c2 = "kitten".parseMath().toSFun>() + assertEquals(x.identity, x.toSVar>().name) + val c2 = "kitten".parseMath().toSFun>() if (c2 is SVar<*>) assertTrue(c2.name == "kitten") else fail() } @@ -31,15 +31,15 @@ internal class AdaptingTests { fun number() { val c1 = MstNumericAlgebra.number(12354324) assertTrue(c1.toSConst().doubleValue == 12354324.0) - val c2 = "0.234".parseMath().toSFun>() + val c2 = "0.234".parseMath().toSFun>() if (c2 is SConst<*>) assertTrue(c2.doubleValue == 0.234) else fail() - val c3 = "1e-3".parseMath().toSFun>() + val c3 = "1e-3".parseMath().toSFun>() if (c3 is SConst<*>) assertEquals(0.001, c3.value) else fail() } @Test fun simpleFunctionShape() { - val linear = "2*x+16".parseMath().toSFun>() + val linear = "2*x+16".parseMath().toSFun>() if (linear !is Sum<*>) fail() if (linear.left !is Prod<*>) fail() if (linear.right !is SConst<*>) fail() @@ -47,22 +47,22 @@ internal class AdaptingTests { @Test fun simpleFunctionDerivative() { - val xSVar = x.toSVar>() - val quadratic = "x^2-4*x-44".parseMath().toSFun>() - val actualDerivative = quadratic.d(xSVar).toMst().compileToExpression(DoubleField) - val expectedDerivative = "2*x-4".parseMath().compileToExpression(DoubleField) + val xSVar = x.toSVar>() + val quadratic = "x^2-4*x-44".parseMath().toSFun>() + val actualDerivative = quadratic.d(xSVar).toMst().compileToExpression(Float64Field) + val expectedDerivative = "2*x-4".parseMath().compileToExpression(Float64Field) assertEquals(actualDerivative(x to 123.0), expectedDerivative(x to 123.0)) } @Test fun moreComplexDerivative() { - val xSVar = x.toSVar>() - val composition = "-sqrt(sin(x^2)-cos(x)^2-16*x)".parseMath().toSFun>() - val actualDerivative = composition.d(xSVar).toMst().compileToExpression(DoubleField) + val xSVar = x.toSVar>() + val composition = "-sqrt(sin(x^2)-cos(x)^2-16*x)".parseMath().toSFun>() + val actualDerivative = composition.d(xSVar).toMst().compileToExpression(Float64Field) val expectedDerivative = "-(2*x*cos(x^2)+2*sin(x)*cos(x)-16)/(2*sqrt(sin(x^2)-16*x-cos(x)^2))" .parseMath() - .compileToExpression(DoubleField) + .compileToExpression(Float64Field) assertEquals(actualDerivative(x to -0.1), expectedDerivative(x to -0.1)) } diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt index beab5c18b..8b463a230 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikDoubleAlgebra.kt @@ -11,15 +11,15 @@ import org.jetbrains.kotlinx.multik.api.ndarrayOf import org.jetbrains.kotlinx.multik.ndarray.data.DataType import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExponentialOperations +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.TrigonometricOperations public class MultikDoubleAlgebra( multikEngine: Engine -) : MultikDivisionTensorAlgebra(multikEngine), +) : MultikDivisionTensorAlgebra(multikEngine), TrigonometricOperations>, ExponentialOperations> { - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field override val type: DataType get() = DataType.DoubleDataType override fun sin(arg: StructureND): MultikTensor = multikMath.mathEx.sin(arg.asMultik().array).wrap() diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikFloatAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikFloatAlgebra.kt index ee194ae24..7a3dda94b 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikFloatAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikFloatAlgebra.kt @@ -9,12 +9,12 @@ import org.jetbrains.kotlinx.multik.api.Engine import org.jetbrains.kotlinx.multik.api.Multik import org.jetbrains.kotlinx.multik.api.ndarrayOf import org.jetbrains.kotlinx.multik.ndarray.data.DataType -import space.kscience.kmath.operations.FloatField +import space.kscience.kmath.operations.Float32Field public class MultikFloatAlgebra( multikEngine: Engine -) : MultikDivisionTensorAlgebra(multikEngine) { - override val elementAlgebra: FloatField get() = FloatField +) : MultikDivisionTensorAlgebra(multikEngine) { + override val elementAlgebra: Float32Field get() = Float32Field override val type: DataType get() = DataType.FloatDataType override fun scalar(value: Float): MultikTensor = Multik.ndarrayOf(value).wrap() diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikIntAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikIntAlgebra.kt index 05b240787..5bd1b3388 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikIntAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikIntAlgebra.kt @@ -9,12 +9,12 @@ import org.jetbrains.kotlinx.multik.api.Engine import org.jetbrains.kotlinx.multik.api.Multik import org.jetbrains.kotlinx.multik.api.ndarrayOf import org.jetbrains.kotlinx.multik.ndarray.data.DataType -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Int32Ring public class MultikIntAlgebra( multikEngine: Engine -) : MultikTensorAlgebra(multikEngine) { - override val elementAlgebra: IntRing get() = IntRing +) : MultikTensorAlgebra(multikEngine) { + override val elementAlgebra: Int32Ring get() = Int32Ring override val type: DataType get() = DataType.IntDataType override fun scalar(value: Int): MultikTensor = Multik.ndarrayOf(value).wrap() } diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikLongAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikLongAlgebra.kt index e713e556e..69a8ec042 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikLongAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikLongAlgebra.kt @@ -9,12 +9,12 @@ import org.jetbrains.kotlinx.multik.api.Engine import org.jetbrains.kotlinx.multik.api.Multik import org.jetbrains.kotlinx.multik.api.ndarrayOf import org.jetbrains.kotlinx.multik.ndarray.data.DataType -import space.kscience.kmath.operations.LongRing +import space.kscience.kmath.operations.Int64Ring public class MultikLongAlgebra( multikEngine: Engine -) : MultikTensorAlgebra(multikEngine) { - override val elementAlgebra: LongRing get() = LongRing +) : MultikTensorAlgebra(multikEngine) { + override val elementAlgebra: Int64Ring get() = Int64Ring override val type: DataType get() = DataType.LongDataType override fun scalar(value: Long): MultikTensor = Multik.ndarrayOf(value).wrap() diff --git a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikShortAlgebra.kt b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikShortAlgebra.kt index 6e5ca5882..7c8740665 100644 --- a/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikShortAlgebra.kt +++ b/kmath-multik/src/commonMain/kotlin/space/kscience/kmath/multik/MultikShortAlgebra.kt @@ -9,12 +9,12 @@ import org.jetbrains.kotlinx.multik.api.Engine import org.jetbrains.kotlinx.multik.api.Multik import org.jetbrains.kotlinx.multik.api.ndarrayOf import org.jetbrains.kotlinx.multik.ndarray.data.DataType -import space.kscience.kmath.operations.ShortRing +import space.kscience.kmath.operations.Int16Ring public class MultikShortAlgebra( multikEngine: Engine -) : MultikTensorAlgebra(multikEngine) { - override val elementAlgebra: ShortRing get() = ShortRing +) : MultikTensorAlgebra(multikEngine) { + override val elementAlgebra: Int16Ring get() = Int16Ring override val type: DataType get() = DataType.ShortDataType override fun scalar(value: Short): MultikTensor = Multik.ndarrayOf(value).wrap() } diff --git a/kmath-multik/src/commonTest/kotlin/space/kscience/kmath/multik/MultikNDTest.kt b/kmath-multik/src/commonTest/kotlin/space/kscience/kmath/multik/MultikNDTest.kt index 1a130aa92..1abcf512d 100644 --- a/kmath-multik/src/commonTest/kotlin/space/kscience/kmath/multik/MultikNDTest.kt +++ b/kmath-multik/src/commonTest/kotlin/space/kscience/kmath/multik/MultikNDTest.kt @@ -10,7 +10,7 @@ import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.one -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.randomNormal import space.kscience.kmath.tensors.core.tensorAlgebra @@ -37,7 +37,7 @@ internal class MultikNDTest { tensor1 dot tensor2 } - val defaultResult = with(DoubleField.tensorAlgebra) { + val defaultResult = with(Float64Field.tensorAlgebra) { tensor1 dot tensor2 } diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt index 0eb147b6f..31523b340 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebra.kt @@ -124,7 +124,7 @@ public sealed interface Nd4jArrayRingOps> : RingOpsND, */ @Suppress("UNCHECKED_CAST") public inline fun auto(): Nd4jArrayRingOps> = when { - T::class == Int::class -> IntRing.nd4j as Nd4jArrayRingOps> + T::class == Int::class -> Int32Ring.nd4j as Nd4jArrayRingOps> else -> throw UnsupportedOperationException("This factory method only supports Long type.") } } @@ -149,8 +149,8 @@ public sealed interface Nd4jArrayField> : FieldOpsND, */ @Suppress("UNCHECKED_CAST") public inline fun auto(): Nd4jArrayField> = when { - T::class == Float::class -> FloatField.nd4j as Nd4jArrayField> - T::class == Double::class -> DoubleField.nd4j as Nd4jArrayField> + T::class == Float::class -> Float32Field.nd4j as Nd4jArrayField> + T::class == Double::class -> Float64Field.nd4j as Nd4jArrayField> else -> throw UnsupportedOperationException("This factory method only supports Float and Double types.") } } @@ -190,8 +190,8 @@ public sealed interface Nd4jArrayExtendedFieldOps> : /** * Represents [FieldND] over [Nd4jArrayDoubleStructure]. */ -public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { - override val elementAlgebra: DoubleField get() = DoubleField +public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { + override val elementAlgebra: Float64Field get() = Float64Field override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() @@ -223,19 +223,19 @@ public open class DoubleNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps +public class DoubleNd4jArrayField(override val shape: ShapeND) : DoubleNd4jArrayFieldOps(), FieldND -public fun DoubleField.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField = +public fun Float64Field.nd4j(shapeFirst: Int, vararg shapeRest: Int): DoubleNd4jArrayField = DoubleNd4jArrayField(ShapeND(shapeFirst, * shapeRest)) /** * Represents [FieldND] over [Nd4jArrayStructure] of [Float]. */ -public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { - override val elementAlgebra: FloatField get() = FloatField +public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps { + override val elementAlgebra: Float32Field get() = Float32Field override fun INDArray.wrap(): Nd4jArrayStructure = asFloatStructure() @@ -272,18 +272,18 @@ public open class FloatNd4jArrayFieldOps : Nd4jArrayExtendedFieldOps +public class FloatNd4jArrayField(override val shape: ShapeND) : FloatNd4jArrayFieldOps(), RingND -public val FloatField.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps +public val Float32Field.nd4j: FloatNd4jArrayFieldOps get() = FloatNd4jArrayFieldOps -public fun FloatField.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField = +public fun Float32Field.nd4j(shapeFirst: Int, vararg shapeRest: Int): FloatNd4jArrayField = FloatNd4jArrayField(ShapeND(shapeFirst, * shapeRest)) /** * Represents [RingND] over [Nd4jArrayIntStructure]. */ -public open class IntNd4jArrayRingOps : Nd4jArrayRingOps { - override val elementAlgebra: IntRing get() = IntRing +public open class IntNd4jArrayRingOps : Nd4jArrayRingOps { + override val elementAlgebra: Int32Ring get() = Int32Ring override fun INDArray.wrap(): Nd4jArrayStructure = asIntStructure() @@ -311,9 +311,9 @@ public open class IntNd4jArrayRingOps : Nd4jArrayRingOps { public companion object : IntNd4jArrayRingOps() } -public val IntRing.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps +public val Int32Ring.nd4j: IntNd4jArrayRingOps get() = IntNd4jArrayRingOps -public class IntNd4jArrayRing(override val shape: ShapeND) : IntNd4jArrayRingOps(), RingND +public class IntNd4jArrayRing(override val shape: ShapeND) : IntNd4jArrayRingOps(), RingND -public fun IntRing.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing = +public fun Int32Ring.nd4j(shapeFirst: Int, vararg shapeRest: Int): IntNd4jArrayRing = IntNd4jArrayRing(ShapeND(shapeFirst, * shapeRest)) \ No newline at end of file diff --git a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt index 5905739f8..c308e64b9 100644 --- a/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt +++ b/kmath-nd4j/src/main/kotlin/space/kscience/kmath/nd4j/Nd4jTensorAlgebra.kt @@ -15,8 +15,8 @@ import org.nd4j.linalg.ops.transforms.Transforms import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnsafeKMathAPI import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.Field +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.api.TensorAlgebra @@ -171,14 +171,14 @@ public sealed interface Nd4jTensorAlgebra> : AnalyticTe /** * [Double] specialization of [Nd4jTensorAlgebra]. */ -public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra { +public object DoubleNd4jTensorAlgebra : Nd4jTensorAlgebra { - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field override fun INDArray.wrap(): Nd4jArrayStructure = asDoubleStructure() @OptIn(UnsafeKMathAPI::class) - override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): Nd4jArrayStructure { + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): Nd4jArrayStructure { val array: INDArray = Nd4j.zeros(*shape.asArray()) val indices = ColumnStrides(shape) indices.asSequence().forEach { index -> diff --git a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt index 708778e77..484618de2 100644 --- a/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt +++ b/kmath-nd4j/src/test/kotlin/space/kscience/kmath/nd4j/Nd4jArrayAlgebraTest.kt @@ -10,8 +10,8 @@ import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.one import space.kscience.kmath.nd.structureND -import space.kscience.kmath.operations.DoubleField -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.invoke import kotlin.math.PI import kotlin.test.Test @@ -23,7 +23,7 @@ import kotlin.test.fail internal class Nd4jArrayAlgebraTest { @Test fun testProduce() { - val res = DoubleField.nd4j.structureND(2, 2) { it.sum().toDouble() } + val res = Float64Field.nd4j.structureND(2, 2) { it.sum().toDouble() } val expected = (Nd4j.create(2, 2) ?: fail()).asDoubleStructure() expected[intArrayOf(0, 0)] = 0.0 expected[intArrayOf(0, 1)] = 1.0 @@ -34,7 +34,7 @@ internal class Nd4jArrayAlgebraTest { @Test fun testMap() { - val res = IntRing.nd4j { + val res = Int32Ring.nd4j { one(2, 2).map { it + it * 2 } } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() @@ -47,7 +47,7 @@ internal class Nd4jArrayAlgebraTest { @Test fun testAdd() { - val res = IntRing.nd4j { one(2, 2) + 25 } + val res = Int32Ring.nd4j { one(2, 2) + 25 } val expected = (Nd4j.create(2, 2) ?: fail()).asIntStructure() expected[intArrayOf(0, 0)] = 26 expected[intArrayOf(0, 1)] = 26 @@ -57,7 +57,7 @@ internal class Nd4jArrayAlgebraTest { } @Test - fun testSin() = DoubleField.nd4j{ + fun testSin() = Float64Field.nd4j{ val initial = structureND(2, 2) { (i, j) -> if (i == j) PI / 2 else 0.0 } val transformed = sin(initial) val expected = structureND(2, 2) { (i, j) -> if (i == j) 1.0 else 0.0 } diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt index b698584aa..2732efac7 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt @@ -9,8 +9,8 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.* import space.kscience.kmath.linear.* import space.kscience.kmath.misc.log -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.DoubleL2Norm +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.algebra import space.kscience.kmath.structures.DoubleBuffer import kotlin.math.abs @@ -32,7 +32,7 @@ public class QowRuns(public val runs: Int) : OptimizationFeature { @UnstableKMathAPI public object QowOptimizer : Optimizer { - private val linearSpace: LinearSpace = Double.algebra.linearSpace + private val linearSpace: LinearSpace = Double.algebra.linearSpace private val solver: LinearSolver = linearSpace.lupSolver() @OptIn(UnstableKMathAPI::class) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt index ae814254b..10947b732 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/distributions/NormalDistribution.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.distributions import space.kscience.kmath.chains.Chain -import space.kscience.kmath.operations.DoubleField.pow +import space.kscience.kmath.operations.Float64Field.pow import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.samplers.GaussianSampler import space.kscience.kmath.samplers.InternalErf diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt index 4becb3413..6cd64df9c 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/series/VarianceRatioTest.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.series import space.kscience.kmath.distributions.NormalDistribution -import space.kscience.kmath.operations.DoubleField.pow +import space.kscience.kmath.operations.Float64Field.pow import space.kscience.kmath.operations.fold import kotlin.math.absoluteValue diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt index 3bf8b33e8..74ef65fa1 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Mean.kt @@ -13,21 +13,20 @@ import space.kscience.kmath.structures.indices * Arithmetic mean */ public class Mean( - private val group: Ring, - private val division: (sum: T, count: Int) -> T, + private val field: Field ) : ComposableStatistic, T>, BlockingStatistic { - override fun evaluateBlocking(data: Buffer): T = group { + override fun evaluateBlocking(data: Buffer): T = with(field) { var res = zero for (i in data.indices) { res += data[i] } - division(res, data.size) + res / data.size } override suspend fun evaluate(data: Buffer): T = super.evaluate(data) - override suspend fun computeIntermediate(data: Buffer): Pair = group { + override suspend fun computeIntermediate(data: Buffer): Pair = with(field) { var res = zero for (i in data.indices) { res += data[i] @@ -36,32 +35,23 @@ public class Mean( } override suspend fun composeIntermediate(first: Pair, second: Pair): Pair = - group { first.first + second.first } to (first.second + second.second) + with(field) { first.first + second.first } to (first.second + second.second) - override suspend fun toResult(intermediate: Pair): T = group { - division(intermediate.first, intermediate.second) + override suspend fun toResult(intermediate: Pair): T = with(field) { + intermediate.first/ intermediate.second } public companion object { - @Deprecated("Use Double.mean instead") - public val double: Mean = Mean(DoubleField) { sum, count -> sum / count } - - @Deprecated("Use Int.mean instead") - public val int: Mean = Mean(IntRing) { sum, count -> sum / count } - - @Deprecated("Use Long.mean instead") - public val long: Mean = Mean(LongRing) { sum, count -> sum / count } - - public fun evaluate(buffer: Buffer): Double = DoubleField.mean.evaluateBlocking(buffer) - public fun evaluate(buffer: Buffer): Int = IntRing.mean.evaluateBlocking(buffer) - public fun evaluate(buffer: Buffer): Long = LongRing.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Double = Float64Field.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Int = Int32Ring.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Long = Int64Ring.mean.evaluateBlocking(buffer) } } //TODO replace with optimized version which respects overflow -public val DoubleField.mean: Mean get() = Mean(DoubleField) { sum, count -> sum / count } -public val IntRing.mean: Mean get() = Mean(IntRing) { sum, count -> sum / count } -public val LongRing.mean: Mean get() = Mean(LongRing) { sum, count -> sum / count } +public val Float64Field.mean: Mean get() = Mean(Float64Field) +public val Int32Ring.mean: Mean get() = Mean(Int32Field) +public val Int64Ring.mean: Mean get() = Mean(Int64Field) diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt index 87046cd46..dc504f0b7 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Median.kt @@ -5,17 +5,34 @@ package space.kscience.kmath.stat -import space.kscience.kmath.operations.asSequence +import space.kscience.kmath.misc.sortedWith +import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer /** * Non-composable median */ -public class Median(private val comparator: Comparator) : BlockingStatistic { - override fun evaluateBlocking(data: Buffer): T = - data.asSequence().sortedWith(comparator).toList()[data.size / 2] //TODO check if this is correct +public class Median(private val field: Field, private val comparator: Comparator) : BlockingStatistic { + + override fun evaluateBlocking(data: Buffer): T = when { + data.size == 0 -> error("Can't compute median of an empty buffer") + data.size == 1 -> data[0] + data.size % 2 == 0 -> with(field) { + val sorted = data.sortedWith(comparator) + (sorted[data.size / 2 - 1] + sorted[data.size / 2]) / 2 + } + + else -> data.sortedWith(comparator)[(data.size - 1) / 2] + } public companion object { - public val real: Median = Median { a: Double, b: Double -> a.compareTo(b) } + + public fun evaluate(buffer: Buffer): Double = Float64Field.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Int = Int32Ring.mean.evaluateBlocking(buffer) + public fun evaluate(buffer: Buffer): Long = Int64Ring.mean.evaluateBlocking(buffer) } -} \ No newline at end of file +} + +public val Float64Field.median: Median get() = Median(Float64Field) { a, b -> a.compareTo(b) } +public val Int32Ring.median: Median get() = Median(Int32Field) { a, b -> a.compareTo(b) } +public val Int64Ring.median: Median get() = Median(Int64Field) { a, b -> a.compareTo(b) } \ No newline at end of file diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Statistic.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Statistic.kt index d7638ff81..7cb40a91a 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Statistic.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/stat/Statistic.kt @@ -15,7 +15,7 @@ import space.kscience.kmath.coroutines.mapParallel import space.kscience.kmath.structures.Buffer /** - * A function, that transforms a buffer of random quantities to some resulting value + * A function that transforms a buffer of random quantities to some resulting value */ public fun interface Statistic { public suspend fun evaluate(data: Buffer): R @@ -67,7 +67,7 @@ private fun ComposableStatistic.flowIntermediate( /** - * Perform a streaming statistical analysis on a chunked data. The computation of inner representation is done in parallel + * Perform a streaming statistical analysis on chunked data. The computation of inner representation is done in parallel * if [dispatcher] allows it. * * The resulting flow contains values that include the whole previous statistics, not only the last chunk. diff --git a/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/stat/TestBasicStatistics.kt b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/stat/TestBasicStatistics.kt new file mode 100644 index 000000000..306574a76 --- /dev/null +++ b/kmath-stat/src/commonTest/kotlin/space/kscience/kmath/stat/TestBasicStatistics.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2018-2023 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.stat + +import space.kscience.kmath.UnstableKMathAPI +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.random.RandomGenerator +import space.kscience.kmath.structures.slice +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(UnstableKMathAPI::class) +class TestBasicStatistics { + companion object { + val float64Sample = RandomGenerator.default(123).nextDoubleBuffer(100) + } + + @Test + fun medianFloat64() { + assertEquals(0.508, Float64Field.median(float64Sample), 0.0005) + assertEquals(0.5055, Float64Field.median(float64Sample.slice { 0..(graph), PowerOperations> { +) : TensorFlowAlgebra(graph), PowerOperations> { - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field override fun structureND( shape: ShapeND, - initializer: DoubleField.(IntArray) -> Double, + initializer: Float64Field.(IntArray) -> Double, ): StructureND { val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array -> ColumnStrides(shape).forEach { index -> @@ -83,7 +83,7 @@ public class DoubleTensorFlowAlgebra internal constructor( * The resulting tensor is available outside of scope */ @UnstableKMathAPI -public fun DoubleField.produceWithTF( +public fun Float64Field.produceWithTF( block: DoubleTensorFlowAlgebra.() -> StructureND, ): StructureND = Graph().use { graph -> val scope = DoubleTensorFlowAlgebra(graph) @@ -96,7 +96,7 @@ public fun DoubleField.produceWithTF( * The resulting tensors are available outside of scope */ @OptIn(UnstableKMathAPI::class) -public fun DoubleField.produceMapWithTF( +public fun Float64Field.produceMapWithTF( block: DoubleTensorFlowAlgebra.() -> Map>, ): Map> = Graph().use { graph -> val scope = DoubleTensorFlowAlgebra(graph) diff --git a/kmath-tensorflow/src/test/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowOps.kt b/kmath-tensorflow/src/test/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowOps.kt index 730feede6..fe950f334 100644 --- a/kmath-tensorflow/src/test/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowOps.kt +++ b/kmath-tensorflow/src/test/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowOps.kt @@ -10,7 +10,7 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.get import space.kscience.kmath.nd.structureND -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.tensors.core.DoubleTensorAlgebra import space.kscience.kmath.tensors.core.DoubleTensorAlgebra.Companion.sum import space.kscience.kmath.tensors.core.randomNormal @@ -20,7 +20,7 @@ import kotlin.test.assertEquals class DoubleTensorFlowOps { @Test fun basicOps() { - val res = DoubleField.produceWithTF { + val res = Float64Field.produceWithTF { val initial = structureND(2, 2) { 1.0 } initial + (initial * 2.0) @@ -36,14 +36,14 @@ class DoubleTensorFlowOps { val tensor1 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12224) val tensor2 = DoubleTensorAlgebra.randomNormal(shape = ShapeND(dim, dim), 12225) - DoubleField.produceWithTF { + Float64Field.produceWithTF { tensor1 dot tensor2 }.sum() } @Test fun extensionOps(){ - val res = DoubleField.produceWithTF { + val res = Float64Field.produceWithTF { val i = structureND(2, 2) { 0.5 } sin(i).pow(2) + cos(i).pow(2) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index c8cf56888..6bcf2753f 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -11,25 +11,28 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.DoubleBufferOps -import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra import space.kscience.kmath.tensors.api.LinearOpsTensorAlgebra import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.* -import kotlin.math.* +import kotlin.math.abs +import kotlin.math.ceil +import kotlin.math.floor +import kotlin.math.sqrt /** * Implementation of basic operations over double tensors and basic algebra operations on them. */ @OptIn(PerformancePitfall::class) public open class DoubleTensorAlgebra : - AnalyticTensorAlgebra, - LinearOpsTensorAlgebra { + AnalyticTensorAlgebra, + LinearOpsTensorAlgebra { public companion object : DoubleTensorAlgebra() - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field public val bufferAlgebra: DoubleBufferOps get() = DoubleBufferOps @@ -41,10 +44,10 @@ public open class DoubleTensorAlgebra : * @return the resulting tensor after applying the function. */ @Suppress("OVERRIDE_BY_INLINE") - final override inline fun StructureND.map(transform: DoubleField.(Double) -> Double): DoubleTensor { + final override inline fun StructureND.map(transform: Float64Field.(Double) -> Double): DoubleTensor { val tensor = asDoubleTensor() //TODO remove additional copy - val array = DoubleBuffer(tensor.source.size) { DoubleField.transform(tensor.source[it]) } + val array = DoubleBuffer(tensor.source.size) { Float64Field.transform(tensor.source[it]) } return DoubleTensor( tensor.shape, array, @@ -59,12 +62,12 @@ public open class DoubleTensorAlgebra : } } - public inline fun Tensor.mapIndexedInPlace(operation: DoubleField.(IntArray, Double) -> Double) { - indices.forEach { set(it, DoubleField.operation(it, get(it))) } + public inline fun Tensor.mapIndexedInPlace(operation: Float64Field.(IntArray, Double) -> Double) { + indices.forEach { set(it, Float64Field.operation(it, get(it))) } } @Suppress("OVERRIDE_BY_INLINE") - final override inline fun StructureND.mapIndexed(transform: DoubleField.(index: IntArray, Double) -> Double): DoubleTensor { + final override inline fun StructureND.mapIndexed(transform: Float64Field.(index: IntArray, Double) -> Double): DoubleTensor { return copyToTensor().apply { mapIndexedInPlace(transform) } } @@ -73,14 +76,14 @@ public open class DoubleTensorAlgebra : final override inline fun zip( left: StructureND, right: StructureND, - transform: DoubleField.(Double, Double) -> Double, + transform: Float64Field.(Double, Double) -> Double, ): DoubleTensor { checkShapesCompatible(left, right) val leftTensor = left.asDoubleTensor() val rightTensor = right.asDoubleTensor() val buffer = DoubleBuffer(leftTensor.source.size) { - DoubleField.transform(leftTensor.source[it], rightTensor.source[it]) + Float64Field.transform(leftTensor.source[it], rightTensor.source[it]) } return DoubleTensor(leftTensor.shape, buffer) } @@ -124,9 +127,9 @@ public open class DoubleTensorAlgebra : * @param initializer mapping tensor indices to values. * @return tensor with the [shape] shape and data generated by the [initializer]. */ - override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): DoubleTensor = fromArray( + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): DoubleTensor = fromArray( shape, - RowStrides(shape).asSequence().map { DoubleField.initializer(it) }.toMutableList().toDoubleArray() + RowStrides(shape).asSequence().map { Float64Field.initializer(it) }.toMutableList().toDoubleArray() ) override fun Tensor.getTensor(i: Int): DoubleTensor { @@ -717,4 +720,4 @@ public open class DoubleTensorAlgebra : } public val Double.Companion.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra -public val DoubleField.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra \ No newline at end of file +public val Float64Field.tensorAlgebra: DoubleTensorAlgebra get() = DoubleTensorAlgebra \ No newline at end of file diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt index d1cdc68d4..a7899e84c 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt @@ -10,7 +10,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.IntRing +import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.api.* import space.kscience.kmath.tensors.core.internal.* @@ -19,12 +19,12 @@ import kotlin.math.* /** * Implementation of basic operations over double tensors and basic algebra operations on them. */ -public open class IntTensorAlgebra : TensorAlgebra { +public open class IntTensorAlgebra : TensorAlgebra { public companion object : IntTensorAlgebra() - override val elementAlgebra: IntRing get() = IntRing + override val elementAlgebra: Int32Ring get() = Int32Ring /** @@ -34,10 +34,10 @@ public open class IntTensorAlgebra : TensorAlgebra { * @return the resulting tensor after applying the function. */ @Suppress("OVERRIDE_BY_INLINE") - final override inline fun StructureND.map(transform: IntRing.(Int) -> Int): IntTensor { + final override inline fun StructureND.map(transform: Int32Ring.(Int) -> Int): IntTensor { val tensor = this.asIntTensor() //TODO remove additional copy - val array = IntBuffer(tensor.source.size) { IntRing.transform(tensor.source[it]) } + val array = IntBuffer(tensor.source.size) { Int32Ring.transform(tensor.source[it]) } return IntTensor( tensor.shape, array, @@ -57,11 +57,11 @@ public open class IntTensorAlgebra : TensorAlgebra { } @Suppress("OVERRIDE_BY_INLINE") - final override inline fun StructureND.mapIndexed(transform: IntRing.(index: IntArray, Int) -> Int): IntTensor { + final override inline fun StructureND.mapIndexed(transform: Int32Ring.(index: IntArray, Int) -> Int): IntTensor { val tensor = this.asIntTensor() //TODO remove additional copy val buffer = IntBuffer(tensor.source.size) { - IntRing.transform(tensor.indices.index(it), tensor.source[it]) + Int32Ring.transform(tensor.indices.index(it), tensor.source[it]) } return IntTensor(tensor.shape, buffer) } @@ -70,14 +70,14 @@ public open class IntTensorAlgebra : TensorAlgebra { final override inline fun zip( left: StructureND, right: StructureND, - transform: IntRing.(Int, Int) -> Int, + transform: Int32Ring.(Int, Int) -> Int, ): IntTensor { checkShapesCompatible(left, right) val leftTensor = left.asIntTensor() val rightTensor = right.asIntTensor() val buffer = IntBuffer(leftTensor.source.size) { - IntRing.transform(leftTensor.source[it], rightTensor.source[it]) + Int32Ring.transform(leftTensor.source[it], rightTensor.source[it]) } return IntTensor(leftTensor.shape, buffer) } @@ -118,9 +118,9 @@ public open class IntTensorAlgebra : TensorAlgebra { * @param initializer mapping tensor indices to values. * @return tensor with the [shape] shape and data generated by the [initializer]. */ - override fun structureND(shape: ShapeND, initializer: IntRing.(IntArray) -> Int): IntTensor = fromArray( + override fun structureND(shape: ShapeND, initializer: Int32Ring.(IntArray) -> Int): IntTensor = fromArray( shape, - RowStrides(shape).asSequence().map { IntRing.initializer(it) }.toMutableList().toIntArray() + RowStrides(shape).asSequence().map { Int32Ring.initializer(it) }.toMutableList().toIntArray() ) override fun Tensor.getTensor(i: Int): IntTensor { @@ -462,6 +462,6 @@ public open class IntTensorAlgebra : TensorAlgebra { } public val Int.Companion.tensorAlgebra: IntTensorAlgebra get() = IntTensorAlgebra -public val IntRing.tensorAlgebra: IntTensorAlgebra get() = IntTensorAlgebra +public val Int32Ring.tensorAlgebra: IntTensorAlgebra get() = IntTensorAlgebra diff --git a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt index 8c7d6d199..f93066423 100644 --- a/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt +++ b/kmath-viktor/src/main/kotlin/space/kscience/kmath/viktor/ViktorFieldOpsND.kt @@ -12,15 +12,15 @@ import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnsafeKMathAPI import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.ExtendedFieldOps +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.NumbersAddOps import space.kscience.kmath.operations.PowerOperations @OptIn(UnstableKMathAPI::class, PerformancePitfall::class) @Suppress("OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") public open class ViktorFieldOpsND : - FieldOpsND, + FieldOpsND, ExtendedFieldOps>, PowerOperations> { @@ -30,13 +30,13 @@ public open class ViktorFieldOpsND : else -> structureND(shape) { this@f64Buffer[it] }.f64Buffer } - override val elementAlgebra: DoubleField get() = DoubleField + override val elementAlgebra: Float64Field get() = Float64Field @OptIn(UnsafeKMathAPI::class) - override fun structureND(shape: ShapeND, initializer: DoubleField.(IntArray) -> Double): ViktorStructureND = + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): ViktorStructureND = F64Array(*shape.asArray()).apply { ColumnStrides(shape).asSequence().forEach { index -> - set(value = DoubleField.initializer(index), indices = index) + set(value = Float64Field.initializer(index), indices = index) } }.asStructure() @@ -44,20 +44,20 @@ public open class ViktorFieldOpsND : @OptIn(UnsafeKMathAPI::class) @PerformancePitfall - override fun StructureND.map(transform: DoubleField.(Double) -> Double): ViktorStructureND = + override fun StructureND.map(transform: Float64Field.(Double) -> Double): ViktorStructureND = F64Array(*shape.asArray()).apply { ColumnStrides(ShapeND(shape)).asSequence().forEach { index -> - set(value = DoubleField.transform(this@map[index]), indices = index) + set(value = Float64Field.transform(this@map[index]), indices = index) } }.asStructure() @OptIn(UnsafeKMathAPI::class) @PerformancePitfall override fun StructureND.mapIndexed( - transform: DoubleField.(index: IntArray, Double) -> Double, + transform: Float64Field.(index: IntArray, Double) -> Double, ): ViktorStructureND = F64Array(*shape.asArray()).apply { ColumnStrides(ShapeND(shape)).asSequence().forEach { index -> - set(value = DoubleField.transform(index, this@mapIndexed[index]), indices = index) + set(value = Float64Field.transform(index, this@mapIndexed[index]), indices = index) } }.asStructure() @@ -66,12 +66,12 @@ public open class ViktorFieldOpsND : override fun zip( left: StructureND, right: StructureND, - transform: DoubleField.(Double, Double) -> Double, + transform: Float64Field.(Double, Double) -> Double, ): ViktorStructureND { require(left.shape.contentEquals(right.shape)) return F64Array(*left.shape.asArray()).apply { ColumnStrides(left.shape).asSequence().forEach { index -> - set(value = DoubleField.transform(left[index], right[index]), indices = index) + set(value = Float64Field.transform(left[index], right[index]), indices = index) } }.asStructure() } @@ -120,12 +120,12 @@ public open class ViktorFieldOpsND : public companion object : ViktorFieldOpsND() } -public val DoubleField.viktorAlgebra: ViktorFieldOpsND get() = ViktorFieldOpsND +public val Float64Field.viktorAlgebra: ViktorFieldOpsND get() = ViktorFieldOpsND @OptIn(UnstableKMathAPI::class) public open class ViktorFieldND( private val shapeAsArray: IntArray, -) : ViktorFieldOpsND(), FieldND, NumbersAddOps> { +) : ViktorFieldOpsND(), FieldND, NumbersAddOps> { override val shape: ShapeND = ShapeND(shapeAsArray) @@ -137,6 +137,6 @@ public open class ViktorFieldND( F64Array.full(init = value.toDouble(), shape = shapeAsArray).asStructure() } -public fun DoubleField.viktorAlgebra(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) +public fun Float64Field.viktorAlgebra(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) public fun ViktorFieldND(vararg shape: Int): ViktorFieldND = ViktorFieldND(shape) \ No newline at end of file From 19bebfd1ede651aed14e44b8e7326da7c8d9a043 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 12 Aug 2023 11:21:59 +0300 Subject: [PATCH 32/34] Finish naming change --- CHANGELOG.md | 4 + .../kmath/benchmarks/BufferBenchmark.kt | 8 +- build.gradle.kts | 2 +- .../kmath/ejml/codegen/ejmlCodegen.kt | 14 +- .../kscience/kmath/functions/interpolate.kt | 4 +- .../space/kscience/kmath/linear/gradient.kt | 6 +- .../kmath/operations/mixedNDOperations.kt | 4 +- .../kmath/structures/StreamDoubleFieldND.kt | 6 +- .../structures/StructureReadBenchmark.kt | 2 +- .../structures/StructureWriteBenchmark.kt | 4 +- .../space/kscience/kmath/ast/TestFolding.kt | 4 +- .../kscience/kmath/commons/linear/CMMatrix.kt | 4 +- .../commons/transform/Transformations.kt | 4 +- .../commons/optimization/OptimizeTest.kt | 6 +- .../space/kscience/kmath/domains/Domain1D.kt | 2 +- .../{DoubleDomain.kt => Float64Domain.kt} | 2 +- .../kmath/domains/HyperSquareDomain.kt | 6 +- .../kmath/domains/UnconstrainedDomain.kt | 2 +- .../kmath/expressions/SymbolIndexer.kt | 6 +- ...leLinearSpace.kt => Float64LinearSpace.kt} | 38 ++-- .../kscience/kmath/linear/LupDecomposition.kt | 6 +- .../{DoubleFieldND.kt => Float64FieldND.kt} | 130 +++++------ .../nd/{ShortRingND.kt => Int16RingND.kt} | 12 +- .../space/kscience/kmath/nd/IntRingND.kt | 6 +- .../kmath/operations/DoubleBufferField.kt | 43 ---- .../kmath/operations/Float64BufferField.kt | 43 ++++ ...DoubleBufferOps.kt => Float64BufferOps.kt} | 100 ++++----- .../kmath/operations/integerFields.kt | 6 +- .../kscience/kmath/operations/numbers.kt | 212 +++++++++--------- .../space/kscience/kmath/structures/Buffer.kt | 8 +- .../{FloatBuffer.kt => Float32Buffer.kt} | 20 +- .../{DoubleBuffer.kt => Float64Buffer.kt} | 36 +-- .../{ShortBuffer.kt => Int16Buffer.kt} | 20 +- .../{IntBuffer.kt => Int32Buffer.kt} | 20 +- .../{LongBuffer.kt => Int64Buffer.kt} | 20 +- .../{ByteBuffer.kt => Int8Buffer.kt} | 18 +- .../kmath/structures/MutableBuffer.kt | 34 +-- .../kmath/structures/bufferPrimitiveAccess.kt | 4 +- .../space/kscience/kmath/misc/PermSortTest.kt | 6 +- .../kmath/chains/BlockingDoubleChain.kt | 8 +- .../kscience/kmath/chains/BlockingIntChain.kt | 4 +- .../kscience/kmath/streaming/BufferFlow.kt | 8 +- .../space/kscience/kmath/real/DoubleVector.kt | 4 +- .../space/kscience/kmath/real/RealMatrix.kt | 10 +- .../kotlin/space/kscience/kmath/real/grids.kt | 16 +- .../space/kscience/kmath/real/realND.kt | 4 +- .../kscience/kmath/real/DoubleVectorTest.kt | 12 +- .../integration/GaussIntegratorRuleFactory.kt | 10 +- .../kmath/integration/SplineIntegrator.kt | 10 +- .../kmath/integration/UnivariateIntegrand.kt | 4 +- .../kmath/interpolation/SplineInterpolator.kt | 4 +- .../kscience/kmath/geometry/RotationTest.kt | 8 +- .../kscience/kmath/histogram/Histogram.kt | 6 +- .../histogram/UniformHistogramGroupND.kt | 6 +- .../kmath/optimization/QowOptimizer.kt | 14 +- .../kscience/kmath/random/RandomChain.kt | 6 +- .../kscience/kmath/random/RandomGenerator.kt | 4 +- .../AhrensDieterExponentialSampler.kt | 4 +- .../kmath/samplers/BoxMullerSampler.kt | 6 +- .../samplers/KempSmallMeanPoissonSampler.kt | 4 +- .../MarsagliaNormalizedGaussianSampler.kt | 4 +- .../kscience/kmath/samplers/PoissonSampler.kt | 6 +- .../space/kscience/kmath/samplers/Sampler.kt | 8 +- .../ZigguratNormalizedGaussianSampler.kt | 4 +- .../core/BroadcastDoubleTensorAlgebra.kt | 10 +- .../kmath/tensors/core/DoubleTensor.kt | 14 +- .../kmath/tensors/core/DoubleTensorAlgebra.kt | 24 +- .../kscience/kmath/tensors/core/IntTensor.kt | 14 +- .../kmath/tensors/core/IntTensorAlgebra.kt | 16 +- .../core/internal/doubleTensorHelpers.kt | 22 +- .../tensors/core/internal/intTensorHelpers.kt | 4 +- .../kmath/tensors/core/internal/linUtils.kt | 10 +- .../kmath/tensors/core/internal/utils.kt | 10 +- .../kscience/kmath/tensors/core/tensorOps.kt | 10 +- .../kmath/tensors/core/tensorTransform.kt | 8 +- .../kmath/tensors/core/TestDoubleTensor.kt | 10 +- .../tensors/core/offsetBufferEquality.kt | 4 +- .../kmath/tensors/core/TestLmAlgorithm.kt | 4 +- .../src/commonMain/kotlin/bufferEquality.kt | 10 +- 79 files changed, 619 insertions(+), 597 deletions(-) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/{DoubleDomain.kt => Float64Domain.kt} (92%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/{DoubleLinearSpace.kt => Float64LinearSpace.kt} (71%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/{DoubleFieldND.kt => Float64FieldND.kt} (57%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/{ShortRingND.kt => Int16RingND.kt} (76%) delete mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt create mode 100644 kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/{DoubleBufferOps.kt => Float64BufferOps.kt} (54%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/{FloatBuffer.kt => Float32Buffer.kt} (62%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/{DoubleBuffer.kt => Float64Buffer.kt} (50%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/{ShortBuffer.kt => Int16Buffer.kt} (65%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/{IntBuffer.kt => Int32Buffer.kt} (61%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/{LongBuffer.kt => Int64Buffer.kt} (63%) rename kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/{ByteBuffer.kt => Int8Buffer.kt} (67%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f011881f..9235cfd57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,18 @@ ## Unreleased ### Added +- Integer divistion algebras ### Changed +- Default naming for algebra and buffers now uses IntXX/FloatXX notation instead of Java types. +- Remove unnecessary inlines in basic algebras. ### Deprecated ### Removed ### Fixed +- Median statistics ### Security diff --git a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt index c2616303b..1675216eb 100644 --- a/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt +++ b/benchmarks/src/jvmMain/kotlin/space/kscience/kmath/benchmarks/BufferBenchmark.kt @@ -14,7 +14,7 @@ import space.kscience.kmath.complex.ComplexField import space.kscience.kmath.complex.complex import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.getDouble import space.kscience.kmath.structures.permute @@ -33,7 +33,7 @@ internal class BufferBenchmark { @Benchmark fun doubleBufferReadWrite(blackhole: Blackhole) { - val buffer = DoubleBuffer(size) { it.toDouble() } + val buffer = Float64Buffer(size) { it.toDouble() } var res = 0.0 (0 until size).forEach { res += buffer[it] @@ -43,7 +43,7 @@ internal class BufferBenchmark { @Benchmark fun bufferViewReadWrite(blackhole: Blackhole) { - val buffer = DoubleBuffer(size) { it.toDouble() }.permute(reversedIndices) + val buffer = Float64Buffer(size) { it.toDouble() }.permute(reversedIndices) var res = 0.0 (0 until size).forEach { res += buffer[it] @@ -53,7 +53,7 @@ internal class BufferBenchmark { @Benchmark fun bufferViewReadWriteSpecialized(blackhole: Blackhole) { - val buffer = DoubleBuffer(size) { it.toDouble() }.permute(reversedIndices) + val buffer = Float64Buffer(size) { it.toDouble() }.permute(reversedIndices) var res = 0.0 (0 until size).forEach { res += buffer.getDouble(it) diff --git a/build.gradle.kts b/build.gradle.kts index 7dbe87445..e2c5fc44f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ allprojects { } group = "space.kscience" - version = "0.3.2-dev-1" + version = "0.4.0-dev-2" } subprojects { diff --git a/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt b/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt index d973ebae4..41b88f093 100644 --- a/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt +++ b/buildSrc/src/main/kotlin/space/kscience/kmath/ejml/codegen/ejmlCodegen.kt @@ -387,9 +387,15 @@ import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature +import space.kscience.kmath.structures.Float64 +import space.kscience.kmath.structures.Float32 +import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Float32Field import space.kscience.kmath.operations.DoubleField import space.kscience.kmath.operations.FloatField import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64Buffer +import space.kscience.kmath.structures.Float32Buffer import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.FloatBuffer import kotlin.reflect.KClass @@ -399,12 +405,12 @@ import kotlin.reflect.cast""") it.appendEjmlVector("Float", "FMatrix") it.appendEjmlMatrix("Double", "DMatrix") it.appendEjmlMatrix("Float", "FMatrix") - it.appendEjmlLinearSpace("Double", "DoubleField", "DMatrix", "DMatrixRMaj", "DMatrixRMaj", "DDRM", "DDRM", true) - it.appendEjmlLinearSpace("Float", "FloatField", "FMatrix", "FMatrixRMaj", "FMatrixRMaj", "FDRM", "FDRM", true) + it.appendEjmlLinearSpace("Double", "Float64Field", "DMatrix", "DMatrixRMaj", "DMatrixRMaj", "DDRM", "DDRM", true) + it.appendEjmlLinearSpace("Float", "Float32Field", "FMatrix", "FMatrixRMaj", "FMatrixRMaj", "FDRM", "FDRM", true) it.appendEjmlLinearSpace( type = "Double", - kmathAlgebra = "DoubleField", + kmathAlgebra = "Float64Field", ejmlMatrixParentTypeMatrix = "DMatrix", ejmlMatrixType = "DMatrixSparseCSC", ejmlMatrixDenseType = "DMatrixRMaj", @@ -415,7 +421,7 @@ import kotlin.reflect.cast""") it.appendEjmlLinearSpace( type = "Float", - kmathAlgebra = "FloatField", + kmathAlgebra = "Float32Field", ejmlMatrixParentTypeMatrix = "FMatrix", ejmlMatrixType = "FMatrixSparseCSC", ejmlMatrixDenseType = "FMatrixRMaj", diff --git a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt index 9bde80a87..6908eebdd 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/functions/interpolate.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.functions import space.kscience.kmath.interpolation.SplineInterpolator import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.operations.Float64Field -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.plotly.Plotly import space.kscience.plotly.UnstablePlotlyAPI import space.kscience.plotly.makeFile @@ -25,7 +25,7 @@ fun main() { } val polynomial: PiecewisePolynomial = SplineInterpolator( - Float64Field, ::DoubleBuffer + Float64Field, ::Float64Buffer ).interpolatePolynomials(data) val function = polynomial.asFunction(Float64Field, 0.0) diff --git a/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt b/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt index 52ed8f05f..eb170e7fa 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/linear/gradient.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.linear import space.kscience.kmath.real.* -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer fun main() { val x0 = DoubleVector(0.0, 0.0, 0.0) @@ -19,9 +19,9 @@ fun main() { fun ((Point) -> Double).grad(x: Point): Point { require(x.size == x0.size) - return DoubleBuffer(x.size) { i -> + return Float64Buffer(x.size) { i -> val h = sigma[i] / 5 - val dVector = DoubleBuffer(x.size) { if (it == i) h else 0.0 } + val dVector = Float64Buffer(x.size) { if (it == i) h else 0.0 } val f1 = this(x + dVector / 2) val f0 = this(x - dVector / 2) (f1 - f0) / h diff --git a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt index 6094d4d4a..bb69c6aff 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/operations/mixedNDOperations.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.operations import space.kscience.kmath.commons.linear.CMLinearSpace import space.kscience.kmath.linear.matrix -import space.kscience.kmath.nd.DoubleBufferND +import space.kscience.kmath.nd.Float64BufferND import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.nd.ndAlgebra @@ -21,7 +21,7 @@ fun main() { val cmMatrix: Structure2D = CMLinearSpace.matrix(2, 2)(0.0, 1.0, 0.0, 3.0) - val res: DoubleBufferND = Float64Field.ndAlgebra { + val res: Float64BufferND = Float64Field.ndAlgebra { exp(viktorStructure) + 2.0 * cmMatrix } diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt index e46e50821..a0e25b81b 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StreamDoubleFieldND.kt @@ -32,15 +32,15 @@ class StreamDoubleFieldND(override val shape: ShapeND) : FieldND.buffer: DoubleBuffer + private val StructureND.buffer: Float64Buffer get() = when { !shape.contentEquals(this@StreamDoubleFieldND.shape) -> throw ShapeMismatchException( this@StreamDoubleFieldND.shape, shape ) - this is BufferND && indices == this@StreamDoubleFieldND.strides -> this.buffer as DoubleBuffer - else -> DoubleBuffer(strides.linearSize) { offset -> get(strides.index(offset)) } + this is BufferND && indices == this@StreamDoubleFieldND.strides -> this.buffer as Float64Buffer + else -> Float64Buffer(strides.linearSize) { offset -> get(strides.index(offset)) } } override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): BufferND { diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt index e6ff0ee28..1e50ef7da 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureReadBenchmark.kt @@ -16,7 +16,7 @@ import kotlin.system.measureTimeMillis fun main() { val n = 6000 val array = DoubleArray(n * n) { 1.0 } - val buffer = DoubleBuffer(array) + val buffer = Float64Buffer(array) val strides = ColumnStrides(ShapeND(n, n)) val structure = BufferND(strides, buffer) diff --git a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt index 14c058417..4aa7b74dd 100644 --- a/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt +++ b/examples/src/main/kotlin/space/kscience/kmath/structures/StructureWriteBenchmark.kt @@ -32,10 +32,10 @@ fun main() { println("Array mapping finished in $time2 millis") - val buffer = DoubleBuffer(DoubleArray(n * n) { 1.0 }) + val buffer = Float64Buffer(DoubleArray(n * n) { 1.0 }) val time3 = measureTimeMillis { - val target = DoubleBuffer(DoubleArray(n * n)) + val target = Float64Buffer(DoubleArray(n * n)) val res = array.forEachIndexed { index, value -> target[index] = value + 1 } diff --git a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt index d67d965ce..61a37944a 100644 --- a/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt +++ b/kmath-ast/src/commonTest/kotlin/space/kscience/kmath/ast/TestFolding.kt @@ -5,9 +5,9 @@ package space.kscience.kmath.ast -import space.kscience.kmath.operations.ByteRing import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Int32Ring +import space.kscience.kmath.operations.Int8Ring import space.kscience.kmath.operations.pi import kotlin.test.Test import kotlin.test.assertEquals @@ -47,6 +47,6 @@ internal class TestFolding { @Test fun foldNumeric() = assertEquals( 42.toByte(), - ("42".parseMath().evaluateConstants(ByteRing) as? TypedMst.Constant ?: fail()).value, + ("42".parseMath().evaluateConstants(Int8Ring) as? TypedMst.Constant ?: fail()).value, ) } diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt index 449aa79ce..4d2e2aa90 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/linear/CMMatrix.kt @@ -11,7 +11,7 @@ import space.kscience.kmath.linear.* import space.kscience.kmath.nd.StructureFeature import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.reflect.KClass import kotlin.reflect.cast @@ -136,7 +136,7 @@ public object CMLinearSpace : LinearSpace { override val u: Matrix by lazy { CMMatrix(sv.u) } override val s: Matrix by lazy { CMMatrix(sv.s) } override val v: Matrix by lazy { CMMatrix(sv.v) } - override val singularValues: Point by lazy { DoubleBuffer(sv.singularValues) } + override val singularValues: Point by lazy { Float64Buffer(sv.singularValues) } } else -> null }?.let(type::cast) diff --git a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt index a77da2d2f..de271aedc 100644 --- a/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt +++ b/kmath-commons/src/main/kotlin/space/kscience/kmath/commons/transform/Transformations.kt @@ -47,7 +47,7 @@ public object Transformations { public fun sine( normalization: DstNormalization = DstNormalization.STANDARD_DST_I, direction: TransformType = TransformType.FORWARD, - ): BufferTransform = DoubleBufferTransform { + ): BufferTransform = Float64BufferTransform { FastSineTransformer(normalization).transform(it.array, direction).asBuffer() } @@ -60,7 +60,7 @@ public object Transformations { public fun hadamard( direction: TransformType = TransformType.FORWARD, - ): BufferTransform = DoubleBufferTransform { + ): BufferTransform = Float64BufferTransform { FastHadamardTransformer().transform(it.array, direction).asBuffer() } } diff --git a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt index bf687aa72..5933d0d36 100644 --- a/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/space/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -13,12 +13,12 @@ import space.kscience.kmath.expressions.Symbol.Companion.x import space.kscience.kmath.expressions.Symbol.Companion.y import space.kscience.kmath.expressions.autodiff import space.kscience.kmath.expressions.symbol -import space.kscience.kmath.operations.DoubleBufferOps.Companion.map +import space.kscience.kmath.operations.Float64BufferOps.Companion.map import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.optimization.* import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.chiSquaredExpression -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import kotlin.test.Test @@ -61,7 +61,7 @@ internal class OptimizeTest { it.pow(2) + it + 1 + chain.next() } - val yErr = DoubleBuffer(x.size) { sigma } + val yErr = Float64Buffer(x.size) { sigma } val chi2 = Double.autodiff.chiSquaredExpression( x, y, yErr diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt index d619883b4..b03e60e95 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Domain1D.kt @@ -23,7 +23,7 @@ public abstract class Domain1D>(public val range: ClosedRange< @UnstableKMathAPI public class DoubleDomain1D( @Suppress("CanBeParameter") public val doubleRange: ClosedFloatingPointRange, -) : Domain1D(doubleRange), DoubleDomain { +) : Domain1D(doubleRange), Float64Domain { override fun getLowerBound(num: Int): Double { require(num == 0) return range.start diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt similarity index 92% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt index e56173624..7878f2551 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/DoubleDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/Float64Domain.kt @@ -12,7 +12,7 @@ import space.kscience.kmath.UnstableKMathAPI * @author Alexander Nozik */ @UnstableKMathAPI -public interface DoubleDomain : Domain { +public interface Float64Domain : Domain { /** * Global lower edge * @param num axis number diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt index 1049a251a..03a080a70 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/HyperSquareDomain.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.domains import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.indices /** @@ -15,7 +15,7 @@ import space.kscience.kmath.structures.indices * and a [Buffer] of upper boundaries. Upper should be greater or equals than lower. */ @UnstableKMathAPI -public class HyperSquareDomain(public val lower: Buffer, public val upper: Buffer) : DoubleDomain { +public class HyperSquareDomain(public val lower: Buffer, public val upper: Buffer) : Float64Domain { init { require(lower.size == upper.size) { "Domain borders size mismatch. Lower borders size is ${lower.size}, but upper borders size is ${upper.size}." @@ -27,7 +27,7 @@ public class HyperSquareDomain(public val lower: Buffer, public val uppe override val dimension: Int get() = lower.size - public val center: DoubleBuffer get() = DoubleBuffer(dimension) { (lower[it] + upper[it]) / 2.0 } + public val center: Float64Buffer get() = Float64Buffer(dimension) { (lower[it] + upper[it]) / 2.0 } override operator fun contains(point: Point): Boolean = point.indices.all { i -> point[i] in lower[i]..upper[i] diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt index 5351a295d..b78190c9b 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/domains/UnconstrainedDomain.kt @@ -8,7 +8,7 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point @UnstableKMathAPI -public class UnconstrainedDomain(override val dimension: Int) : DoubleDomain { +public class UnconstrainedDomain(override val dimension: Int) : Float64Domain { override operator fun contains(point: Point): Boolean = true override fun getLowerBound(num: Int): Double = Double.NEGATIVE_INFINITY diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt index 7112e921a..f0e39d489 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/expressions/SymbolIndexer.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point import space.kscience.kmath.nd.Structure2D import space.kscience.kmath.structures.BufferFactory -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.jvm.JvmInline @@ -62,8 +62,8 @@ public interface SymbolIndexer { public fun Map.toPoint(bufferFactory: BufferFactory): Point = bufferFactory(symbols.size) { getValue(symbols[it]) } - public fun Map.toPoint(): DoubleBuffer = - DoubleBuffer(symbols.size) { getValue(symbols[it]) } + public fun Map.toPoint(): Float64Buffer = + Float64Buffer(symbols.size) { getValue(symbols[it]) } public fun Map.toDoubleArray(): DoubleArray = DoubleArray(symbols.size) { getValue(symbols[it]) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt similarity index 71% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt index 4f631336c..eb4434c00 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/DoubleLinearSpace.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/Float64LinearSpace.kt @@ -7,13 +7,13 @@ package space.kscience.kmath.linear import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.DoubleBufferOps +import space.kscience.kmath.operations.Float64BufferOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer -public object DoubleLinearSpace : LinearSpace { +public object Float64LinearSpace : LinearSpace { override val elementAlgebra: Float64Field get() = Float64Field @@ -21,29 +21,29 @@ public object DoubleLinearSpace : LinearSpace { rows: Int, columns: Int, initializer: Float64Field.(i: Int, j: Int) -> Double - ): Matrix = DoubleFieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) -> + ): Matrix = Floa64FieldOpsND.structureND(ShapeND(rows, columns)) { (i, j) -> Float64Field.initializer(i, j) }.as2D() - override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): DoubleBuffer = - DoubleBuffer(size) { Float64Field.initializer(it) } + override fun buildVector(size: Int, initializer: Float64Field.(Int) -> Double): Float64Buffer = + Float64Buffer(size) { Float64Field.initializer(it) } - override fun Matrix.unaryMinus(): Matrix = DoubleFieldOpsND { + override fun Matrix.unaryMinus(): Matrix = Floa64FieldOpsND { asND().map { -it }.as2D() } - override fun Matrix.plus(other: Matrix): Matrix = DoubleFieldOpsND { + override fun Matrix.plus(other: Matrix): Matrix = Floa64FieldOpsND { require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::plus. Expected $shape but found ${other.shape}" } asND().plus(other.asND()).as2D() } - override fun Matrix.minus(other: Matrix): Matrix = DoubleFieldOpsND { + override fun Matrix.minus(other: Matrix): Matrix = Floa64FieldOpsND { require(shape.contentEquals(other.shape)) { "Shape mismatch on Matrix::minus. Expected $shape but found ${other.shape}" } asND().minus(other.asND()).as2D() } // Create a continuous in-memory representation of this vector for better memory layout handling - private fun Buffer.linearize() = if (this is DoubleBuffer) { + private fun Buffer.linearize() = if (this is Float64Buffer) { this.array } else { DoubleArray(size) { get(it) } @@ -66,10 +66,10 @@ public object DoubleLinearSpace : LinearSpace { } @OptIn(PerformancePitfall::class) - override fun Matrix.dot(vector: Point): DoubleBuffer { + override fun Matrix.dot(vector: Point): Float64Buffer { require(colNum == vector.size) { "Matrix dot vector operation dimension mismatch: ($rowNum, $colNum) x (${vector.size})" } val rows = this@dot.rows.map { it.linearize() } - return DoubleBuffer(rowNum) { i -> + return Float64Buffer(rowNum) { i -> val r = rows[i] var res = 0.0 for (j in r.indices) { @@ -80,29 +80,29 @@ public object DoubleLinearSpace : LinearSpace { } - override fun Matrix.times(value: Double): Matrix = DoubleFieldOpsND { + override fun Matrix.times(value: Double): Matrix = Floa64FieldOpsND { asND().map { it * value }.as2D() } - public override fun Point.plus(other: Point): DoubleBuffer = DoubleBufferOps.run { + public override fun Point.plus(other: Point): Float64Buffer = Float64BufferOps.run { this@plus + other } - public override fun Point.minus(other: Point): DoubleBuffer = DoubleBufferOps.run { + public override fun Point.minus(other: Point): Float64Buffer = Float64BufferOps.run { this@minus - other } - public override fun Point.times(value: Double): DoubleBuffer = DoubleBufferOps.run { + public override fun Point.times(value: Double): Float64Buffer = Float64BufferOps.run { scale(this@times, value) } - public operator fun Point.div(value: Double): DoubleBuffer = DoubleBufferOps.run { + public operator fun Point.div(value: Double): Float64Buffer = Float64BufferOps.run { scale(this@div, 1.0 / value) } - public override fun Double.times(v: Point): DoubleBuffer = v * this + public override fun Double.times(v: Point): Float64Buffer = v * this } -public val Float64Field.linearSpace: DoubleLinearSpace get() = DoubleLinearSpace +public val Float64Field.linearSpace: Float64LinearSpace get() = Float64LinearSpace diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt index 4bb67bce9..ce55623de 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/linear/LupDecomposition.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.linear import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.* import space.kscience.kmath.structures.BufferAccessor2D -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.MutableBuffer import space.kscience.kmath.structures.MutableBufferFactory @@ -159,7 +159,7 @@ public fun LinearSpace.lup( matrix: Matrix, singularityThreshold: Double = 1e-11, ): LupDecomposition = - lup(::DoubleBuffer, matrix) { it < singularityThreshold } + lup(::Float64Buffer, matrix) { it < singularityThreshold } internal fun LupDecomposition.solve( factory: MutableBufferFactory, @@ -227,4 +227,4 @@ public fun , F : Field> LinearSpace.lupSolver( } public fun LinearSpace.lupSolver(singularityThreshold: Double = 1e-11): LinearSolver = - lupSolver(::DoubleBuffer) { it < singularityThreshold } + lupSolver(::Float64Buffer) { it < singularityThreshold } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt similarity index 57% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt index 2f47f0b37..464ee16b9 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/DoubleFieldND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Float64FieldND.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.nd import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.* -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.math.pow @@ -17,9 +17,9 @@ import kotlin.math.pow as kpow /** * A simple mutable [StructureND] of doubles */ -public class DoubleBufferND( +public class Float64BufferND( indexes: ShapeIndexer, - override val buffer: DoubleBuffer, + override val buffer: Float64Buffer, ) : MutableBufferND(indexes, buffer), MutableStructureNDOfDouble{ override fun getDouble(index: IntArray): Double = buffer[indices.offset(index)] @@ -29,37 +29,37 @@ public class DoubleBufferND( } -public sealed class DoubleFieldOpsND : BufferedFieldOpsND(Float64Field.bufferAlgebra), +public sealed class Floa64FieldOpsND : BufferedFieldOpsND(Float64Field.bufferAlgebra), ScaleOperations>, ExtendedFieldOps> { @OptIn(PerformancePitfall::class) - override fun StructureND.toBufferND(): DoubleBufferND = when (this) { - is DoubleBufferND -> this + override fun StructureND.toBufferND(): Float64BufferND = when (this) { + is Float64BufferND -> this else -> { val indexer = indexerBuilder(shape) - DoubleBufferND(indexer, DoubleBuffer(indexer.linearSize) { offset -> get(indexer.index(offset)) }) + Float64BufferND(indexer, Float64Buffer(indexer.linearSize) { offset -> get(indexer.index(offset)) }) } } protected inline fun mapInline( - arg: DoubleBufferND, + arg: Float64BufferND, transform: (Double) -> Double, - ): DoubleBufferND { + ): Float64BufferND { val indexes = arg.indices val array = arg.buffer.array - return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { transform(array[it]) }) + return Float64BufferND(indexes, Float64Buffer(indexes.linearSize) { transform(array[it]) }) } private inline fun zipInline( - l: DoubleBufferND, - r: DoubleBufferND, + l: Float64BufferND, + r: Float64BufferND, block: (l: Double, r: Double) -> Double, - ): DoubleBufferND { + ): Float64BufferND { require(l.indices == r.indices) { "Zip requires the same shapes, but found ${l.shape} on the left and ${r.shape} on the right" } val indexes = l.indices val lArray = l.buffer.array val rArray = r.buffer.array - return DoubleBufferND(indexes, DoubleBuffer(indexes.linearSize) { block(lArray[it], rArray[it]) }) + return Float64BufferND(indexes, Float64Buffer(indexes.linearSize) { block(lArray[it], rArray[it]) }) } @OptIn(PerformancePitfall::class) @@ -74,56 +74,56 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND( transform: Float64Field.(Double, Double) -> Double, ): BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> Float64Field.transform(l, r) } - override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): DoubleBufferND { + override fun structureND(shape: ShapeND, initializer: Float64Field.(IntArray) -> Double): Float64BufferND { val indexer = indexerBuilder(shape) - return DoubleBufferND( + return Float64BufferND( indexer, - DoubleBuffer(indexer.linearSize) { offset -> + Float64Buffer(indexer.linearSize) { offset -> elementAlgebra.initializer(indexer.index(offset)) } ) } - override fun add(left: StructureND, right: StructureND): DoubleBufferND = + override fun add(left: StructureND, right: StructureND): Float64BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> l + r } - override fun multiply(left: StructureND, right: StructureND): DoubleBufferND = + override fun multiply(left: StructureND, right: StructureND): Float64BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l, r -> l * r } - override fun StructureND.unaryMinus(): DoubleBufferND = mapInline(toBufferND()) { -it } + override fun StructureND.unaryMinus(): Float64BufferND = mapInline(toBufferND()) { -it } - override fun StructureND.div(arg: StructureND): DoubleBufferND = + override fun StructureND.div(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l, r -> l / r } - override fun divide(left: StructureND, right: StructureND): DoubleBufferND = + override fun divide(left: StructureND, right: StructureND): Float64BufferND = zipInline(left.toBufferND(), right.toBufferND()) { l: Double, r: Double -> l / r } - override fun StructureND.div(arg: Double): DoubleBufferND = + override fun StructureND.div(arg: Double): Float64BufferND = mapInline(toBufferND()) { it / arg } - override fun Double.div(arg: StructureND): DoubleBufferND = + override fun Double.div(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { this / it } - override fun StructureND.unaryPlus(): DoubleBufferND = toBufferND() + override fun StructureND.unaryPlus(): Float64BufferND = toBufferND() - override fun StructureND.plus(arg: StructureND): DoubleBufferND = + override fun StructureND.plus(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l + r } - override fun StructureND.minus(arg: StructureND): DoubleBufferND = + override fun StructureND.minus(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l - r } - override fun StructureND.times(arg: StructureND): DoubleBufferND = + override fun StructureND.times(arg: StructureND): Float64BufferND = zipInline(toBufferND(), arg.toBufferND()) { l: Double, r: Double -> l * r } - override fun StructureND.times(k: Number): DoubleBufferND = + override fun StructureND.times(k: Number): Float64BufferND = mapInline(toBufferND()) { it * k.toDouble() } - override fun StructureND.div(k: Number): DoubleBufferND = + override fun StructureND.div(k: Number): Float64BufferND = mapInline(toBufferND()) { it / k.toDouble() } - override fun Number.times(arg: StructureND): DoubleBufferND = arg * this + override fun Number.times(arg: StructureND): Float64BufferND = arg * this - override fun StructureND.plus(arg: Double): DoubleBufferND = mapInline(toBufferND()) { it + arg } + override fun StructureND.plus(arg: Double): Float64BufferND = mapInline(toBufferND()) { it + arg } override fun StructureND.minus(arg: Double): StructureND = mapInline(toBufferND()) { it - arg } @@ -131,49 +131,49 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND( override fun Double.minus(arg: StructureND): StructureND = mapInline(arg.toBufferND()) { this - it } - override fun scale(a: StructureND, value: Double): DoubleBufferND = + override fun scale(a: StructureND, value: Double): Float64BufferND = mapInline(a.toBufferND()) { it * value } - override fun exp(arg: StructureND): DoubleBufferND = + override fun exp(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.exp(it) } - override fun ln(arg: StructureND): DoubleBufferND = + override fun ln(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.ln(it) } - override fun sin(arg: StructureND): DoubleBufferND = + override fun sin(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.sin(it) } - override fun cos(arg: StructureND): DoubleBufferND = + override fun cos(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.cos(it) } - override fun tan(arg: StructureND): DoubleBufferND = + override fun tan(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.tan(it) } - override fun asin(arg: StructureND): DoubleBufferND = + override fun asin(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.asin(it) } - override fun acos(arg: StructureND): DoubleBufferND = + override fun acos(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.acos(it) } - override fun atan(arg: StructureND): DoubleBufferND = + override fun atan(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.atan(it) } - override fun sinh(arg: StructureND): DoubleBufferND = + override fun sinh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.sinh(it) } - override fun cosh(arg: StructureND): DoubleBufferND = + override fun cosh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.cosh(it) } - override fun tanh(arg: StructureND): DoubleBufferND = + override fun tanh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.tanh(it) } - override fun asinh(arg: StructureND): DoubleBufferND = + override fun asinh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.asinh(it) } - override fun acosh(arg: StructureND): DoubleBufferND = + override fun acosh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.acosh(it) } - override fun atanh(arg: StructureND): DoubleBufferND = + override fun atanh(arg: StructureND): Float64BufferND = mapInline(arg.toBufferND()) { kotlin.math.atanh(it) } override fun power( @@ -185,23 +185,23 @@ public sealed class DoubleFieldOpsND : BufferedFieldOpsND( mapInline(arg.toBufferND()) { it.pow(pow.toDouble()) } } - public companion object : DoubleFieldOpsND() + public companion object : Floa64FieldOpsND() } @OptIn(UnstableKMathAPI::class) -public class DoubleFieldND(override val shape: ShapeND) : - DoubleFieldOpsND(), FieldND, NumbersAddOps>, +public class Float64FieldND(override val shape: ShapeND) : + Floa64FieldOpsND(), FieldND, NumbersAddOps>, ExtendedField> { - override fun power(arg: StructureND, pow: UInt): DoubleBufferND = mapInline(arg.toBufferND()) { + override fun power(arg: StructureND, pow: UInt): Float64BufferND = mapInline(arg.toBufferND()) { it.kpow(pow.toInt()) } - override fun power(arg: StructureND, pow: Int): DoubleBufferND = mapInline(arg.toBufferND()) { + override fun power(arg: StructureND, pow: Int): Float64BufferND = mapInline(arg.toBufferND()) { it.kpow(pow) } - override fun power(arg: StructureND, pow: Number): DoubleBufferND = if (pow.isInteger()) { + override fun power(arg: StructureND, pow: Number): Float64BufferND = if (pow.isInteger()) { power(arg, pow.toInt()) } else { val dpow = pow.toDouble() @@ -211,34 +211,34 @@ public class DoubleFieldND(override val shape: ShapeND) : } } - override fun sinh(arg: StructureND): DoubleBufferND = super.sinh(arg) + override fun sinh(arg: StructureND): Float64BufferND = super.sinh(arg) - override fun cosh(arg: StructureND): DoubleBufferND = super.cosh(arg) + override fun cosh(arg: StructureND): Float64BufferND = super.cosh(arg) - override fun tanh(arg: StructureND): DoubleBufferND = super.tan(arg) + override fun tanh(arg: StructureND): Float64BufferND = super.tan(arg) - override fun asinh(arg: StructureND): DoubleBufferND = super.asinh(arg) + override fun asinh(arg: StructureND): Float64BufferND = super.asinh(arg) - override fun acosh(arg: StructureND): DoubleBufferND = super.acosh(arg) + override fun acosh(arg: StructureND): Float64BufferND = super.acosh(arg) - override fun atanh(arg: StructureND): DoubleBufferND = super.atanh(arg) + override fun atanh(arg: StructureND): Float64BufferND = super.atanh(arg) - override fun number(value: Number): DoubleBufferND { + override fun number(value: Number): Float64BufferND { val d = value.toDouble() // minimize conversions return structureND(shape) { d } } } -public val Float64Field.ndAlgebra: DoubleFieldOpsND get() = DoubleFieldOpsND +public val Float64Field.ndAlgebra: Floa64FieldOpsND get() = Floa64FieldOpsND -public fun Float64Field.ndAlgebra(vararg shape: Int): DoubleFieldND = DoubleFieldND(ShapeND(shape)) -public fun Float64Field.ndAlgebra(shape: ShapeND): DoubleFieldND = DoubleFieldND(shape) +public fun Float64Field.ndAlgebra(vararg shape: Int): Float64FieldND = Float64FieldND(ShapeND(shape)) +public fun Float64Field.ndAlgebra(shape: ShapeND): Float64FieldND = Float64FieldND(shape) /** * Produce a context for n-dimensional operations inside this real field */ @UnstableKMathAPI -public inline fun Float64Field.withNdAlgebra(vararg shape: Int, action: DoubleFieldND.() -> R): R { +public inline fun Float64Field.withNdAlgebra(vararg shape: Int, action: Float64FieldND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } - return DoubleFieldND(ShapeND(shape)).run(action) + return Float64FieldND(ShapeND(shape)).run(action) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Int16RingND.kt similarity index 76% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Int16RingND.kt index 12f59dc72..758a486f5 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/ShortRingND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/Int16RingND.kt @@ -12,14 +12,14 @@ import space.kscience.kmath.operations.bufferAlgebra import kotlin.contracts.InvocationKind import kotlin.contracts.contract -public sealed class ShortRingOpsND : BufferedRingOpsND(Int16Ring.bufferAlgebra) { - public companion object : ShortRingOpsND() +public sealed class Int16RingOpsND : BufferedRingOpsND(Int16Ring.bufferAlgebra) { + public companion object : Int16RingOpsND() } @OptIn(UnstableKMathAPI::class) -public class ShortRingND( +public class Int16RingND( override val shape: ShapeND -) : ShortRingOpsND(), RingND, NumbersAddOps> { +) : Int16RingOpsND(), RingND, NumbersAddOps> { override fun number(value: Number): BufferND { val short @@ -28,7 +28,7 @@ public class ShortRingND( } } -public inline fun Int16Ring.withNdAlgebra(vararg shape: Int, action: ShortRingND.() -> R): R { +public inline fun Int16Ring.withNdAlgebra(vararg shape: Int, action: Int16RingND.() -> R): R { contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) } - return ShortRingND(ShapeND(shape)).run(action) + return Int16RingND(ShapeND(shape)).run(action) } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt index 4ce2b80e6..039432bc7 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/nd/IntRingND.kt @@ -9,13 +9,13 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.operations.Int32Ring import space.kscience.kmath.operations.NumbersAddOps import space.kscience.kmath.operations.bufferAlgebra -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Int32Buffer import kotlin.contracts.InvocationKind import kotlin.contracts.contract public class IntBufferND( indexes: ShapeIndexer, - override val buffer: IntBuffer, + override val buffer: Int32Buffer, ) : MutableBufferND(indexes, buffer) public sealed class IntRingOpsND : BufferedRingOpsND(Int32Ring.bufferAlgebra) { @@ -24,7 +24,7 @@ public sealed class IntRingOpsND : BufferedRingOpsND(Int32Ring.b val indexer = indexerBuilder(shape) return IntBufferND( indexer, - IntBuffer(indexer.linearSize) { offset -> + Int32Buffer(indexer.linearSize) { offset -> elementAlgebra.initializer(indexer.index(offset)) } ) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt deleted file mode 100644 index 2e6b63a92..000000000 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferField.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018-2022 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.operations - -import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer - -/** - * [ExtendedField] over [DoubleBuffer]. - * - * @property size the size of buffers to operate on. - */ -public class DoubleBufferField(public val size: Int) : ExtendedField>, DoubleBufferOps() { - override val zero: Buffer by lazy { DoubleBuffer(size) { 0.0 } } - override val one: Buffer by lazy { DoubleBuffer(size) { 1.0 } } - - override fun sinh(arg: Buffer): DoubleBuffer = super.sinh(arg) - - override fun cosh(arg: Buffer): DoubleBuffer = super.cosh(arg) - - override fun tanh(arg: Buffer): DoubleBuffer = super.tanh(arg) - - override fun asinh(arg: Buffer): DoubleBuffer = super.asinh(arg) - - override fun acosh(arg: Buffer): DoubleBuffer = super.acosh(arg) - - override fun atanh(arg: Buffer): DoubleBuffer = super.atanh(arg) - - override fun power(arg: Buffer, pow: Number): DoubleBuffer = if (pow.isInteger()) { - arg.map { it.pow(pow.toInt()) } - } else { - arg.map { - if(it<0) throw IllegalArgumentException("Negative argument $it could not be raised to the fractional power") - it.pow(pow.toDouble()) - } - } - - override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = - super.unaryOperationFunction(operation) -} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt new file mode 100644 index 000000000..e33cb2c6e --- /dev/null +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferField.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2018-2022 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.operations + +import space.kscience.kmath.structures.Buffer +import space.kscience.kmath.structures.Float64Buffer + +/** + * [ExtendedField] over [Float64Buffer]. + * + * @property size the size of buffers to operate on. + */ +public class Float64BufferField(public val size: Int) : ExtendedField>, Float64BufferOps() { + override val zero: Buffer by lazy { Float64Buffer(size) { 0.0 } } + override val one: Buffer by lazy { Float64Buffer(size) { 1.0 } } + + override fun sinh(arg: Buffer): Float64Buffer = super.sinh(arg) + + override fun cosh(arg: Buffer): Float64Buffer = super.cosh(arg) + + override fun tanh(arg: Buffer): Float64Buffer = super.tanh(arg) + + override fun asinh(arg: Buffer): Float64Buffer = super.asinh(arg) + + override fun acosh(arg: Buffer): Float64Buffer = super.acosh(arg) + + override fun atanh(arg: Buffer): Float64Buffer = super.atanh(arg) + + override fun power(arg: Buffer, pow: Number): Float64Buffer = if (pow.isInteger()) { + arg.map { it.pow(pow.toInt()) } + } else { + arg.map { + if(it<0) throw IllegalArgumentException("Negative argument $it could not be raised to the fractional power") + it.pow(pow.toDouble()) + } + } + + override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = + super.unaryOperationFunction(operation) +} \ No newline at end of file diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt similarity index 54% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt index e15263bef..923534e1c 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/DoubleBufferOps.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Float64BufferOps.kt @@ -12,9 +12,9 @@ import kotlin.math.pow import kotlin.math.sqrt /** - * [ExtendedFieldOps] over [DoubleBuffer]. + * [ExtendedFieldOps] over [Float64Buffer]. */ -public abstract class DoubleBufferOps : BufferAlgebra, ExtendedFieldOps>, +public abstract class Float64BufferOps : BufferAlgebra, ExtendedFieldOps>, Norm, Double> { override val elementAlgebra: Float64Field get() = Float64Field @@ -23,23 +23,23 @@ public abstract class DoubleBufferOps : BufferAlgebra, Ext @Suppress("OVERRIDE_BY_INLINE") @OptIn(UnstableKMathAPI::class) - final override inline fun Buffer.map(block: Float64Field.(Double) -> Double): DoubleBuffer = + final override inline fun Buffer.map(block: Float64Field.(Double) -> Double): Float64Buffer = DoubleArray(size) { Float64Field.block(getDouble(it)) }.asBuffer() @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE") - final override inline fun Buffer.mapIndexed(block: Float64Field.(index: Int, arg: Double) -> Double): DoubleBuffer = - DoubleBuffer(size) { Float64Field.block(it, getDouble(it)) } + final override inline fun Buffer.mapIndexed(block: Float64Field.(index: Int, arg: Double) -> Double): Float64Buffer = + Float64Buffer(size) { Float64Field.block(it, getDouble(it)) } @OptIn(UnstableKMathAPI::class) @Suppress("OVERRIDE_BY_INLINE") final override inline fun Buffer.zip( other: Buffer, block: Float64Field.(left: Double, right: Double) -> Double, - ): DoubleBuffer { + ): Float64Buffer { require(size == other.size) { "Incompatible buffer sizes. left: ${size}, right: ${other.size}" } - return DoubleBuffer(size) { Float64Field.block(getDouble(it), other.getDouble(it)) } + return Float64Buffer(size) { Float64Field.block(getDouble(it), other.getDouble(it)) } } override fun unaryOperationFunction(operation: String): (arg: Buffer) -> Buffer = @@ -48,32 +48,32 @@ public abstract class DoubleBufferOps : BufferAlgebra, Ext override fun binaryOperationFunction(operation: String): (left: Buffer, right: Buffer) -> Buffer = super.binaryOperationFunction(operation) - override fun Buffer.unaryMinus(): DoubleBuffer = map { -it } + override fun Buffer.unaryMinus(): Float64Buffer = map { -it } - override fun add(left: Buffer, right: Buffer): DoubleBuffer { + override fun add(left: Buffer, right: Buffer): Float64Buffer { require(right.size == left.size) { "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } - return if (left is DoubleBuffer && right is DoubleBuffer) { + return if (left is Float64Buffer && right is Float64Buffer) { val aArray = left.array val bArray = right.array - DoubleBuffer(DoubleArray(left.size) { aArray[it] + bArray[it] }) - } else DoubleBuffer(DoubleArray(left.size) { left[it] + right[it] }) + Float64Buffer(DoubleArray(left.size) { aArray[it] + bArray[it] }) + } else Float64Buffer(DoubleArray(left.size) { left[it] + right[it] }) } - override fun Buffer.plus(arg: Buffer): DoubleBuffer = add(this, arg) + override fun Buffer.plus(arg: Buffer): Float64Buffer = add(this, arg) - override fun Buffer.minus(arg: Buffer): DoubleBuffer { + override fun Buffer.minus(arg: Buffer): Float64Buffer { require(arg.size == this.size) { "The size of the first buffer ${this.size} should be the same as for second one: ${arg.size} " } - return if (this is DoubleBuffer && arg is DoubleBuffer) { + return if (this is Float64Buffer && arg is Float64Buffer) { val aArray = this.array val bArray = arg.array - DoubleBuffer(DoubleArray(this.size) { aArray[it] - bArray[it] }) - } else DoubleBuffer(DoubleArray(this.size) { this[it] - arg[it] }) + Float64Buffer(DoubleArray(this.size) { aArray[it] - bArray[it] }) + } else Float64Buffer(DoubleArray(this.size) { this[it] - arg[it] }) } // @@ -96,61 +96,61 @@ public abstract class DoubleBufferOps : BufferAlgebra, Ext // } @UnstableKMathAPI - override fun multiply(left: Buffer, right: Buffer): DoubleBuffer { + override fun multiply(left: Buffer, right: Buffer): Float64Buffer { require(right.size == left.size) { "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } - return if (left is DoubleBuffer && right is DoubleBuffer) { + return if (left is Float64Buffer && right is Float64Buffer) { val aArray = left.array val bArray = right.array - DoubleBuffer(DoubleArray(left.size) { aArray[it] * bArray[it] }) - } else DoubleBuffer(DoubleArray(left.size) { left[it] * right[it] }) + Float64Buffer(DoubleArray(left.size) { aArray[it] * bArray[it] }) + } else Float64Buffer(DoubleArray(left.size) { left[it] * right[it] }) } - override fun divide(left: Buffer, right: Buffer): DoubleBuffer { + override fun divide(left: Buffer, right: Buffer): Float64Buffer { require(right.size == left.size) { "The size of the first buffer ${left.size} should be the same as for second one: ${right.size} " } - return if (left is DoubleBuffer && right is DoubleBuffer) { + return if (left is Float64Buffer && right is Float64Buffer) { val aArray = left.array val bArray = right.array - DoubleBuffer(DoubleArray(left.size) { aArray[it] / bArray[it] }) - } else DoubleBuffer(DoubleArray(left.size) { left[it] / right[it] }) + Float64Buffer(DoubleArray(left.size) { aArray[it] / bArray[it] }) + } else Float64Buffer(DoubleArray(left.size) { left[it] / right[it] }) } - override fun sin(arg: Buffer): DoubleBuffer = arg.map { sin(it) } + override fun sin(arg: Buffer): Float64Buffer = arg.map { sin(it) } - override fun cos(arg: Buffer): DoubleBuffer = arg.map { cos(it) } + override fun cos(arg: Buffer): Float64Buffer = arg.map { cos(it) } - override fun tan(arg: Buffer): DoubleBuffer = arg.map { tan(it) } + override fun tan(arg: Buffer): Float64Buffer = arg.map { tan(it) } - override fun asin(arg: Buffer): DoubleBuffer = arg.map { asin(it) } + override fun asin(arg: Buffer): Float64Buffer = arg.map { asin(it) } - override fun acos(arg: Buffer): DoubleBuffer = arg.map { acos(it) } + override fun acos(arg: Buffer): Float64Buffer = arg.map { acos(it) } - override fun atan(arg: Buffer): DoubleBuffer = arg.map { atan(it) } + override fun atan(arg: Buffer): Float64Buffer = arg.map { atan(it) } - override fun sinh(arg: Buffer): DoubleBuffer = arg.map { sinh(it) } + override fun sinh(arg: Buffer): Float64Buffer = arg.map { sinh(it) } - override fun cosh(arg: Buffer): DoubleBuffer = arg.map { cosh(it) } + override fun cosh(arg: Buffer): Float64Buffer = arg.map { cosh(it) } - override fun tanh(arg: Buffer): DoubleBuffer = arg.map { tanh(it) } + override fun tanh(arg: Buffer): Float64Buffer = arg.map { tanh(it) } - override fun asinh(arg: Buffer): DoubleBuffer = arg.map { asinh(it) } + override fun asinh(arg: Buffer): Float64Buffer = arg.map { asinh(it) } - override fun acosh(arg: Buffer): DoubleBuffer = arg.map { acosh(it) } + override fun acosh(arg: Buffer): Float64Buffer = arg.map { acosh(it) } - override fun atanh(arg: Buffer): DoubleBuffer = arg.map { atanh(it) } + override fun atanh(arg: Buffer): Float64Buffer = arg.map { atanh(it) } - override fun exp(arg: Buffer): DoubleBuffer = arg.map { exp(it) } + override fun exp(arg: Buffer): Float64Buffer = arg.map { exp(it) } - override fun ln(arg: Buffer): DoubleBuffer = arg.map { ln(it) } + override fun ln(arg: Buffer): Float64Buffer = arg.map { ln(it) } - override fun norm(arg: Buffer): Double = DoubleL2Norm.norm(arg) + override fun norm(arg: Buffer): Double = Float64L2Norm.norm(arg) - override fun scale(a: Buffer, value: Double): DoubleBuffer = a.map { it * value } + override fun scale(a: Buffer, value: Double): Float64Buffer = a.map { it * value } override fun power(arg: Buffer, pow: Number): Buffer = if (pow is Int) { arg.map { it.pow(pow) } @@ -158,37 +158,37 @@ public abstract class DoubleBufferOps : BufferAlgebra, Ext arg.map { it.pow(pow.toDouble()) } } - public companion object : DoubleBufferOps() + public companion object : Float64BufferOps() } -public object DoubleL2Norm : Norm, Double> { +public object Float64L2Norm : Norm, Double> { override fun norm(arg: Point): Double = sqrt(arg.fold(0.0) { acc: Double, d: Double -> acc + d.pow(2) }) } -public fun DoubleBufferOps.sum(buffer: Buffer): Double = buffer.reduce(Double::plus) +public fun Float64BufferOps.sum(buffer: Buffer): Double = buffer.reduce(Double::plus) /** * Sum of elements using given [conversion] */ -public inline fun DoubleBufferOps.sumOf(buffer: Buffer, conversion: (T) -> Double): Double = +public inline fun Float64BufferOps.sumOf(buffer: Buffer, conversion: (T) -> Double): Double = buffer.fold(0.0) { acc, value -> acc + conversion(value) } -public fun DoubleBufferOps.average(buffer: Buffer): Double = sum(buffer) / buffer.size +public fun Float64BufferOps.average(buffer: Buffer): Double = sum(buffer) / buffer.size /** * Average of elements using given [conversion] */ -public inline fun DoubleBufferOps.averageOf(buffer: Buffer, conversion: (T) -> Double): Double = +public inline fun Float64BufferOps.averageOf(buffer: Buffer, conversion: (T) -> Double): Double = sumOf(buffer, conversion) / buffer.size -public fun DoubleBufferOps.dispersion(buffer: Buffer): Double { +public fun Float64BufferOps.dispersion(buffer: Buffer): Double { val av = average(buffer) return buffer.fold(0.0) { acc, value -> acc + (value - av).pow(2) } / buffer.size } -public fun DoubleBufferOps.std(buffer: Buffer): Double = sqrt(dispersion(buffer)) +public fun Float64BufferOps.std(buffer: Buffer): Double = sqrt(dispersion(buffer)) -public fun DoubleBufferOps.covariance(x: Buffer, y: Buffer): Double { +public fun Float64BufferOps.covariance(x: Buffer, y: Buffer): Double { require(x.size == y.size) { "Expected buffers of the same size, but x.size == ${x.size} and y.size == ${y.size}" } val xMean = average(x) val yMean = average(y) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt index a318dea53..72e33c523 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/integerFields.kt @@ -22,7 +22,7 @@ import kotlin.math.roundToLong */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int16Field : Field, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::ShortBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int16Buffer) override val zero: Int16 get() = 0 override val one: Int16 get() = 1 @@ -45,7 +45,7 @@ public object Int16Field : Field, Norm, NumericAlgebra, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::IntBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int32Buffer) override val zero: Int get() = 0 override val one: Int get() = 1 @@ -68,7 +68,7 @@ public object Int32Field : Field, Norm, NumericAlgebra, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::LongBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int64Buffer) override val zero: Int64 get() = 0L override val one: Int64 get() = 1L diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt index 17b3a72cc..0b7bef852 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/numbers.kt @@ -63,16 +63,16 @@ public interface ExtendedField : ExtendedFieldOps, Field, NumericAlgebr } /** - * A field for [Double] without boxing. Does not produce appropriate field element. + * A field for [Double] without boxing. Does not produce an appropriate field element. */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Float64Field : ExtendedField, Norm, ScaleOperations { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::DoubleBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Float64Buffer) - override inline val zero: Double get() = 0.0 - override inline val one: Double get() = 1.0 + override val zero: Double get() = 0.0 + override val one: Double get() = 1.0 - override inline fun number(value: Number): Double = value.toDouble() + override fun number(value: Number): Double = value.toDouble() override fun binaryOperationFunction(operation: String): (left: Double, right: Double) -> Double = when (operation) { @@ -80,26 +80,26 @@ public object Float64Field : ExtendedField, Norm, ScaleO else -> super.binaryOperationFunction(operation) } - override inline fun add(left: Double, right: Double): Double = left + right + override fun add(left: Double, right: Double): Double = left + right - override inline fun multiply(left: Double, right: Double): Double = left * right - override inline fun divide(left: Double, right: Double): Double = left / right + override fun multiply(left: Double, right: Double): Double = left * right + override fun divide(left: Double, right: Double): Double = left / right - override inline fun scale(a: Double, value: Double): Double = a * value + override fun scale(a: Double, value: Double): Double = a * value - override inline fun sin(arg: Double): Double = kotlin.math.sin(arg) - override inline fun cos(arg: Double): Double = kotlin.math.cos(arg) - override inline fun tan(arg: Double): Double = kotlin.math.tan(arg) - override inline fun acos(arg: Double): Double = kotlin.math.acos(arg) - override inline fun asin(arg: Double): Double = kotlin.math.asin(arg) - override inline fun atan(arg: Double): Double = kotlin.math.atan(arg) + override fun sin(arg: Double): Double = kotlin.math.sin(arg) + override fun cos(arg: Double): Double = kotlin.math.cos(arg) + override fun tan(arg: Double): Double = kotlin.math.tan(arg) + override fun acos(arg: Double): Double = kotlin.math.acos(arg) + override fun asin(arg: Double): Double = kotlin.math.asin(arg) + override fun atan(arg: Double): Double = kotlin.math.atan(arg) - override inline fun sinh(arg: Double): Double = kotlin.math.sinh(arg) - override inline fun cosh(arg: Double): Double = kotlin.math.cosh(arg) - override inline fun tanh(arg: Double): Double = kotlin.math.tanh(arg) - override inline fun asinh(arg: Double): Double = kotlin.math.asinh(arg) - override inline fun acosh(arg: Double): Double = kotlin.math.acosh(arg) - override inline fun atanh(arg: Double): Double = kotlin.math.atanh(arg) + override fun sinh(arg: Double): Double = kotlin.math.sinh(arg) + override fun cosh(arg: Double): Double = kotlin.math.cosh(arg) + override fun tanh(arg: Double): Double = kotlin.math.tanh(arg) + override fun asinh(arg: Double): Double = kotlin.math.asinh(arg) + override fun acosh(arg: Double): Double = kotlin.math.acosh(arg) + override fun atanh(arg: Double): Double = kotlin.math.atanh(arg) override fun sqrt(arg: Double): Double = kotlin.math.sqrt(arg) override fun power(arg: Double, pow: Number): Double = when { @@ -108,16 +108,16 @@ public object Float64Field : ExtendedField, Norm, ScaleO else -> arg.kpow(pow.toDouble()) } - override inline fun exp(arg: Double): Double = kotlin.math.exp(arg) - override inline fun ln(arg: Double): Double = kotlin.math.ln(arg) + override fun exp(arg: Double): Double = kotlin.math.exp(arg) + override fun ln(arg: Double): Double = kotlin.math.ln(arg) - override inline fun norm(arg: Double): Double = abs(arg) + override fun norm(arg: Double): Double = abs(arg) - override inline fun Double.unaryMinus(): Double = -this - override inline fun Double.plus(arg: Double): Double = this + arg - override inline fun Double.minus(arg: Double): Double = this - arg - override inline fun Double.times(arg: Double): Double = this * arg - override inline fun Double.div(arg: Double): Double = this / arg + override fun Double.unaryMinus(): Double = -this + override fun Double.plus(arg: Double): Double = this + arg + override fun Double.minus(arg: Double): Double = this - arg + override fun Double.times(arg: Double): Double = this * arg + override fun Double.div(arg: Double): Double = this / arg } public typealias DoubleField = Float64Field @@ -125,14 +125,14 @@ public typealias DoubleField = Float64Field public val Double.Companion.algebra: Float64Field get() = Float64Field /** - * A field for [Float] without boxing. Does not produce appropriate field element. + * A field for [Float] without boxing. Does not produce an appropriate field element. */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Float32Field : ExtendedField, Norm { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::FloatBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Float32Buffer) - override inline val zero: Float get() = 0.0f - override inline val one: Float get() = 1.0f + override val zero: Float get() = 0.0f + override val one: Float get() = 1.0f override fun number(value: Number): Float = value.toFloat() @@ -142,40 +142,40 @@ public object Float32Field : ExtendedField, Norm { else -> super.binaryOperationFunction(operation) } - override inline fun add(left: Float, right: Float): Float = left + right + override fun add(left: Float, right: Float): Float = left + right override fun scale(a: Float, value: Double): Float = a * value.toFloat() - override inline fun multiply(left: Float, right: Float): Float = left * right + override fun multiply(left: Float, right: Float): Float = left * right - override inline fun divide(left: Float, right: Float): Float = left / right + override fun divide(left: Float, right: Float): Float = left / right - override inline fun sin(arg: Float): Float = kotlin.math.sin(arg) - override inline fun cos(arg: Float): Float = kotlin.math.cos(arg) - override inline fun tan(arg: Float): Float = kotlin.math.tan(arg) - override inline fun acos(arg: Float): Float = kotlin.math.acos(arg) - override inline fun asin(arg: Float): Float = kotlin.math.asin(arg) - override inline fun atan(arg: Float): Float = kotlin.math.atan(arg) + override fun sin(arg: Float): Float = kotlin.math.sin(arg) + override fun cos(arg: Float): Float = kotlin.math.cos(arg) + override fun tan(arg: Float): Float = kotlin.math.tan(arg) + override fun acos(arg: Float): Float = kotlin.math.acos(arg) + override fun asin(arg: Float): Float = kotlin.math.asin(arg) + override fun atan(arg: Float): Float = kotlin.math.atan(arg) - override inline fun sinh(arg: Float): Float = kotlin.math.sinh(arg) - override inline fun cosh(arg: Float): Float = kotlin.math.cosh(arg) - override inline fun tanh(arg: Float): Float = kotlin.math.tanh(arg) - override inline fun asinh(arg: Float): Float = kotlin.math.asinh(arg) - override inline fun acosh(arg: Float): Float = kotlin.math.acosh(arg) - override inline fun atanh(arg: Float): Float = kotlin.math.atanh(arg) + override fun sinh(arg: Float): Float = kotlin.math.sinh(arg) + override fun cosh(arg: Float): Float = kotlin.math.cosh(arg) + override fun tanh(arg: Float): Float = kotlin.math.tanh(arg) + override fun asinh(arg: Float): Float = kotlin.math.asinh(arg) + override fun acosh(arg: Float): Float = kotlin.math.acosh(arg) + override fun atanh(arg: Float): Float = kotlin.math.atanh(arg) - override inline fun sqrt(arg: Float): Float = kotlin.math.sqrt(arg) - override inline fun power(arg: Float, pow: Number): Float = arg.kpow(pow.toFloat()) + override fun sqrt(arg: Float): Float = kotlin.math.sqrt(arg) + override fun power(arg: Float, pow: Number): Float = arg.kpow(pow.toFloat()) - override inline fun exp(arg: Float): Float = kotlin.math.exp(arg) - override inline fun ln(arg: Float): Float = kotlin.math.ln(arg) + override fun exp(arg: Float): Float = kotlin.math.exp(arg) + override fun ln(arg: Float): Float = kotlin.math.ln(arg) - override inline fun norm(arg: Float): Float = abs(arg) + override fun norm(arg: Float): Float = abs(arg) - override inline fun Float.unaryMinus(): Float = -this - override inline fun Float.plus(arg: Float): Float = this + arg - override inline fun Float.minus(arg: Float): Float = this - arg - override inline fun Float.times(arg: Float): Float = this * arg - override inline fun Float.div(arg: Float): Float = this / arg + override fun Float.unaryMinus(): Float = -this + override fun Float.plus(arg: Float): Float = this + arg + override fun Float.minus(arg: Float): Float = this - arg + override fun Float.times(arg: Float): Float = this * arg + override fun Float.div(arg: Float): Float = this / arg } public typealias FloatField = Float32Field @@ -183,24 +183,24 @@ public typealias FloatField = Float32Field public val Float.Companion.algebra: Float32Field get() = Float32Field /** - * A field for [Int] without boxing. Does not produce corresponding ring element. + * A field for [Int] without boxing. Does not produce a corresponding ring element. */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int32Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::IntBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int32Buffer) - override inline val zero: Int get() = 0 - override inline val one: Int get() = 1 + override val zero: Int get() = 0 + override val one: Int get() = 1 override fun number(value: Number): Int = value.toInt() - override inline fun add(left: Int, right: Int): Int = left + right - override inline fun multiply(left: Int, right: Int): Int = left * right - override inline fun norm(arg: Int): Int = abs(arg) + override fun add(left: Int, right: Int): Int = left + right + override fun multiply(left: Int, right: Int): Int = left * right + override fun norm(arg: Int): Int = abs(arg) - override inline fun Int.unaryMinus(): Int = -this - override inline fun Int.plus(arg: Int): Int = this + arg - override inline fun Int.minus(arg: Int): Int = this - arg - override inline fun Int.times(arg: Int): Int = this * arg + override fun Int.unaryMinus(): Int = -this + override fun Int.plus(arg: Int): Int = this + arg + override fun Int.minus(arg: Int): Int = this - arg + override fun Int.times(arg: Int): Int = this * arg } public typealias IntRing = Int32Ring @@ -208,24 +208,24 @@ public typealias IntRing = Int32Ring public val Int.Companion.algebra: Int32Ring get() = Int32Ring /** - * A field for [Short] without boxing. Does not produce appropriate ring element. + * A field for [Short] without boxing. Does not produce an appropriate ring element. */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int16Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::ShortBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int16Buffer) - override inline val zero: Short get() = 0 - override inline val one: Short get() = 1 + override val zero: Short get() = 0 + override val one: Short get() = 1 override fun number(value: Number): Short = value.toShort() - override inline fun add(left: Short, right: Short): Short = (left + right).toShort() - override inline fun multiply(left: Short, right: Short): Short = (left * right).toShort() + override fun add(left: Short, right: Short): Short = (left + right).toShort() + override fun multiply(left: Short, right: Short): Short = (left * right).toShort() override fun norm(arg: Short): Short = if (arg > 0) arg else (-arg).toShort() - override inline fun Short.unaryMinus(): Short = (-this).toShort() - override inline fun Short.plus(arg: Short): Short = (this + arg).toShort() - override inline fun Short.minus(arg: Short): Short = (this - arg).toShort() - override inline fun Short.times(arg: Short): Short = (this * arg).toShort() + override fun Short.unaryMinus(): Short = (-this).toShort() + override fun Short.plus(arg: Short): Short = (this + arg).toShort() + override fun Short.minus(arg: Short): Short = (this - arg).toShort() + override fun Short.times(arg: Short): Short = (this * arg).toShort() } public typealias ShortRing = Int16Ring @@ -235,45 +235,47 @@ public val Short.Companion.algebra: Int16Ring get() = Int16Ring /** * A field for [Byte] without boxing. Does not produce appropriate ring element. */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") -public object ByteRing : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::ByteBuffer) +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +public object Int8Ring : Ring, Norm, NumericAlgebra { + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int8Buffer) - override inline val zero: Byte get() = 0 - override inline val one: Byte get() = 1 + override val zero: Byte get() = 0 + override val one: Byte get() = 1 override fun number(value: Number): Byte = value.toByte() - override inline fun add(left: Byte, right: Byte): Byte = (left + right).toByte() - override inline fun multiply(left: Byte, right: Byte): Byte = (left * right).toByte() + override fun add(left: Byte, right: Byte): Byte = (left + right).toByte() + override fun multiply(left: Byte, right: Byte): Byte = (left * right).toByte() override fun norm(arg: Byte): Byte = if (arg > 0) arg else (-arg).toByte() - override inline fun Byte.unaryMinus(): Byte = (-this).toByte() - override inline fun Byte.plus(arg: Byte): Byte = (this + arg).toByte() - override inline fun Byte.minus(arg: Byte): Byte = (this - arg).toByte() - override inline fun Byte.times(arg: Byte): Byte = (this * arg).toByte() + override fun Byte.unaryMinus(): Byte = (-this).toByte() + override fun Byte.plus(arg: Byte): Byte = (this + arg).toByte() + override fun Byte.minus(arg: Byte): Byte = (this - arg).toByte() + override fun Byte.times(arg: Byte): Byte = (this * arg).toByte() } -public val Byte.Companion.algebra: ByteRing get() = ByteRing +public typealias ByteRing = Int8Ring + +public val Byte.Companion.algebra: Int8Ring get() = Int8Ring /** * A field for [Double] without boxing. Does not produce an appropriate ring element. */ -@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "OVERRIDE_BY_INLINE") +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") public object Int64Ring : Ring, Norm, NumericAlgebra { - override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::LongBuffer) + override val bufferFactory: MutableBufferFactory = MutableBufferFactory(::Int64Buffer) - override inline val zero: Long get() = 0L - override inline val one: Long get() = 1L + override val zero: Long get() = 0L + override val one: Long get() = 1L override fun number(value: Number): Long = value.toLong() - override inline fun add(left: Long, right: Long): Long = left + right - override inline fun multiply(left: Long, right: Long): Long = left * right + override fun add(left: Long, right: Long): Long = left + right + override fun multiply(left: Long, right: Long): Long = left * right override fun norm(arg: Long): Long = abs(arg) - override inline fun Long.unaryMinus(): Long = (-this) - override inline fun Long.plus(arg: Long): Long = (this + arg) - override inline fun Long.minus(arg: Long): Long = (this - arg) - override inline fun Long.times(arg: Long): Long = (this * arg) + override fun Long.unaryMinus(): Long = (-this) + override fun Long.plus(arg: Long): Long = (this + arg) + override fun Long.minus(arg: Long): Long = (this - arg) + override fun Long.times(arg: Long): Long = (this * arg) } public typealias LongRing = Int64Ring diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt index cef8d1d4d..4c8ba0652 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Buffer.kt @@ -93,8 +93,8 @@ public interface Buffer : WithSize { List(size, initializer).asBuffer() /** - * Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([IntBuffer], - * [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. + * Creates a [Buffer] of given [type]. If the type is primitive, specialized buffers are used ([Int32Buffer], + * [Float64Buffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @@ -110,8 +110,8 @@ public interface Buffer : WithSize { } /** - * Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([IntBuffer], - * [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. + * Creates a [Buffer] of given type [T]. If the type is primitive, specialized buffers are used ([Int32Buffer], + * [Float64Buffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float32Buffer.kt similarity index 62% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float32Buffer.kt index f1533ee3a..44ef4dcf0 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/FloatBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float32Buffer.kt @@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline * @author Iaroslav Postovalov */ @JvmInline -public value class FloatBuffer(public val array: FloatArray) : PrimitiveBuffer { +public value class Float32Buffer(public val array: FloatArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Float = array[index] @@ -26,35 +26,37 @@ public value class FloatBuffer(public val array: FloatArray) : PrimitiveBuffer = - FloatBuffer(array.copyOf()) + Float32Buffer(array.copyOf()) } +public typealias FloatBuffer = Float32Buffer + /** - * Creates a new [FloatBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a new [Float32Buffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for a buffer element given its index. */ -public inline fun FloatBuffer(size: Int, init: (Int) -> Float): FloatBuffer = FloatBuffer(FloatArray(size) { init(it) }) +public inline fun Float32Buffer(size: Int, init: (Int) -> Float): Float32Buffer = Float32Buffer(FloatArray(size) { init(it) }) /** - * Returns a new [FloatBuffer] of given elements. + * Returns a new [Float32Buffer] of given elements. */ -public fun FloatBuffer(vararg floats: Float): FloatBuffer = FloatBuffer(floats) +public fun Float32Buffer(vararg floats: Float): Float32Buffer = Float32Buffer(floats) /** * Returns a new [FloatArray] containing all the elements of this [Buffer]. */ public fun Buffer.toFloatArray(): FloatArray = when (this) { - is FloatBuffer -> array.copyOf() + is Float32Buffer -> array.copyOf() else -> FloatArray(size, ::get) } /** - * Returns [FloatBuffer] over this array. + * Returns [Float32Buffer] over this array. * * @receiver the array. * @return the new buffer. */ -public fun FloatArray.asBuffer(): FloatBuffer = FloatBuffer(this) +public fun FloatArray.asBuffer(): Float32Buffer = Float32Buffer(this) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt similarity index 50% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt index 1696d5055..0542c1bf4 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/DoubleBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Float64Buffer.kt @@ -14,7 +14,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class DoubleBuffer(public val array: DoubleArray) : PrimitiveBuffer { +public value class Float64Buffer(public val array: DoubleArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Double = array[index] @@ -25,57 +25,59 @@ public value class DoubleBuffer(public val array: DoubleArray) : PrimitiveBuffer override operator fun iterator(): DoubleIterator = array.iterator() - override fun copy(): DoubleBuffer = DoubleBuffer(array.copyOf()) + override fun copy(): Float64Buffer = Float64Buffer(array.copyOf()) override fun toString(): String = Buffer.toString(this) public companion object { - public fun zero(size: Int): DoubleBuffer = DoubleArray(size).asBuffer() + public fun zero(size: Int): Float64Buffer = DoubleArray(size).asBuffer() } } +public typealias DoubleBuffer = Float64Buffer + /** - * Creates a new [DoubleBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a new [Float64Buffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for a buffer element given its index. */ -public inline fun DoubleBuffer(size: Int, init: (Int) -> Double): DoubleBuffer = - DoubleBuffer(DoubleArray(size) { init(it) }) +public inline fun Float64Buffer(size: Int, init: (Int) -> Double): Float64Buffer = + Float64Buffer(DoubleArray(size) { init(it) }) /** - * Returns a new [DoubleBuffer] of given elements. + * Returns a new [Float64Buffer] of given elements. */ -public fun DoubleBuffer(vararg doubles: Double): DoubleBuffer = DoubleBuffer(doubles) +public fun Float64Buffer(vararg doubles: Double): Float64Buffer = Float64Buffer(doubles) /** * Returns a new [DoubleArray] containing all the elements of this [Buffer]. */ public fun Buffer.toDoubleArray(): DoubleArray = when (this) { - is DoubleBuffer -> array + is Float64Buffer -> array else -> DoubleArray(size, ::get) } /** - * Represent this buffer as [DoubleBuffer]. Does not guarantee that changes in the original buffer are reflected on this buffer. + * Represent this buffer as [Float64Buffer]. Does not guarantee that changes in the original buffer are reflected on this buffer. */ -public fun Buffer.toDoubleBuffer(): DoubleBuffer = when (this) { - is DoubleBuffer -> this +public fun Buffer.toFloat64Buffer(): Float64Buffer = when (this) { + is Float64Buffer -> this else -> DoubleArray(size, ::get).asBuffer() } /** - * Returns [DoubleBuffer] over this array. + * Returns [Float64Buffer] over this array. * * @receiver the array. * @return the new buffer. */ -public fun DoubleArray.asBuffer(): DoubleBuffer = DoubleBuffer(this) +public fun DoubleArray.asBuffer(): Float64Buffer = Float64Buffer(this) -public fun interface DoubleBufferTransform : BufferTransform { - public fun transform(arg: DoubleBuffer): DoubleBuffer +public fun interface Float64BufferTransform : BufferTransform { + public fun transform(arg: Float64Buffer): Float64Buffer - override fun transform(arg: Buffer): DoubleBuffer = arg.toDoubleBuffer() + override fun transform(arg: Buffer): Float64Buffer = arg.toFloat64Buffer() } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ShortBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int16Buffer.kt similarity index 65% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ShortBuffer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int16Buffer.kt index 7dbb2b58e..1ba40c934 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ShortBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int16Buffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class ShortBuffer(public val array: ShortArray) : MutableBuffer { +public value class Int16Buffer(public val array: ShortArray) : MutableBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Short = array[index] @@ -23,35 +23,37 @@ public value class ShortBuffer(public val array: ShortArray) : MutableBuffer = ShortBuffer(array.copyOf()) + override fun copy(): MutableBuffer = Int16Buffer(array.copyOf()) } +public typealias ShortBuffer = Int16Buffer + /** - * Creates a new [ShortBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a new [Int16Buffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for a buffer element given its index. */ -public inline fun ShortBuffer(size: Int, init: (Int) -> Short): ShortBuffer = ShortBuffer(ShortArray(size) { init(it) }) +public inline fun Int16Buffer(size: Int, init: (Int) -> Short): Int16Buffer = Int16Buffer(ShortArray(size) { init(it) }) /** - * Returns a new [ShortBuffer] of given elements. + * Returns a new [Int16Buffer] of given elements. */ -public fun ShortBuffer(vararg shorts: Short): ShortBuffer = ShortBuffer(shorts) +public fun Int16Buffer(vararg shorts: Short): Int16Buffer = Int16Buffer(shorts) /** * Returns a new [ShortArray] containing all the elements of this [Buffer]. */ public fun Buffer.toShortArray(): ShortArray = when (this) { - is ShortBuffer -> array.copyOf() + is Int16Buffer -> array.copyOf() else -> ShortArray(size, ::get) } /** - * Returns [ShortBuffer] over this array. + * Returns [Int16Buffer] over this array. * * @receiver the array. * @return the new buffer. */ -public fun ShortArray.asBuffer(): ShortBuffer = ShortBuffer(this) +public fun ShortArray.asBuffer(): Int16Buffer = Int16Buffer(this) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int32Buffer.kt similarity index 61% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int32Buffer.kt index 0de7119b1..afd94e9cd 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/IntBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int32Buffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class IntBuffer(public val array: IntArray) : PrimitiveBuffer { +public value class Int32Buffer(public val array: IntArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Int = array[index] @@ -24,35 +24,37 @@ public value class IntBuffer(public val array: IntArray) : PrimitiveBuffer override operator fun iterator(): IntIterator = array.iterator() - override fun copy(): IntBuffer = IntBuffer(array.copyOf()) + override fun copy(): Int32Buffer = Int32Buffer(array.copyOf()) } +public typealias IntBuffer = Int32Buffer + /** - * Creates a new [IntBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a new [Int32Buffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for a buffer element given its index. */ -public inline fun IntBuffer(size: Int, init: (Int) -> Int): IntBuffer = IntBuffer(IntArray(size) { init(it) }) +public inline fun Int32Buffer(size: Int, init: (Int) -> Int): Int32Buffer = Int32Buffer(IntArray(size) { init(it) }) /** - * Returns a new [IntBuffer] of given elements. + * Returns a new [Int32Buffer] of given elements. */ -public fun IntBuffer(vararg ints: Int): IntBuffer = IntBuffer(ints) +public fun Int32Buffer(vararg ints: Int): Int32Buffer = Int32Buffer(ints) /** * Returns a new [IntArray] containing all the elements of this [Buffer]. */ public fun Buffer.toIntArray(): IntArray = when (this) { - is IntBuffer -> array.copyOf() + is Int32Buffer -> array.copyOf() else -> IntArray(size, ::get) } /** - * Returns [IntBuffer] over this array. + * Returns [Int32Buffer] over this array. * * @receiver the array. * @return the new buffer. */ -public fun IntArray.asBuffer(): IntBuffer = IntBuffer(this) +public fun IntArray.asBuffer(): Int32Buffer = Int32Buffer(this) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int64Buffer.kt similarity index 63% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int64Buffer.kt index 9f77fc9d8..c67d109aa 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/LongBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int64Buffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class LongBuffer(public val array: LongArray) : PrimitiveBuffer { +public value class Int64Buffer(public val array: LongArray) : PrimitiveBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Long = array[index] @@ -25,35 +25,37 @@ public value class LongBuffer(public val array: LongArray) : PrimitiveBuffer = - LongBuffer(array.copyOf()) + Int64Buffer(array.copyOf()) } +public typealias LongBuffer = Int64Buffer + /** - * Creates a new [LongBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a new [Int64Buffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for a buffer element given its index. */ -public inline fun LongBuffer(size: Int, init: (Int) -> Long): LongBuffer = LongBuffer(LongArray(size) { init(it) }) +public inline fun Int64Buffer(size: Int, init: (Int) -> Long): Int64Buffer = Int64Buffer(LongArray(size) { init(it) }) /** - * Returns a new [LongBuffer] of given elements. + * Returns a new [Int64Buffer] of given elements. */ -public fun LongBuffer(vararg longs: Long): LongBuffer = LongBuffer(longs) +public fun Int64Buffer(vararg longs: Long): Int64Buffer = Int64Buffer(longs) /** * Returns a new [LongArray] containing all the elements of this [Buffer]. */ public fun Buffer.toLongArray(): LongArray = when (this) { - is LongBuffer -> array.copyOf() + is Int64Buffer -> array.copyOf() else -> LongArray(size, ::get) } /** - * Returns [LongBuffer] over this array. + * Returns [Int64Buffer] over this array. * * @receiver the array. * @return the new buffer. */ -public fun LongArray.asBuffer(): LongBuffer = LongBuffer(this) +public fun LongArray.asBuffer(): Int64Buffer = Int64Buffer(this) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ByteBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int8Buffer.kt similarity index 67% rename from kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ByteBuffer.kt rename to kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int8Buffer.kt index 2be17c5e4..923fbec06 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/ByteBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/Int8Buffer.kt @@ -13,7 +13,7 @@ import kotlin.jvm.JvmInline * @property array the underlying array. */ @JvmInline -public value class ByteBuffer(public val array: ByteArray) : MutableBuffer { +public value class Int8Buffer(public val array: ByteArray) : MutableBuffer { override val size: Int get() = array.size override operator fun get(index: Int): Byte = array[index] @@ -23,35 +23,35 @@ public value class ByteBuffer(public val array: ByteArray) : MutableBuffer } override operator fun iterator(): ByteIterator = array.iterator() - override fun copy(): MutableBuffer = ByteBuffer(array.copyOf()) + override fun copy(): MutableBuffer = Int8Buffer(array.copyOf()) } /** - * Creates a new [ByteBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a new [Int8Buffer] with the specified [size], where each element is calculated by calling the specified * [init] function. * * The function [init] is called for each array element sequentially starting from the first one. * It should return the value for a buffer element given its index. */ -public inline fun ByteBuffer(size: Int, init: (Int) -> Byte): ByteBuffer = ByteBuffer(ByteArray(size) { init(it) }) +public inline fun Int8Buffer(size: Int, init: (Int) -> Byte): Int8Buffer = Int8Buffer(ByteArray(size) { init(it) }) /** - * Returns a new [ByteBuffer] of given elements. + * Returns a new [Int8Buffer] of given elements. */ -public fun ByteBuffer(vararg bytes: Byte): ByteBuffer = ByteBuffer(bytes) +public fun Int8Buffer(vararg bytes: Byte): Int8Buffer = Int8Buffer(bytes) /** * Returns a new [ByteArray] containing all the elements of this [Buffer]. */ public fun Buffer.toByteArray(): ByteArray = when (this) { - is ByteBuffer -> array.copyOf() + is Int8Buffer -> array.copyOf() else -> ByteArray(size, ::get) } /** - * Returns [ByteBuffer] over this array. + * Returns [Int8Buffer] over this array. * * @receiver the array. * @return the new buffer. */ -public fun ByteArray.asBuffer(): ByteBuffer = ByteBuffer(this) +public fun ByteArray.asBuffer(): Int8Buffer = Int8Buffer(this) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt index c0bfc6ecc..772c670ea 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/MutableBuffer.kt @@ -25,40 +25,40 @@ public interface MutableBuffer : Buffer { public companion object { /** - * Creates a [DoubleBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a [Float64Buffer] with the specified [size], where each element is calculated by calling the specified * [initializer] function. */ - public inline fun double(size: Int, initializer: (Int) -> Double): DoubleBuffer = - DoubleBuffer(size, initializer) + public inline fun double(size: Int, initializer: (Int) -> Double): Float64Buffer = + Float64Buffer(size, initializer) /** - * Creates a [ShortBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a [Int16Buffer] with the specified [size], where each element is calculated by calling the specified * [initializer] function. */ - public inline fun short(size: Int, initializer: (Int) -> Short): ShortBuffer = - ShortBuffer(size, initializer) + public inline fun short(size: Int, initializer: (Int) -> Short): Int16Buffer = + Int16Buffer(size, initializer) /** - * Creates a [IntBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a [Int32Buffer] with the specified [size], where each element is calculated by calling the specified * [initializer] function. */ - public inline fun int(size: Int, initializer: (Int) -> Int): IntBuffer = - IntBuffer(size, initializer) + public inline fun int(size: Int, initializer: (Int) -> Int): Int32Buffer = + Int32Buffer(size, initializer) /** - * Creates a [LongBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a [Int64Buffer] with the specified [size], where each element is calculated by calling the specified * [initializer] function. */ - public inline fun long(size: Int, initializer: (Int) -> Long): LongBuffer = - LongBuffer(size, initializer) + public inline fun long(size: Int, initializer: (Int) -> Long): Int64Buffer = + Int64Buffer(size, initializer) /** - * Creates a [FloatBuffer] with the specified [size], where each element is calculated by calling the specified + * Creates a [Float32Buffer] with the specified [size], where each element is calculated by calling the specified * [initializer] function. */ - public inline fun float(size: Int, initializer: (Int) -> Float): FloatBuffer = - FloatBuffer(size, initializer) + public inline fun float(size: Int, initializer: (Int) -> Float): Float32Buffer = + Float32Buffer(size, initializer) /** @@ -69,7 +69,7 @@ public interface MutableBuffer : Buffer { /** * Creates a [MutableBuffer] of given [type]. If the type is primitive, specialized buffers are used - * ([IntBuffer], [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. + * ([Int32Buffer], [Float64Buffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ @@ -86,7 +86,7 @@ public interface MutableBuffer : Buffer { /** * Creates a [MutableBuffer] of given type [T]. If the type is primitive, specialized buffers are used - * ([IntBuffer], [DoubleBuffer], etc.), [ListBuffer] is returned otherwise. + * ([Int32Buffer], [Float64Buffer], etc.), [ListBuffer] is returned otherwise. * * The [size] is specified, and each element is calculated by calling the specified [initializer] function. */ diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt index 353892105..9033dea98 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/structures/bufferPrimitiveAccess.kt @@ -13,7 +13,7 @@ public fun Buffer.getDouble(index: Int): Double = if (this is BufferView } else { get(index) } -} else if (this is DoubleBuffer) { +} else if (this is Float64Buffer) { array[index] } else { get(index) @@ -30,7 +30,7 @@ public fun Buffer.getInt(index: Int): Int = if (this is BufferView) { } else { get(index) } -} else if (this is IntBuffer) { +} else if (this is Int32Buffer) { array[index] } else { get(index) diff --git a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/misc/PermSortTest.kt b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/misc/PermSortTest.kt index dd97df1e8..fa9ac19c0 100644 --- a/kmath-core/src/commonTest/kotlin/space/kscience/kmath/misc/PermSortTest.kt +++ b/kmath-core/src/commonTest/kotlin/space/kscience/kmath/misc/PermSortTest.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.misc import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.misc.PermSortTest.Platform.* -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Int32Buffer import space.kscience.kmath.structures.asBuffer import kotlin.random.Random import kotlin.test.Test @@ -29,7 +29,7 @@ class PermSortTest { */ @Test fun testOnEmptyBuffer() { - val emptyBuffer = IntBuffer(0) {it} + val emptyBuffer = Int32Buffer(0) {it} var permutations = emptyBuffer.indicesSorted() assertTrue(permutations.isEmpty(), "permutation on an empty buffer should return an empty result") permutations = emptyBuffer.indicesSortedDescending() @@ -100,5 +100,5 @@ class PermSortTest { } } - private fun Random.buffer(size : Int) = IntBuffer(size) { nextInt() } + private fun Random.buffer(size : Int) = Int32Buffer(size) { nextInt() } } diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt index 797d2db4a..e67cab72d 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingDoubleChain.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.chains -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer /** * Chunked, specialized chain for double values, which supports blocking [nextBlocking] operation @@ -15,7 +15,7 @@ public interface BlockingDoubleChain : BlockingBufferChain { /** * Returns an [DoubleArray] chunk of [size] values of [next]. */ - override fun nextBufferBlocking(size: Int): DoubleBuffer + override fun nextBufferBlocking(size: Int): Float64Buffer override suspend fun fork(): BlockingDoubleChain @@ -23,9 +23,9 @@ public interface BlockingDoubleChain : BlockingBufferChain { } public fun BlockingDoubleChain.map(transform: (Double) -> Double): BlockingDoubleChain = object : BlockingDoubleChain { - override fun nextBufferBlocking(size: Int): DoubleBuffer { + override fun nextBufferBlocking(size: Int): Float64Buffer { val block = this@map.nextBufferBlocking(size) - return DoubleBuffer(size) { transform(block[it]) } + return Float64Buffer(size) { transform(block[it]) } } override suspend fun fork(): BlockingDoubleChain = this@map.fork().map(transform) diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingIntChain.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingIntChain.kt index a481156f2..172aa95cd 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingIntChain.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/chains/BlockingIntChain.kt @@ -5,13 +5,13 @@ package space.kscience.kmath.chains -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Int32Buffer /** * Performance optimized chain for integer values */ public interface BlockingIntChain : BlockingBufferChain { - override fun nextBufferBlocking(size: Int): IntBuffer + override fun nextBufferBlocking(size: Int): Int32Buffer override suspend fun fork(): BlockingIntChain } \ No newline at end of file diff --git a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt index 2cc8b8393..c52b6c3d5 100644 --- a/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt +++ b/kmath-coroutines/src/commonMain/kotlin/space/kscience/kmath/streaming/BufferFlow.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.flow import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer /** * Create a [Flow] from buffer @@ -55,7 +55,7 @@ public fun Flow.chunked(bufferSize: Int, bufferFactory: BufferFactory) /** * Specialized flow chunker for real buffer */ -public fun Flow.chunked(bufferSize: Int): Flow = flow { +public fun Flow.chunked(bufferSize: Int): Flow = flow { require(bufferSize > 0) { "Resulting chunk size must be more than zero" } if (this@chunked is BlockingDoubleChain) { @@ -70,13 +70,13 @@ public fun Flow.chunked(bufferSize: Int): Flow = flow { counter++ if (counter == bufferSize) { - val buffer = DoubleBuffer(array) + val buffer = Float64Buffer(array) emit(buffer) counter = 0 } } - if (counter > 0) emit(DoubleBuffer(counter) { array[it] }) + if (counter > 0) emit(Float64Buffer(counter) { array[it] }) } } diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt index 411a35188..108b840dd 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.real import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.Point -import space.kscience.kmath.operations.DoubleL2Norm +import space.kscience.kmath.operations.Float64L2Norm import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.MutableBuffer.Companion.double import space.kscience.kmath.structures.asBuffer @@ -103,4 +103,4 @@ public fun DoubleVector.sum(): Double { return res } -public val DoubleVector.norm: Double get() = DoubleL2Norm.norm(this) \ No newline at end of file +public val DoubleVector.norm: Double get() = Float64L2Norm.norm(this) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt index f86b9ef5f..fe5593eaa 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/RealMatrix.kt @@ -15,7 +15,7 @@ import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.algebra import space.kscience.kmath.operations.asIterable import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.math.pow /* @@ -130,19 +130,19 @@ public fun RealMatrix.extractColumns(columnRange: IntRange): RealMatrix = public fun RealMatrix.extractColumn(columnIndex: Int): RealMatrix = extractColumns(columnIndex..columnIndex) -public fun RealMatrix.sumByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +public fun RealMatrix.sumByColumn(): Float64Buffer = Float64Buffer(colNum) { j -> columns[j].sum() } -public fun RealMatrix.minByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +public fun RealMatrix.minByColumn(): Float64Buffer = Float64Buffer(colNum) { j -> columns[j].asIterable().minOrNull() ?: error("Cannot produce min on empty column") } -public fun RealMatrix.maxByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +public fun RealMatrix.maxByColumn(): Float64Buffer = Float64Buffer(colNum) { j -> columns[j].asIterable().maxOrNull() ?: error("Cannot produce min on empty column") } -public fun RealMatrix.averageByColumn(): DoubleBuffer = DoubleBuffer(colNum) { j -> +public fun RealMatrix.averageByColumn(): Float64Buffer = Float64Buffer(colNum) { j -> columns[j].asIterable().average() } diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt index adb62b173..79af446e2 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/grids.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.real import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.math.floor public val ClosedFloatingPointRange.length: Double get() = endInclusive - start @@ -16,30 +16,30 @@ public val ClosedFloatingPointRange.length: Double get() = endInclusive * Create a Buffer-based grid with equally distributed [numberOfPoints] points. The range could be increasing or decreasing. * If range has a zero size, then the buffer consisting of [numberOfPoints] equal values is returned. */ -public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, numberOfPoints: Int): DoubleBuffer { +public fun Buffer.Companion.fromRange(range: ClosedFloatingPointRange, numberOfPoints: Int): Float64Buffer { require(numberOfPoints >= 2) { "Number of points in grid must be more than 1" } val normalizedRange = when { range.endInclusive > range.start -> range range.endInclusive < range.start -> range.endInclusive..range.start - else -> return DoubleBuffer(numberOfPoints) { range.start } + else -> return Float64Buffer(numberOfPoints) { range.start } } val step = normalizedRange.length / (numberOfPoints - 1) - return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it } + return Float64Buffer(numberOfPoints) { normalizedRange.start + step * it } } /** * Create a Buffer-based grid with equally distributed points with a fixed [step]. The range could be increasing or decreasing. * If the step is larger than the range size, single point is returned. */ -public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange, step: Double): DoubleBuffer { +public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange, step: Double): Float64Buffer { require(step > 0) { "The grid step must be positive" } val normalizedRange = when { range.endInclusive > range.start -> range range.endInclusive < range.start -> range.endInclusive..range.start - else -> return DoubleBuffer(range.start) + else -> return Float64Buffer(range.start) } val numberOfPoints = floor(normalizedRange.length / step).toInt() + 1 - return DoubleBuffer(numberOfPoints) { normalizedRange.start + step * it } + return Float64Buffer(numberOfPoints) { normalizedRange.start + step * it } } /** @@ -51,4 +51,4 @@ public fun Buffer.Companion.withFixedStep(range: ClosedFloatingPointRange.step(step: Double): DoubleBuffer = Buffer.withFixedStep(this, step) \ No newline at end of file +public infix fun ClosedFloatingPointRange.step(step: Double): Float64Buffer = Buffer.withFixedStep(this, step) \ No newline at end of file diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt index 13b579144..1b9f0f4e2 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/realND.kt @@ -7,14 +7,14 @@ package space.kscience.kmath.real import space.kscience.kmath.nd.BufferND import space.kscience.kmath.operations.Float64Field -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer /** * Map one [BufferND] using function without indices. */ public inline fun BufferND.mapInline(crossinline transform: Float64Field.(Double) -> Double): BufferND { val array = DoubleArray(indices.linearSize) { offset -> Float64Field.transform(buffer[offset]) } - return BufferND(indices, DoubleBuffer(array)) + return BufferND(indices, Float64Buffer(array)) } /** diff --git a/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt b/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt index a25091ac2..16eccd79b 100644 --- a/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt +++ b/kmath-for-real/src/commonTest/kotlin/space/kscience/kmath/real/DoubleVectorTest.kt @@ -9,30 +9,30 @@ import space.kscience.kmath.linear.asMatrix import space.kscience.kmath.linear.linearSpace import space.kscience.kmath.linear.transpose import space.kscience.kmath.operations.algebra -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.test.Test import kotlin.test.assertEquals internal class DoubleVectorTest { @Test fun testSum() { - val vector1 = DoubleBuffer(5) { it.toDouble() } - val vector2 = DoubleBuffer(5) { 5 - it.toDouble() } + val vector1 = Float64Buffer(5) { it.toDouble() } + val vector2 = Float64Buffer(5) { 5 - it.toDouble() } val sum = vector1 + vector2 assertEquals(5.0, sum[2]) } @Test fun testVectorToMatrix() { - val vector = DoubleBuffer(5) { it.toDouble() } + val vector = Float64Buffer(5) { it.toDouble() } val matrix = vector.asMatrix() assertEquals(4.0, matrix[4, 0]) } @Test fun testDot() = Double.algebra.linearSpace.run { - val vector1 = DoubleBuffer(5) { it.toDouble() } - val vector2 = DoubleBuffer(5) { 5 - it.toDouble() } + val vector1 = Float64Buffer(5) { it.toDouble() } + val vector2 = Float64Buffer(5) { 5 - it.toDouble() } val matrix1 = vector1.asMatrix() val matrix2 = vector2.asMatrix().transpose() val product = matrix1 dot matrix2 diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt index 4ed4965c9..975fcd3a8 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/GaussIntegratorRuleFactory.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.integration import space.kscience.kmath.operations.mapToBuffer import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import kotlin.math.ulp import kotlin.native.concurrent.ThreadLocal @@ -32,11 +32,11 @@ public fun GaussIntegratorRuleFactory.build( val normalized: Pair, Buffer> = build(numPoints) val length = range.endInclusive - range.start - val points = normalized.first.mapToBuffer(::DoubleBuffer) { + val points = normalized.first.mapToBuffer(::Float64Buffer) { range.start + length / 2 + length / 2 * it } - val weights = normalized.second.mapToBuffer(::DoubleBuffer) { + val weights = normalized.second.mapToBuffer(::Float64Buffer) { it * length / 2 } @@ -64,8 +64,8 @@ public object GaussLegendreRuleFactory : GaussIntegratorRuleFactory { if (numPoints == 1) { // Break recursion. return Pair( - DoubleBuffer(0.0), - DoubleBuffer(0.0) + Float64Buffer(0.0), + Float64Buffer(0.0) ) } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt index 319e86cd0..1306e501f 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/SplineIntegrator.kt @@ -14,7 +14,7 @@ import space.kscience.kmath.interpolation.SplineInterpolator import space.kscience.kmath.interpolation.interpolatePolynomials import space.kscience.kmath.operations.* import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.MutableBufferFactory /** @@ -62,7 +62,7 @@ public class SplineIntegrator>( val nodes: Buffer = integrand.getFeature()?.nodes ?: run { val numPoints = integrand.getFeature()?.maxCalls ?: 100 val step = (range.endInclusive - range.start) / (numPoints - 1) - DoubleBuffer(numPoints) { i -> range.start + i * step } + Float64Buffer(numPoints) { i -> range.start + i * step } } val values = nodes.mapToBuffer(bufferFactory) { integrand.function(it) } @@ -85,15 +85,15 @@ public class SplineIntegrator>( public object DoubleSplineIntegrator : UnivariateIntegrator { override fun process(integrand: UnivariateIntegrand): UnivariateIntegrand { val range = integrand.getFeature()?.range ?: 0.0..1.0 - val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, ::DoubleBuffer) + val interpolator: PolynomialInterpolator = SplineInterpolator(Float64Field, ::Float64Buffer) val nodes: Buffer = integrand.getFeature()?.nodes ?: run { val numPoints = integrand.getFeature()?.maxCalls ?: 100 val step = (range.endInclusive - range.start) / (numPoints - 1) - DoubleBuffer(numPoints) { i -> range.start + i * step } + Float64Buffer(numPoints) { i -> range.start + i * step } } - val values = nodes.mapToBuffer(::DoubleBuffer) { integrand.function(it) } + val values = nodes.mapToBuffer(::Float64Buffer) { integrand.function(it) } val polynomials = interpolator.interpolatePolynomials(nodes, values) val res = polynomials.integrate(Float64Field, range) return integrand + IntegrandValue(res) + IntegrandCallsPerformed(integrand.calls + nodes.size) diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt index f18e86b80..e29a6c881 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/integration/UnivariateIntegrand.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.integration import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.misc.FeatureSet import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer public class UnivariateIntegrand internal constructor( override val features: FeatureSet, @@ -46,7 +46,7 @@ public class UnivariateIntegrandRanges(public val ranges: List) : IntegrandFeature { - public constructor(vararg nodes: Double) : this(DoubleBuffer(nodes)) + public constructor(vararg nodes: Double) : this(Float64Buffer(nodes)) override fun toString(): String = "UnivariateNodes($nodes)" } diff --git a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt index 76082af74..ed4657d53 100644 --- a/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt +++ b/kmath-functions/src/commonMain/kotlin/space/kscience/kmath/interpolation/SplineInterpolator.kt @@ -12,7 +12,7 @@ import space.kscience.kmath.functions.Polynomial import space.kscience.kmath.operations.Field import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.MutableBufferFactory /** @@ -80,4 +80,4 @@ public fun > Field.splineInterpolator( ): SplineInterpolator = SplineInterpolator(this, bufferFactory) public val Float64Field.splineInterpolator: SplineInterpolator - get() = SplineInterpolator(this, ::DoubleBuffer) \ No newline at end of file + get() = SplineInterpolator(this, ::Float64Buffer) \ No newline at end of file diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt index c7c202a70..33a9bcc01 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.geometry import space.kscience.kmath.complex.Quaternion import space.kscience.kmath.complex.normalized -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.testutils.assertBufferEquals import kotlin.test.Test @@ -38,15 +38,15 @@ class RotationTest { fun fromRotation() { val q = Quaternion.fromRotation(0.3.radians, Euclidean3DSpace.vector(1.0, 1.0, 1.0)) - assertBufferEquals(DoubleBuffer(0.9887711, 0.0862781, 0.0862781, 0.0862781), q) + assertBufferEquals(Float64Buffer(0.9887711, 0.0862781, 0.0862781, 0.0862781), q) } @Test fun fromEuler() { val q = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.ZXY) - assertBufferEquals(DoubleBuffer(0.9818562, 0.0342708, 0.1060205, 0.1534393), q) + assertBufferEquals(Float64Buffer(0.9818562, 0.0342708, 0.1060205, 0.1534393), q) val q1 = Quaternion.fromEuler(0.1.radians, 0.2.radians, 0.3.radians, RotationOrder.XYZ) - assertBufferEquals(DoubleBuffer(0.9818562, 0.0640713, 0.0911575, 0.1534393), q1) + assertBufferEquals(Float64Buffer(0.9818562, 0.0640713, 0.0911575, 0.1534393), q1) } } \ No newline at end of file diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt index a4ae6d935..3303fa172 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/Histogram.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.histogram import space.kscience.kmath.domains.Domain import space.kscience.kmath.linear.Point -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer /** @@ -66,9 +66,9 @@ public fun HistogramBuilder.put(point: Point): Unit = put public fun HistogramBuilder.put(vararg point: T): Unit = put(point.asBuffer()) public fun HistogramBuilder.put(vararg point: Number): Unit = - put(DoubleBuffer(point.map { it.toDouble() }.toDoubleArray())) + put(Float64Buffer(point.map { it.toDouble() }.toDoubleArray())) -public fun HistogramBuilder.put(vararg point: Double): Unit = put(DoubleBuffer(point)) +public fun HistogramBuilder.put(vararg point: Double): Unit = put(Float64Buffer(point)) public fun HistogramBuilder.fill(sequence: Iterable>): Unit = sequence.forEach { put(it) } /** diff --git a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt index fe9c3ae01..f7094f7a9 100644 --- a/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt +++ b/kmath-histograms/src/commonMain/kotlin/space/kscience/kmath/histogram/UniformHistogramGroupND.kt @@ -41,7 +41,7 @@ public class UniformHistogramGroupND>( override val shape: ShapeND = ShapeND(IntArray(binNums.size) { binNums[it] + 2 }) - private val binSize = DoubleBuffer(dimension) { (upper[it] - lower[it]) / binNums[it] } + private val binSize = Float64Buffer(dimension) { (upper[it] - lower[it]) / binNums[it] } /** * Get internal [StructureND] bin index for given axis @@ -129,7 +129,7 @@ public fun > Histogram.Companion.uniformNDFromRanges( public fun Histogram.Companion.uniformDoubleNDFromRanges( vararg ranges: ClosedFloatingPointRange, ): UniformHistogramGroupND = - uniformNDFromRanges(DoubleFieldOpsND, *ranges, bufferFactory = ::DoubleBuffer) + uniformNDFromRanges(Floa64FieldOpsND, *ranges, bufferFactory = ::Float64Buffer) /** @@ -164,4 +164,4 @@ public fun > Histogram.Companion.uniformNDFromRanges( public fun Histogram.Companion.uniformDoubleNDFromRanges( vararg ranges: Pair, Int>, ): UniformHistogramGroupND = - uniformNDFromRanges(DoubleFieldOpsND, *ranges, bufferFactory = ::DoubleBuffer) \ No newline at end of file + uniformNDFromRanges(Floa64FieldOpsND, *ranges, bufferFactory = ::Float64Buffer) \ No newline at end of file diff --git a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt index 2732efac7..e922fd423 100644 --- a/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt +++ b/kmath-optimization/src/commonMain/kotlin/space/kscience/kmath/optimization/QowOptimizer.kt @@ -9,10 +9,10 @@ import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.expressions.* import space.kscience.kmath.linear.* import space.kscience.kmath.misc.log -import space.kscience.kmath.operations.DoubleL2Norm import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Float64L2Norm import space.kscience.kmath.operations.algebra -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.math.abs @@ -63,7 +63,7 @@ public object QowOptimizer : Optimizer { * Array of dispersions in each point */ val dispersion: Point by lazy { - DoubleBuffer(problem.data.size) { d -> + Float64Buffer(problem.data.size) { d -> 1.0 / problem.weight(d).invoke(allParameters) } } @@ -148,8 +148,8 @@ public object QowOptimizer : Optimizer { * Quasi optimal weights equations values */ private fun QoWeight.getEqValues(theta: Map): Point { - val distances = DoubleBuffer(data.size) { d -> distance(d, theta) } - return DoubleBuffer(size) { s -> + val distances = Float64Buffer(data.size) { d -> distance(d, theta) } + return Float64Buffer(size) { s -> val base = (0 until data.size).sumOf { d -> distances[d] * derivs[d, s] / dispersion[d] } //Prior probability correction prior?.let { prior -> @@ -186,7 +186,7 @@ public object QowOptimizer : Optimizer { var eqvalues = getEqValues(par) //Values of the weight functions - dis = DoubleL2Norm.norm(eqvalues) // discrepancy + dis = Float64L2Norm.norm(eqvalues) // discrepancy logger?.log { "Starting discrepancy is $dis" } var i = 0 var flag = false @@ -205,7 +205,7 @@ public object QowOptimizer : Optimizer { logger?.log { "Parameter values after step are: \n\t$currentSolution" } eqvalues = getEqValues(currentSolution.freeParameters) - val currentDis = DoubleL2Norm.norm(eqvalues)// discrepancy after the step + val currentDis = Float64L2Norm.norm(eqvalues)// discrepancy after the step logger?.log { "The discrepancy after step is: $currentDis." } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomChain.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomChain.kt index 7e0ee3cd4..1cc409393 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomChain.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomChain.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.random import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.chains.Chain -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer /** * A possibly stateful chain producing random values. @@ -31,8 +31,8 @@ public fun RandomGenerator.chain(generator: suspend RandomGenerator.() -> R) * A type-specific double chunk random chain */ public class UniformDoubleChain(public val generator: RandomGenerator) : BlockingDoubleChain { - override fun nextBufferBlocking(size: Int): DoubleBuffer = generator.nextDoubleBuffer(size) - override suspend fun nextBuffer(size: Int): DoubleBuffer = nextBufferBlocking(size) + override fun nextBufferBlocking(size: Int): Float64Buffer = generator.nextDoubleBuffer(size) + override suspend fun nextBuffer(size: Int): Float64Buffer = nextBufferBlocking(size) override suspend fun fork(): UniformDoubleChain = UniformDoubleChain(generator.fork()) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomGenerator.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomGenerator.kt index f7388006e..ece6bfcc1 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomGenerator.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/random/RandomGenerator.kt @@ -5,7 +5,7 @@ package space.kscience.kmath.random -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.random.Random /** @@ -25,7 +25,7 @@ public interface RandomGenerator { /** * A chunk of doubles of given [size]. */ - public fun nextDoubleBuffer(size: Int): DoubleBuffer = DoubleBuffer(size) { nextDouble() } + public fun nextDoubleBuffer(size: Int): Float64Buffer = Float64Buffer(size) { nextDouble() } /** * Gets the next random `Int` from the random number generator. diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt index e5a48d4d8..04e1221ca 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/AhrensDieterExponentialSampler.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.math.ln import kotlin.math.pow @@ -56,7 +56,7 @@ public class AhrensDieterExponentialSampler(public val mean: Double) : Sampler + return Float64Buffer(size) { index -> if (state.isNaN()) { // Generate a pair of Gaussian numbers. val x = xs[index] diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt index 6d2899314..44834b5bf 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/KempSmallMeanPoissonSampler.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingIntChain import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Int32Buffer import kotlin.math.exp /** @@ -55,7 +55,7 @@ public class KempSmallMeanPoissonSampler internal constructor( return x } - override fun nextBufferBlocking(size: Int): IntBuffer = IntBuffer(size) { nextBlocking() } + override fun nextBufferBlocking(size: Int): Int32Buffer = Int32Buffer(size) { nextBlocking() } override suspend fun fork(): BlockingIntChain = sample(generator.fork()) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MarsagliaNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MarsagliaNormalizedGaussianSampler.kt index 3c3fe10fd..961883af5 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MarsagliaNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/MarsagliaNormalizedGaussianSampler.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.random.RandomGenerator -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.math.ln import kotlin.math.sqrt @@ -59,7 +59,7 @@ public object MarsagliaNormalizedGaussianSampler : NormalizedGaussianSampler { } } - override fun nextBufferBlocking(size: Int): DoubleBuffer = DoubleBuffer(size) { nextBlocking() } + override fun nextBufferBlocking(size: Int): Float64Buffer = Float64Buffer(size) { nextBlocking() } override suspend fun fork(): BlockingDoubleChain = sample(generator.fork()) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt index 454691e05..964a57178 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/PoissonSampler.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.chains.BlockingIntChain import space.kscience.kmath.misc.toIntExact import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.stat.Sampler -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Int32Buffer import kotlin.math.* @@ -71,7 +71,7 @@ public class SmallMeanPoissonSampler(public val mean: Double) : Sampler { return n } - override fun nextBufferBlocking(size: Int): IntBuffer = IntBuffer(size) { nextBlocking() } + override fun nextBufferBlocking(size: Int): Int32Buffer = Int32Buffer(size) { nextBlocking() } override suspend fun fork(): BlockingIntChain = sample(generator.fork()) } @@ -191,7 +191,7 @@ public class LargeMeanPoissonSampler(public val mean: Double) : Sampler { return min(y2 + y.toLong(), Int.MAX_VALUE.toLong()).toIntExact() } - override fun nextBufferBlocking(size: Int): IntBuffer = IntBuffer(size) { nextBlocking() } + override fun nextBufferBlocking(size: Int): Int32Buffer = Int32Buffer(size) { nextBlocking() } override suspend fun fork(): BlockingIntChain = sample(generator.fork()) } diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt index b87a429df..34355dca7 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/Sampler.kt @@ -12,8 +12,8 @@ import space.kscience.kmath.chains.combine import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.structures.Buffer import space.kscience.kmath.structures.BufferFactory -import space.kscience.kmath.structures.DoubleBuffer -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Float64Buffer +import space.kscience.kmath.structures.Int32Buffer import kotlin.jvm.JvmName /** @@ -62,14 +62,14 @@ public suspend fun Sampler.next(generator: RandomGenerator): T = sa */ @JvmName("sampleRealBuffer") public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = - sampleBuffer(generator, size, ::DoubleBuffer) + sampleBuffer(generator, size, ::Float64Buffer) /** * Generates [size] integer samples and chunks them into some buffers. */ @JvmName("sampleIntBuffer") public fun Sampler.sampleBuffer(generator: RandomGenerator, size: Int): Chain> = - sampleBuffer(generator, size, ::IntBuffer) + sampleBuffer(generator, size, ::Int32Buffer) /** diff --git a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/ZigguratNormalizedGaussianSampler.kt b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/ZigguratNormalizedGaussianSampler.kt index 40ce37d15..062894c90 100644 --- a/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/ZigguratNormalizedGaussianSampler.kt +++ b/kmath-stat/src/commonMain/kotlin/space/kscience/kmath/samplers/ZigguratNormalizedGaussianSampler.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.samplers import space.kscience.kmath.chains.BlockingDoubleChain import space.kscience.kmath.random.RandomGenerator -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.math.* /** @@ -63,7 +63,7 @@ public object ZigguratNormalizedGaussianSampler : NormalizedGaussianSampler { } override fun sample(generator: RandomGenerator): BlockingDoubleChain = object : BlockingDoubleChain { - override fun nextBufferBlocking(size: Int): DoubleBuffer = DoubleBuffer(size) { sampleOne(generator) } + override fun nextBufferBlocking(size: Int): Float64Buffer = Float64Buffer(size) { sampleOne(generator) } override suspend fun fork(): BlockingDoubleChain = sample(generator.fork()) } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt index 7db91722f..662469bf3 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/BroadcastDoubleTensorAlgebra.kt @@ -7,7 +7,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureND -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.broadcastTensors import space.kscience.kmath.tensors.core.internal.broadcastTo @@ -22,7 +22,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleBuffer(newThis.indices.linearSize) { i -> + val resBuffer = Float64Buffer(newThis.indices.linearSize) { i -> newThis.source[i] + newOther.source[i] } return DoubleTensor(newThis.shape, resBuffer) @@ -39,7 +39,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleBuffer(newThis.indices.linearSize) { i -> + val resBuffer = Float64Buffer(newThis.indices.linearSize) { i -> newThis.source[i] - newOther.source[i] } return DoubleTensor(newThis.shape, resBuffer) @@ -56,7 +56,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleBuffer(newThis.indices.linearSize) { i -> + val resBuffer = Float64Buffer(newThis.indices.linearSize) { i -> newThis.source[i] * newOther.source[i] } return DoubleTensor(newThis.shape, resBuffer) @@ -73,7 +73,7 @@ public object BroadcastDoubleTensorAlgebra : DoubleTensorAlgebra() { val broadcast = broadcastTensors(asDoubleTensor(), arg.asDoubleTensor()) val newThis = broadcast[0] val newOther = broadcast[1] - val resBuffer = DoubleBuffer(newThis.indices.linearSize) { i -> + val resBuffer = Float64Buffer(newThis.indices.linearSize) { i -> newThis.source[i] / newOther.source[i] } return DoubleTensor(newThis.shape, resBuffer) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt index d2c2e9d5d..8a97114c3 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensor.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.core.internal.toPrettyString public class OffsetDoubleBuffer( - override val origin: DoubleBuffer, + override val origin: Float64Buffer, private val offset: Int, override val size: Int, ) : MutableBuffer, BufferView { @@ -34,7 +34,7 @@ public class OffsetDoubleBuffer( /** * Copy only a part of buffer that belongs to this [OffsetDoubleBuffer] */ - override fun copy(): DoubleBuffer = origin.array.copyOfRange(offset, offset + size).asBuffer() + override fun copy(): Float64Buffer = origin.array.copyOfRange(offset, offset + size).asBuffer() override fun iterator(): Iterator = iterator { for (i in indices) { @@ -60,15 +60,15 @@ public fun OffsetDoubleBuffer.slice(range: IntRange): OffsetDoubleBuffer = view( /** * Map only operable content of the offset buffer */ -public inline fun OffsetDoubleBuffer.map(operation: (Double) -> Double): DoubleBuffer = - DoubleBuffer(size) { operation(get(it)) } +public inline fun OffsetDoubleBuffer.map(operation: (Double) -> Double): Float64Buffer = + Float64Buffer(size) { operation(get(it)) } public inline fun OffsetDoubleBuffer.zip( other: OffsetDoubleBuffer, operation: (l: Double, r: Double) -> Double, -): DoubleBuffer { +): Float64Buffer { require(size == other.size) { "The sizes of zipped buffers must be the same" } - return DoubleBuffer(size) { operation(get(it), other[it]) } + return Float64Buffer(size) { operation(get(it), other[it]) } } /** @@ -92,7 +92,7 @@ public open class DoubleTensor( require(linearSize == source.size) { "Source buffer size must be equal tensor size" } } - public constructor(shape: ShapeND, buffer: DoubleBuffer) : this(shape, OffsetDoubleBuffer(buffer, 0, buffer.size)) + public constructor(shape: ShapeND, buffer: Float64Buffer) : this(shape, OffsetDoubleBuffer(buffer, 0, buffer.size)) @OptIn(PerformancePitfall::class) diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt index 6bcf2753f..334a27c5b 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/DoubleTensorAlgebra.kt @@ -10,7 +10,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* -import space.kscience.kmath.operations.DoubleBufferOps +import space.kscience.kmath.operations.Float64BufferOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.structures.* import space.kscience.kmath.tensors.api.AnalyticTensorAlgebra @@ -34,7 +34,7 @@ public open class DoubleTensorAlgebra : override val elementAlgebra: Float64Field get() = Float64Field - public val bufferAlgebra: DoubleBufferOps get() = DoubleBufferOps + public val bufferAlgebra: Float64BufferOps get() = Float64BufferOps /** @@ -47,7 +47,7 @@ public open class DoubleTensorAlgebra : final override inline fun StructureND.map(transform: Float64Field.(Double) -> Double): DoubleTensor { val tensor = asDoubleTensor() //TODO remove additional copy - val array = DoubleBuffer(tensor.source.size) { Float64Field.transform(tensor.source[it]) } + val array = Float64Buffer(tensor.source.size) { Float64Field.transform(tensor.source[it]) } return DoubleTensor( tensor.shape, array, @@ -82,14 +82,14 @@ public open class DoubleTensorAlgebra : val leftTensor = left.asDoubleTensor() val rightTensor = right.asDoubleTensor() - val buffer = DoubleBuffer(leftTensor.source.size) { + val buffer = Float64Buffer(leftTensor.source.size) { Float64Field.transform(leftTensor.source[it], rightTensor.source[it]) } return DoubleTensor(leftTensor.shape, buffer) } - public inline fun StructureND.reduceElements(transform: (DoubleBuffer) -> Double): Double = + public inline fun StructureND.reduceElements(transform: (Float64Buffer) -> Double): Double = transform(asDoubleTensor().source.copy()) //TODO Add read-only DoubleBuffer wrapper. To avoid protective copy @@ -107,7 +107,7 @@ public open class DoubleTensorAlgebra : check(buffer.size == shape.linearSize) { "Inconsistent shape $shape for buffer of size ${buffer.size} provided" } - return DoubleTensor(shape, buffer.toDoubleBuffer()) + return DoubleTensor(shape, buffer.toFloat64Buffer()) } @@ -151,7 +151,7 @@ public open class DoubleTensorAlgebra : */ public fun full(value: Double, shape: ShapeND): DoubleTensor { checkNotEmptyShape(shape) - val buffer = DoubleBuffer(shape.linearSize) { value } + val buffer = Float64Buffer(shape.linearSize) { value } return DoubleTensor(shape, buffer) } @@ -163,7 +163,7 @@ public open class DoubleTensorAlgebra : */ public fun fullLike(structureND: StructureND<*>, value: Double): DoubleTensor { val shape = structureND.shape - val buffer = DoubleBuffer(structureND.indices.linearSize) { value } + val buffer = Float64Buffer(structureND.indices.linearSize) { value } return DoubleTensor(shape, buffer) } @@ -205,7 +205,7 @@ public open class DoubleTensorAlgebra : */ public fun eye(n: Int): DoubleTensor { val shape = ShapeND(n, n) - val buffer = DoubleBuffer(n * n) { 0.0 } + val buffer = Float64Buffer(n * n) { 0.0 } val res = DoubleTensor(shape, buffer) for (i in 0 until n) { res[intArrayOf(i, i)] = 1.0 @@ -353,7 +353,7 @@ public open class DoubleTensorAlgebra : */ public infix fun StructureND.matmul(other: StructureND): DoubleTensor { if (shape.size == 1 && other.shape.size == 1) { - return DoubleTensor(ShapeND(1), DoubleBuffer(times(other).sum())) + return DoubleTensor(ShapeND(1), Float64Buffer(times(other).sum())) } var penultimateDim = false @@ -529,7 +529,7 @@ public open class DoubleTensorAlgebra : val init = foldFunction(DoubleArray(1) { 0.0 }) val resTensor = DoubleTensor( resShape, - DoubleBuffer(resNumElements) { init } + Float64Buffer(resNumElements) { init } ) val dt = asDoubleTensor() for (index in resTensor.indices) { @@ -557,7 +557,7 @@ public open class DoubleTensorAlgebra : val init = foldFunction(DoubleArray(1) { 0.0 }) val resTensor = IntTensor( resShape, - IntBuffer(resNumElements) { init } + Int32Buffer(resNumElements) { init } ) for (index in resTensor.indices) { val prefix = index.take(dim).toIntArray() diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt index f028e2cbb..1066793a7 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensor.kt @@ -13,7 +13,7 @@ import space.kscience.kmath.structures.* * Default [BufferedTensor] implementation for [Int] values */ public class OffsetIntBuffer( - private val source: IntBuffer, + private val source: Int32Buffer, private val offset: Int, override val size: Int, ) : MutableBuffer { @@ -34,7 +34,7 @@ public class OffsetIntBuffer( /** * Copy only a part of buffer that belongs to this tensor */ - override fun copy(): IntBuffer = source.array.copyOfRange(offset, offset + size).asBuffer() + override fun copy(): Int32Buffer = source.array.copyOfRange(offset, offset + size).asBuffer() override fun iterator(): Iterator = iterator { for (i in indices) { @@ -53,15 +53,15 @@ public fun OffsetIntBuffer.slice(range: IntRange): OffsetIntBuffer = view(range. /** * Map only operable content of the offset buffer */ -public inline fun OffsetIntBuffer.map(operation: (Int) -> Int): IntBuffer = - IntBuffer(size) { operation(get(it)) } +public inline fun OffsetIntBuffer.map(operation: (Int) -> Int): Int32Buffer = + Int32Buffer(size) { operation(get(it)) } public inline fun OffsetIntBuffer.zip( other: OffsetIntBuffer, operation: (l: Int, r: Int) -> Int, -): IntBuffer { +): Int32Buffer { require(size == other.size) { "The sizes of zipped buffers must be the same" } - return IntBuffer(size) { operation(get(it), other[it]) } + return Int32Buffer(size) { operation(get(it), other[it]) } } /** @@ -83,7 +83,7 @@ public class IntTensor( require(linearSize == source.size) { "Source buffer size must be equal tensor size" } } - public constructor(shape: ShapeND, buffer: IntBuffer) : this(shape, OffsetIntBuffer(buffer, 0, buffer.size)) + public constructor(shape: ShapeND, buffer: Int32Buffer) : this(shape, OffsetIntBuffer(buffer, 0, buffer.size)) @OptIn(PerformancePitfall::class) override fun get(index: IntArray): Int = this.source[indices.offset(index)] diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt index a7899e84c..a09351b85 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/IntTensorAlgebra.kt @@ -37,7 +37,7 @@ public open class IntTensorAlgebra : TensorAlgebra { final override inline fun StructureND.map(transform: Int32Ring.(Int) -> Int): IntTensor { val tensor = this.asIntTensor() //TODO remove additional copy - val array = IntBuffer(tensor.source.size) { Int32Ring.transform(tensor.source[it]) } + val array = Int32Buffer(tensor.source.size) { Int32Ring.transform(tensor.source[it]) } return IntTensor( tensor.shape, array, @@ -60,7 +60,7 @@ public open class IntTensorAlgebra : TensorAlgebra { final override inline fun StructureND.mapIndexed(transform: Int32Ring.(index: IntArray, Int) -> Int): IntTensor { val tensor = this.asIntTensor() //TODO remove additional copy - val buffer = IntBuffer(tensor.source.size) { + val buffer = Int32Buffer(tensor.source.size) { Int32Ring.transform(tensor.indices.index(it), tensor.source[it]) } return IntTensor(tensor.shape, buffer) @@ -76,14 +76,14 @@ public open class IntTensorAlgebra : TensorAlgebra { val leftTensor = left.asIntTensor() val rightTensor = right.asIntTensor() - val buffer = IntBuffer(leftTensor.source.size) { + val buffer = Int32Buffer(leftTensor.source.size) { Int32Ring.transform(leftTensor.source[it], rightTensor.source[it]) } return IntTensor(leftTensor.shape, buffer) } - public inline fun StructureND.reduceElements(transform: (IntBuffer) -> Int): Int = + public inline fun StructureND.reduceElements(transform: (Int32Buffer) -> Int): Int = transform(asIntTensor().source.copy()) //TODO do we need protective copy? @@ -139,7 +139,7 @@ public open class IntTensorAlgebra : TensorAlgebra { */ public fun full(value: Int, shape: ShapeND): IntTensor { checkNotEmptyShape(shape) - val buffer = IntBuffer(shape.linearSize) { value } + val buffer = Int32Buffer(shape.linearSize) { value } return IntTensor(shape, buffer) } @@ -151,7 +151,7 @@ public open class IntTensorAlgebra : TensorAlgebra { */ public fun fullLike(structureND: StructureND<*>, value: Int): IntTensor { val shape = structureND.shape - val buffer = IntBuffer(structureND.indices.linearSize) { value } + val buffer = Int32Buffer(structureND.indices.linearSize) { value } return IntTensor(shape, buffer) } @@ -193,7 +193,7 @@ public open class IntTensorAlgebra : TensorAlgebra { */ public fun eye(n: Int): IntTensor { val shape = ShapeND(n, n) - val buffer = IntBuffer(n * n) { 0 } + val buffer = Int32Buffer(n * n) { 0 } val res = IntTensor(shape, buffer) for (i in 0 until n) { res[intArrayOf(i, i)] = 1 @@ -420,7 +420,7 @@ public open class IntTensorAlgebra : TensorAlgebra { val init = foldFunction(IntArray(1) { 0 }) val resTensor = IntTensor( resShape, - IntBuffer(resNumElements) { init } + Int32Buffer(resNumElements) { init } ) for (index in resTensor.indices) { val prefix = index.take(dim).toIntArray() diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt index a293c8da3..0c1ffdbf3 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/doubleTensorHelpers.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.* -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices import space.kscience.kmath.tensors.core.BroadcastDoubleTensorAlgebra.eye @@ -22,13 +22,13 @@ import kotlin.math.sqrt internal fun MutableStructure2D.jacobiHelper( maxIteration: Int, epsilon: Double, -): Pair> { +): Pair> { val n = rowNum val A_ = copyToTensor() val V = eye(n) - val D = DoubleBuffer(n) { get(it, it) } - val B = DoubleBuffer(n) { get(it, it) } - val Z = DoubleBuffer(n) { 0.0 } + val D = Float64Buffer(n) { get(it, it) } + val B = Float64Buffer(n) { get(it, it) } + val Z = Float64Buffer(n) { 0.0 } // assume that buffered tensor is square matrix operator fun DoubleTensor.get(i: Int, j: Int): Double { @@ -59,8 +59,8 @@ internal fun MutableStructure2D.jacobiHelper( fun jacobiIteration( a: DoubleTensor, v: DoubleTensor, - d: DoubleBuffer, - z: DoubleBuffer, + d: Float64Buffer, + z: Float64Buffer, ) { for (ip in 0 until n - 1) { for (iq in ip + 1 until n) { @@ -108,9 +108,9 @@ internal fun MutableStructure2D.jacobiHelper( } fun updateDiagonal( - d: DoubleBuffer, - z: DoubleBuffer, - b: DoubleBuffer, + d: Float64Buffer, + z: Float64Buffer, + b: Float64Buffer, ) { for (ip in 0 until d.size) { b[ip] += z[ip] @@ -137,7 +137,7 @@ internal fun MutableStructure2D.jacobiHelper( /** * Concatenate a list of arrays */ -internal fun List.concat(): DoubleBuffer { +internal fun List.concat(): Float64Buffer { val array = DoubleArray(sumOf { it.size }) var pointer = 0 while (pointer < array.size) { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt index 35f0bf324..c47d94a92 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/intTensorHelpers.kt @@ -9,7 +9,7 @@ import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.first import space.kscience.kmath.nd.last import space.kscience.kmath.operations.asSequence -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Int32Buffer import space.kscience.kmath.structures.VirtualBuffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices @@ -19,7 +19,7 @@ import space.kscience.kmath.tensors.core.OffsetIntBuffer /** * Concatenate a list of arrays */ -internal fun List.concat(): IntBuffer { +internal fun List.concat(): Int32Buffer { val array = IntArray(sumOf { it.size }) var pointer = 0 while (pointer < array.size) { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt index 7a96001c6..272af41d5 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/linUtils.kt @@ -7,8 +7,8 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer -import space.kscience.kmath.structures.IntBuffer +import space.kscience.kmath.structures.Float64Buffer +import space.kscience.kmath.structures.Int32Buffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.structures.indices import space.kscience.kmath.tensors.core.* @@ -97,7 +97,7 @@ internal fun StructureND.setUpPivots(): IntTensor { return IntTensor( ShapeND(pivotsShape), - IntBuffer(pivotsShape.reduce(Int::times)) { 0 } + Int32Buffer(pivotsShape.reduce(Int::times)) { 0 } ) } @@ -241,10 +241,10 @@ internal fun DoubleTensorAlgebra.svd1d(a: DoubleTensor, epsilon: Double = 1e-10) val b: DoubleTensor if (n > m) { b = a.transposed(0, 1).dot(a) - v = DoubleTensor(ShapeND(m), DoubleBuffer.randomUnitVector(m, 0)) + v = DoubleTensor(ShapeND(m), Float64Buffer.randomUnitVector(m, 0)) } else { b = a.dot(a.transposed(0, 1)) - v = DoubleTensor(ShapeND(n), DoubleBuffer.randomUnitVector(n, 0)) + v = DoubleTensor(ShapeND(n), Float64Buffer.randomUnitVector(n, 0)) } var lastV: DoubleTensor diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt index 2709ac474..d48086d57 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/internal/utils.kt @@ -8,22 +8,22 @@ package space.kscience.kmath.tensors.core.internal import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.asList import space.kscience.kmath.nd.last -import space.kscience.kmath.operations.DoubleBufferOps.Companion.map +import space.kscience.kmath.operations.Float64BufferOps.Companion.map import space.kscience.kmath.random.RandomGenerator import space.kscience.kmath.samplers.GaussianSampler -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.tensors.core.BufferedTensor import space.kscience.kmath.tensors.core.DoubleTensor import kotlin.math.* -internal fun DoubleBuffer.Companion.randomNormals(n: Int, seed: Long): DoubleBuffer { +internal fun Float64Buffer.Companion.randomNormals(n: Int, seed: Long): Float64Buffer { val distribution = GaussianSampler(0.0, 1.0) val generator = RandomGenerator.default(seed) return distribution.sample(generator).nextBufferBlocking(n) } -internal fun DoubleBuffer.Companion.randomUnitVector(n: Int, seed: Long): DoubleBuffer { - val unnorm: DoubleBuffer = randomNormals(n, seed) +internal fun Float64Buffer.Companion.randomUnitVector(n: Int, seed: Long): Float64Buffer { + val unnorm: Float64Buffer = randomNormals(n, seed) val norm = sqrt(unnorm.array.sumOf { it * it }) return unnorm.map { it / norm } } diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt index 88ffb0bfe..1b630a0ef 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorOps.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.* import space.kscience.kmath.operations.covariance import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.tensors.api.Tensor import space.kscience.kmath.tensors.core.internal.* import kotlin.math.min @@ -24,7 +24,7 @@ import kotlin.math.sign * with `0.0` mean and `1.0` standard deviation. */ public fun DoubleTensorAlgebra.randomNormal(shape: ShapeND, seed: Long = 0): DoubleTensor = - fromBuffer(shape, DoubleBuffer.randomNormals(shape.linearSize, seed)) + fromBuffer(shape, Float64Buffer.randomNormals(shape.linearSize, seed)) /** * Returns a tensor with the same shape as `input` of random numbers drawn from normal distributions @@ -36,7 +36,7 @@ public fun DoubleTensorAlgebra.randomNormal(shape: ShapeND, seed: Long = 0): Dou * with `0.0` mean and `1.0` standard deviation. */ public fun DoubleTensorAlgebra.randomNormalLike(structure: WithShape, seed: Long = 0): DoubleTensor = - DoubleTensor(structure.shape, DoubleBuffer.randomNormals(structure.shape.linearSize, seed)) + DoubleTensor(structure.shape, Float64Buffer.randomNormals(structure.shape.linearSize, seed)) /** * Concatenates a sequence of tensors with equal shapes along the first dimension. @@ -336,7 +336,7 @@ public fun DoubleTensorAlgebra.detLU(structureND: StructureND, epsilon: set(n - 2, 1) }) - val resBuffer = DoubleBuffer(detTensorShape.linearSize) { 0.0 } + val resBuffer = Float64Buffer(detTensorShape.linearSize) { 0.0 } val detTensor = DoubleTensor( detTensorShape, @@ -389,7 +389,7 @@ public fun DoubleTensorAlgebra.covariance(vectors: List>): Double check(vectors.all { it.size == m }) { "Vectors must have same shapes" } val resTensor = DoubleTensor( ShapeND(n, n), - DoubleBuffer(n * n) { 0.0 } + Float64Buffer(n * n) { 0.0 } ) for (i in 0 until n) { for (j in 0 until n) { diff --git a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt index 3b0d15400..52a530652 100644 --- a/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt +++ b/kmath-tensors/src/commonMain/kotlin/space/kscience/kmath/tensors/core/tensorTransform.kt @@ -6,7 +6,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.nd.* -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.asBuffer import space.kscience.kmath.tensors.api.Tensor @@ -16,7 +16,7 @@ import space.kscience.kmath.tensors.api.Tensor */ public fun StructureND.copyToTensor(): DoubleTensor = if (this is DoubleTensor) { DoubleTensor(shape, source.copy()) -} else if (this is DoubleBufferND && indices is RowStrides) { +} else if (this is Float64BufferND && indices is RowStrides) { DoubleTensor(shape, buffer.copy()) } else { DoubleTensor( @@ -29,7 +29,7 @@ public fun StructureND.toDoubleTensor(): DoubleTensor { return if (this is IntTensor) { DoubleTensor( shape, - DoubleBuffer(linearSize) { source[it].toDouble() } + Float64Buffer(linearSize) { source[it].toDouble() } ) } else { val tensor = DoubleTensorAlgebra.zeroesLike(this) @@ -45,7 +45,7 @@ public fun StructureND.toDoubleTensor(): DoubleTensor { */ public fun StructureND.asDoubleTensor(): DoubleTensor = if (this is DoubleTensor) { this -} else if (this is DoubleBufferND && indices is RowStrides) { +} else if (this is Float64BufferND && indices is RowStrides) { DoubleTensor(shape, buffer) } else { copyToTensor() diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt index 811fc1117..3081639af 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/TestDoubleTensor.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.toDoubleArray import space.kscience.kmath.tensors.core.internal.matrixSequence import space.kscience.kmath.testutils.assertBufferEquals @@ -65,10 +65,10 @@ internal class TestDoubleTensor { fun testNoBufferProtocol() { // create buffer - val doubleArray = DoubleBuffer(1.0, 2.0, 3.0) + val doubleArray = Float64Buffer(1.0, 2.0, 3.0) // create ND buffers, no data is copied - val ndArray: MutableBufferND = DoubleBufferND(ColumnStrides(ShapeND(3)), doubleArray) + val ndArray: MutableBufferND = Float64BufferND(ColumnStrides(ShapeND(3)), doubleArray) // map to tensors val tensorArray = ndArray.asDoubleTensor() // Data is copied because of strides change. @@ -95,8 +95,8 @@ internal class TestDoubleTensor { val tensor: DoubleTensor = structureND(ShapeND(3, 3)) { (i, j) -> (i - j).toDouble() } //println(tensor.toPrettyString()) val tensor2d = tensor.asDoubleTensor2D() - assertBufferEquals(DoubleBuffer(1.0, 0.0, -1.0), tensor2d.rows[1]) - assertBufferEquals(DoubleBuffer(-2.0, -1.0, 0.0), tensor2d.columns[2]) + assertBufferEquals(Float64Buffer(1.0, 0.0, -1.0), tensor2d.rows[1]) + assertBufferEquals(Float64Buffer(-2.0, -1.0, 0.0), tensor2d.columns[2]) } @Test diff --git a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt index e9fc7fb9c..c3127ed97 100644 --- a/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt +++ b/kmath-tensors/src/commonTest/kotlin/space/kscience/kmath/tensors/core/offsetBufferEquality.kt @@ -6,13 +6,13 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.structures.Buffer -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.structures.indices import kotlin.jvm.JvmName /** - * Simplified [DoubleBuffer] to array comparison + * Simplified [Float64Buffer] to array comparison */ public fun OffsetDoubleBuffer.contentEquals(vararg doubles: Double): Boolean = indices.all { get(it) == doubles[it] } diff --git a/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt b/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt index 4c039190a..0d0332395 100644 --- a/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt +++ b/kmath-tensors/src/jvmTest/kotlin/space/kscience/kmath/tensors/core/TestLmAlgorithm.kt @@ -8,7 +8,7 @@ package space.kscience.kmath.tensors.core import space.kscience.kmath.PerformancePitfall import space.kscience.kmath.nd.* import space.kscience.kmath.operations.invoke -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.test.Test import kotlin.test.assertEquals @@ -283,7 +283,7 @@ class TestLmAlgorithm { assertEquals(4858, result.funcCalls) assertEquals(5.14347, result.resultLambda, 1e-5) assertEquals(result.typeOfConvergence, TypeOfConvergence.InParameters) - val expectedParameters = DoubleBuffer( + val expectedParameters = Float64Buffer( -23.6412, -16.7402, -21.5705, diff --git a/test-utils/src/commonMain/kotlin/bufferEquality.kt b/test-utils/src/commonMain/kotlin/bufferEquality.kt index 9e4d9ec22..c4485abbd 100644 --- a/test-utils/src/commonMain/kotlin/bufferEquality.kt +++ b/test-utils/src/commonMain/kotlin/bufferEquality.kt @@ -5,16 +5,16 @@ package space.kscience.kmath.testutils -import space.kscience.kmath.structures.DoubleBuffer +import space.kscience.kmath.structures.Float64Buffer import kotlin.jvm.JvmName /** - * Simplified [DoubleBuffer] to array comparison + * Simplified [Float64Buffer] to array comparison */ -public fun DoubleBuffer.contentEquals(vararg doubles: Double): Boolean = array.contentEquals(doubles) +public fun Float64Buffer.contentEquals(vararg doubles: Double): Boolean = array.contentEquals(doubles) @JvmName("contentEqualsArray") -public infix fun DoubleBuffer.contentEquals(otherArray: DoubleArray): Boolean = array.contentEquals(otherArray) +public infix fun Float64Buffer.contentEquals(otherArray: DoubleArray): Boolean = array.contentEquals(otherArray) @JvmName("contentEqualsBuffer") -public infix fun DoubleBuffer.contentEquals(otherBuffer: DoubleBuffer): Boolean = array.contentEquals(otherBuffer.array) \ No newline at end of file +public infix fun Float64Buffer.contentEquals(otherBuffer: Float64Buffer): Boolean = array.contentEquals(otherBuffer.array) \ No newline at end of file From efb853c1bcd0ad9aa3f7bf8d5b978ef8fd74843d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 12 Aug 2023 13:16:18 +0300 Subject: [PATCH 33/34] Refactor geometry --- CHANGELOG.md | 5 +- .../kscience/kmath/complex/Quaternion.kt | 35 +++--- .../kscience/kmath/complex/QuaternionTest.kt | 24 ++-- .../kscience/kmath/operations/Algebra.kt | 8 +- .../kmath/operations/NumericAlgebra.kt | 4 +- .../kmath/operations/OptionalOperations.kt | 4 +- .../space/kscience/kmath/real/DoubleVector.kt | 1 - .../space/kscience/kmath/geometry/Line.kt | 2 + .../space/kscience/kmath/geometry/Vector2D.kt | 29 +++++ .../space/kscience/kmath/geometry/Vector3D.kt | 41 +++++++ .../geometry/{ => euclidean2d}/Circle2D.kt | 9 +- .../geometry/euclidean2d/Float32Space2D.kt | 79 +++++++++++++ .../Float64Space2D.kt} | 33 ++---- .../geometry/{ => euclidean2d}/Polygon.kt | 4 +- .../geometry/euclidean3d/Float32Space3D.kt | 108 ++++++++++++++++++ .../Float64Space3D.kt} | 71 ++++-------- .../geometry/{ => euclidean3d}/rotations3D.kt | 21 ++-- .../kscience/kmath/geometry/floatPrecision.kt | 8 +- .../kmath/geometry/quaternionOperations.kt | 55 +++++++++ .../kmath/geometry/Euclidean2DSpaceTest.kt | 11 +- .../kmath/geometry/Euclidean3DSpaceTest.kt | 17 +-- .../kmath/geometry/ProjectionAlongTest.kt | 10 +- .../kmath/geometry/ProjectionOntoLineTest.kt | 12 +- .../kscience/kmath/geometry/RotationTest.kt | 5 +- .../kscience/kmath/geometry/Vector2DTest.kt | 3 +- .../kscience/kmath/geometry/Vector3DTest.kt | 3 +- .../kscience/kmath/geometry/testUtils.kt | 2 + .../tensorflow/DoubleTensorFlowAlgebra.kt | 4 +- 28 files changed, 454 insertions(+), 154 deletions(-) create mode 100644 kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector2D.kt create mode 100644 kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt rename kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/{ => euclidean2d}/Circle2D.kt (56%) create mode 100644 kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt rename kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/{Euclidean2DSpace.kt => euclidean2d/Float64Space2D.kt} (75%) rename kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/{ => euclidean2d}/Polygon.kt (74%) create mode 100644 kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt rename kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/{Euclidean3DSpace.kt => euclidean3d/Float64Space3D.kt} (66%) rename kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/{ => euclidean3d}/rotations3D.kt (89%) create mode 100644 kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9235cfd57..27b67a4d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,14 @@ ## Unreleased ### Added -- Integer divistion algebras +- Integer division algebras +- Float32 geometries ### Changed - Default naming for algebra and buffers now uses IntXX/FloatXX notation instead of Java types. - Remove unnecessary inlines in basic algebras. +- QuaternionField -> QuaternionAlgebra and does not implement `Field` anymore since it is non-commutative +- kmath-geometry is split into `euclidean2d` and `euclidean3d` ### Deprecated diff --git a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt index d4259c4dc..8f3d15a26 100644 --- a/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt +++ b/kmath-complex/src/commonMain/kotlin/space/kscience/kmath/complex/Quaternion.kt @@ -122,16 +122,17 @@ public val Quaternion.reciprocal: Quaternion /** * Produce a normalized version of this quaternion */ -public fun Quaternion.normalized(): Quaternion = with(QuaternionField){ this@normalized / norm(this@normalized) } +public fun Quaternion.normalized(): Quaternion = with(QuaternionAlgebra){ this@normalized / norm(this@normalized) } /** * A field of [Quaternion]. */ @OptIn(UnstableKMathAPI::class) -public object QuaternionField : Field, Norm, PowerOperations, +public object QuaternionAlgebra : Group, Norm, PowerOperations, ExponentialOperations, NumbersAddOps, ScaleOperations { + override val zero: Quaternion = Quaternion(0.0) - override val one: Quaternion = Quaternion(1.0) + public val one: Quaternion = Quaternion(1.0) /** * The `i` quaternion unit. @@ -154,14 +155,16 @@ public object QuaternionField : Field, Norm, Pow override fun scale(a: Quaternion, value: Double): Quaternion = Quaternion(a.w * value, a.x * value, a.y * value, a.z * value) - override fun multiply(left: Quaternion, right: Quaternion): Quaternion = Quaternion( + public fun multiply(left: Quaternion, right: Quaternion): Quaternion = Quaternion( left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z, left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y, left.w * right.y - left.x * right.z + left.y * right.w + left.z * right.x, left.w * right.z + left.x * right.y - left.y * right.x + left.z * right.w, ) - override fun divide(left: Quaternion, right: Quaternion): Quaternion { + public operator fun Quaternion.times(other: Quaternion): Quaternion = multiply(this, other) + + public fun divide(left: Quaternion, right: Quaternion): Quaternion { val s = right.w * right.w + right.x * right.x + right.y * right.y + right.z * right.z return Quaternion( @@ -172,14 +175,17 @@ public object QuaternionField : Field, Norm, Pow ) } + public operator fun Quaternion.div(other: Quaternion): Quaternion = divide(this, other) + + override fun power(arg: Quaternion, pow: Number): Quaternion { - if (pow is Int) return pwr(arg, pow) - if (floor(pow.toDouble()) == pow.toDouble()) return pwr(arg, pow.toInt()) + if (pow is Int) return power(arg, pow) + if (floor(pow.toDouble()) == pow.toDouble()) return power(arg, pow.toInt()) return exp(pow * ln(arg)) } - private fun pwr(x: Quaternion, a: Int): Quaternion = when { - a < 0 -> -(pwr(x, -a)) + private fun power(x: Quaternion, a: Int): Quaternion = when { + a < 0 -> -(power(x, -a)) a == 0 -> one a == 1 -> x a == 2 -> pwr2(x) @@ -243,14 +249,15 @@ public object QuaternionField : Field, Norm, Pow return Quaternion(ln(n), th * arg.x, th * arg.y, th * arg.z) } - override operator fun Number.plus(other: Quaternion): Quaternion = + public override operator fun Number.plus(other: Quaternion): Quaternion = Quaternion(toDouble() + other.w, other.x, other.y, other.z) - override operator fun Number.minus(other: Quaternion): Quaternion = + public override operator fun Number.minus(other: Quaternion): Quaternion = Quaternion(toDouble() - other.w, -other.x, -other.y, -other.z) - override operator fun Quaternion.plus(other: Number): Quaternion = Quaternion(w + other.toDouble(), x, y, z) - override operator fun Quaternion.minus(other: Number): Quaternion = Quaternion(w - other.toDouble(), x, y, z) + public override operator fun Quaternion.plus(other: Number): Quaternion = Quaternion(w + other.toDouble(), x, y, z) + + public override operator fun Quaternion.minus(other: Number): Quaternion = Quaternion(w - other.toDouble(), x, y, z) override operator fun Number.times(arg: Quaternion): Quaternion = Quaternion(toDouble() * arg.w, toDouble() * arg.x, toDouble() * arg.y, toDouble() * arg.z) @@ -275,7 +282,7 @@ public object QuaternionField : Field, Norm, Pow override fun sinh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / 2.0 override fun cosh(arg: Quaternion): Quaternion = (exp(arg) + exp(-arg)) / 2.0 override fun tanh(arg: Quaternion): Quaternion = (exp(arg) - exp(-arg)) / (exp(-arg) + exp(arg)) - override fun asinh(arg: Quaternion): Quaternion = ln(sqrt(arg * arg + one) + arg) + override fun asinh(arg: Quaternion): Quaternion = ln(sqrt(pwr2(arg) + one) + arg) override fun acosh(arg: Quaternion): Quaternion = ln(arg + sqrt((arg - one) * (arg + one))) override fun atanh(arg: Quaternion): Quaternion = (ln(arg + one) - ln(one - arg)) / 2.0 } diff --git a/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt b/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt index fd0fd46a7..1b371adca 100644 --- a/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt +++ b/kmath-complex/src/commonTest/kotlin/space/kscience/kmath/complex/QuaternionTest.kt @@ -14,20 +14,20 @@ internal class QuaternionTest { @Test fun testNorm() { - assertEquals(2.0, QuaternionField.norm(Quaternion(1.0, 1.0, 1.0, 1.0))) + assertEquals(2.0, QuaternionAlgebra.norm(Quaternion(1.0, 1.0, 1.0, 1.0))) } @Test - fun testInverse() = QuaternionField { + fun testInverse() = QuaternionAlgebra { val q = Quaternion(1.0, 2.0, -3.0, 4.0) assertBufferEquals(one, q * q.reciprocal, 1e-4) } @Test fun testAddition() { - assertEquals(Quaternion(42, 42), QuaternionField { Quaternion(16, 16) + Quaternion(26, 26) }) - assertEquals(Quaternion(42, 16), QuaternionField { Quaternion(16, 16) + 26 }) - assertEquals(Quaternion(42, 16), QuaternionField { 26 + Quaternion(16, 16) }) + assertEquals(Quaternion(42, 42), QuaternionAlgebra { Quaternion(16, 16) + Quaternion(26, 26) }) + assertEquals(Quaternion(42, 16), QuaternionAlgebra { Quaternion(16, 16) + 26 }) + assertEquals(Quaternion(42, 16), QuaternionAlgebra { 26 + Quaternion(16, 16) }) } // @Test @@ -39,9 +39,9 @@ internal class QuaternionTest { @Test fun testMultiplication() { - assertEquals(Quaternion(42, 42), QuaternionField { Quaternion(4.2, 0) * Quaternion(10, 10) }) - assertEquals(Quaternion(42, 21), QuaternionField { Quaternion(4.2, 2.1) * 10 }) - assertEquals(Quaternion(42, 21), QuaternionField { 10 * Quaternion(4.2, 2.1) }) + assertEquals(Quaternion(42, 42), QuaternionAlgebra { Quaternion(4.2, 0) * Quaternion(10, 10) }) + assertEquals(Quaternion(42, 21), QuaternionAlgebra { Quaternion(4.2, 2.1) * 10 }) + assertEquals(Quaternion(42, 21), QuaternionAlgebra { 10 * Quaternion(4.2, 2.1) }) } // @Test @@ -53,11 +53,11 @@ internal class QuaternionTest { @Test fun testPower() { - assertEquals(QuaternionField.zero, QuaternionField { zero pow 2 }) - assertEquals(QuaternionField.zero, QuaternionField { zero pow 2 }) + assertEquals(QuaternionAlgebra.zero, QuaternionAlgebra { zero pow 2 }) + assertEquals(QuaternionAlgebra.zero, QuaternionAlgebra { zero pow 2 }) assertEquals( - QuaternionField { i * 8 }.let { it.x.toInt() to it.w.toInt() }, - QuaternionField { Quaternion(2, 2) pow 2 }.let { it.x.toInt() to it.w.toInt() }) + QuaternionAlgebra { i * 8 }.let { it.x.toInt() to it.w.toInt() }, + QuaternionAlgebra { Quaternion(2, 2) pow 2 }.let { it.x.toInt() to it.w.toInt() }) } } diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt index 0960ab023..67f37ed62 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/Algebra.kt @@ -300,7 +300,7 @@ public interface Ring : Group, RingOps { * commutative operations [add] and [multiply]; binary operation [divide] as multiplication of left operand by * reciprocal of right one. * - * @param T the type of element of this semifield. + * @param T the type of the semifield element. */ public interface FieldOps : RingOps { /** @@ -336,10 +336,12 @@ public interface FieldOps : RingOps { /** * Represents field i.e., algebraic structure with three operations: associative, commutative addition and - * multiplication, and division. **This interface differs from the eponymous mathematical definition: fields in KMath + * multiplication, and division. + * + * **This interface differs from the eponymous mathematical definition: fields in KMath * also support associative multiplication by scalar.** * - * @param T the type of element of this field. + * @param T the type of the field element. */ public interface Field : Ring, FieldOps, ScaleOperations, NumericAlgebra { override fun number(value: Number): T = scale(one, value.toDouble()) diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt index 9bcfb00a2..06ad68298 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/NumericAlgebra.kt @@ -150,7 +150,7 @@ public interface ScaleOperations : Algebra { * TODO to be removed and replaced by extensions after multiple receivers are there */ @UnstableKMathAPI -public interface NumbersAddOps : RingOps, NumericAlgebra { +public interface NumbersAddOps : GroupOps, NumericAlgebra { /** * Addition of element and scalar. * @@ -177,7 +177,7 @@ public interface NumbersAddOps : RingOps, NumericAlgebra { public operator fun T.minus(other: Number): T = this - number(other) /** - * Subtraction of number from element. + * Subtraction of number from the element. * * @receiver the minuend. * @param other the subtrahend. diff --git a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt index c24394add..48cac2870 100644 --- a/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt +++ b/kmath-core/src/commonMain/kotlin/space/kscience/kmath/operations/OptionalOperations.kt @@ -82,9 +82,9 @@ public expect fun Number.isInteger(): Boolean /** * A context extension to include power operations based on exponentiation. * - * @param T the type of element of this structure. + * @param T the type of this structure element */ -public interface PowerOperations : FieldOps { +public interface PowerOperations: Algebra { /** * Raises [arg] to a power if possible (negative number could not be raised to a fractional power). diff --git a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt index 108b840dd..d13528636 100644 --- a/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt +++ b/kmath-for-real/src/commonMain/kotlin/space/kscience/kmath/real/DoubleVector.kt @@ -16,7 +16,6 @@ import kotlin.math.pow public typealias DoubleVector = Point -@Suppress("FunctionName") public fun DoubleVector(vararg doubles: Double): DoubleVector = doubles.asBuffer() /** diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt index a7f6ae35d..92d31a7cb 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Line.kt @@ -7,6 +7,8 @@ package space.kscience.kmath.geometry import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import space.kscience.kmath.geometry.euclidean2d.DoubleVector2D +import space.kscience.kmath.geometry.euclidean3d.DoubleVector3D /** * A line formed by [start] vector of start and a [direction] vector. Direction vector is not necessarily normalized, diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector2D.kt new file mode 100644 index 000000000..9eced7ba9 --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector2D.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2018-2023 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.geometry + +import space.kscience.kmath.linear.Point + +public interface Vector2D : Point, Vector { + public val x: T + public val y: T + override val size: Int get() = 2 + + override operator fun get(index: Int): T = when (index) { + 0 -> x + 1 -> y + else -> error("Accessing outside of point bounds") + } + + override operator fun iterator(): Iterator = iterator { + yield(x) + yield(y) + } +} + + +public operator fun Vector2D.component1(): T = x +public operator fun Vector2D.component2(): T = y \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt new file mode 100644 index 000000000..7e7c6c4ed --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Vector3D.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2018-2023 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.geometry + +import space.kscience.kmath.linear.Point +import space.kscience.kmath.structures.Buffer + +public interface Vector3D : Point, Vector { + public val x: T + public val y: T + public val z: T + override val size: Int get() = 3 + + override operator fun get(index: Int): T = when (index) { + 0 -> x + 1 -> y + 2 -> z + else -> error("Accessing outside of point bounds") + } + + override operator fun iterator(): Iterator = listOf(x, y, z).iterator() +} + +public operator fun Vector3D.component1(): T = x +public operator fun Vector3D.component2(): T = y +public operator fun Vector3D.component3(): T = z + +public fun Buffer.asVector3D(): Vector3D = object : Vector3D { + init { + require(this@asVector3D.size == 3) { "Buffer of size 3 is required for Vector3D" } + } + + override val x: T get() = this@asVector3D[0] + override val y: T get() = this@asVector3D[1] + override val z: T get() = this@asVector3D[2] + + override fun toString(): String = this@asVector3D.toString() +} \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Circle2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt similarity index 56% rename from kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Circle2D.kt rename to kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt index d37ed45c0..7841be8ff 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Circle2D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Circle2D.kt @@ -1,20 +1,19 @@ /* - * Copyright 2018-2022 KMath contributors. + * Copyright 2018-2023 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.geometry +package space.kscience.kmath.geometry.euclidean2d import kotlinx.serialization.Serializable -import space.kscience.kmath.geometry.Euclidean2DSpace.distanceTo -import kotlin.math.* +import kotlin.math.PI /** * A circle in 2D space */ @Serializable public data class Circle2D( - @Serializable(Euclidean2DSpace.VectorSerializer::class) public val center: DoubleVector2D, + @Serializable(Float64Space2D.VectorSerializer::class) public val center: DoubleVector2D, public val radius: Double ) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt new file mode 100644 index 000000000..89c3dc204 --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float32Space2D.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2018-2023 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.geometry.euclidean2d + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import space.kscience.kmath.geometry.GeometrySpace +import space.kscience.kmath.geometry.Vector2D +import space.kscience.kmath.operations.Float32Field +import space.kscience.kmath.operations.ScaleOperations +import kotlin.math.pow +import kotlin.math.sqrt + +@Serializable(Float32Space2D.VectorSerializer::class) +public interface Float32Vector2D: Vector2D + + +public object Float32Space2D : + GeometrySpace, + ScaleOperations { + + @Serializable + @SerialName("Float32Vector2D") + private data class Vector2DImpl( + override val x: Float, + override val y: Float, + ) : Float32Vector2D + + public object VectorSerializer : KSerializer { + private val proxySerializer = Vector2DImpl.serializer() + override val descriptor: SerialDescriptor get() = proxySerializer.descriptor + + override fun deserialize(decoder: Decoder): Float32Vector2D = decoder.decodeSerializableValue(proxySerializer) + + override fun serialize(encoder: Encoder, value: Float32Vector2D) { + val vector = value as? Vector2DImpl ?: Vector2DImpl(value.x, value.y) + encoder.encodeSerializableValue(proxySerializer, vector) + } + } + + public fun vector(x: Float, y: Float): Float32Vector2D = + Vector2DImpl(x, y) + + public fun vector(x: Number, y: Number): Float32Vector2D = + vector(x.toFloat(), y.toFloat()) + + override val zero: Float32Vector2D by lazy { vector(0f, 0f) } + + override fun norm(arg: Float32Vector2D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2)).toDouble() + + public fun Float32Vector2D.norm(): Double = norm(this) + + override fun Float32Vector2D.unaryMinus(): Float32Vector2D = vector(-x, -y) + + override fun Float32Vector2D.distanceTo(other: Float32Vector2D): Double = (this - other).norm() + + override fun add(left: Float32Vector2D, right: Float32Vector2D): Float32Vector2D = + vector(left.x + right.x, left.y + right.y) + + override fun scale(a: Float32Vector2D, value: Double): Float32Vector2D = + vector(a.x * value, a.y * value) + + override fun Float32Vector2D.dot(other: Float32Vector2D): Double = + (x * other.x + y * other.y).toDouble() + + public val xAxis: Float32Vector2D = vector(1.0, 0.0) + public val yAxis: Float32Vector2D = vector(0.0, 1.0) +} + +public fun Float32Vector2D(x: Number, y: Number): Float32Vector2D = Float32Space2D.vector(x, y) + +public val Float32Field.euclidean2D: Float32Space2D get() = Float32Space2D diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt similarity index 75% rename from kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt rename to kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt index 3df8dba7b..030a46185 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Float64Space2D.kt @@ -1,9 +1,9 @@ /* - * Copyright 2018-2022 KMath contributors. + * Copyright 2018-2023 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.geometry +package space.kscience.kmath.geometry.euclidean2d import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -11,43 +11,26 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import space.kscience.kmath.linear.Point +import space.kscience.kmath.geometry.GeometrySpace +import space.kscience.kmath.geometry.Vector2D +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.ScaleOperations import kotlin.math.pow import kotlin.math.sqrt -public interface Vector2D : Point, Vector { - public val x: T - public val y: T - override val size: Int get() = 2 - override operator fun get(index: Int): T = when (index) { - 0 -> x - 1 -> y - else -> error("Accessing outside of point bounds") - } - - override operator fun iterator(): Iterator = iterator { - yield(x) - yield(y) - } -} - - -public operator fun Vector2D.component1(): T = x -public operator fun Vector2D.component2(): T = y public typealias DoubleVector2D = Vector2D public typealias Float64Vector2D = Vector2D -public val Vector2D.r: Double get() = Euclidean2DSpace.norm(this) +public val Vector2D.r: Double get() = Float64Space2D.norm(this) /** * 2D Euclidean space */ -public object Euclidean2DSpace : GeometrySpace, +public object Float64Space2D : GeometrySpace, ScaleOperations, Norm { @@ -88,3 +71,5 @@ public object Euclidean2DSpace : GeometrySpace, public val xAxis: DoubleVector2D = vector(1.0, 0.0) public val yAxis: DoubleVector2D = vector(0.0, 1.0) } + +public val Float64Field.euclidean2D: Float64Space2D get() = Float64Space2D \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Polygon.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Polygon.kt similarity index 74% rename from kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Polygon.kt rename to kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Polygon.kt index 20f4a031e..85b377a56 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Polygon.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean2d/Polygon.kt @@ -3,7 +3,9 @@ * 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.geometry +package space.kscience.kmath.geometry.euclidean2d + +import space.kscience.kmath.geometry.Vector2D /** diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt new file mode 100644 index 000000000..1413a885b --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float32Space3D.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2018-2023 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.geometry.euclidean3d + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import space.kscience.kmath.geometry.GeometrySpace +import space.kscience.kmath.geometry.Vector3D +import space.kscience.kmath.operations.Float32Field +import space.kscience.kmath.operations.ScaleOperations +import kotlin.math.pow +import kotlin.math.sqrt + +@Serializable(Float32Space3D.VectorSerializer::class) +public interface Float32Vector3D: Vector3D + + +public object Float32Space3D : + GeometrySpace, + ScaleOperations{ + + @Serializable + @SerialName("Float32Vector3D") + private data class Vector3DImpl( + override val x: Float, + override val y: Float, + override val z: Float, + ) : Float32Vector3D + + public object VectorSerializer : KSerializer { + private val proxySerializer = Vector3DImpl.serializer() + override val descriptor: SerialDescriptor get() = proxySerializer.descriptor + + override fun deserialize(decoder: Decoder): Float32Vector3D = decoder.decodeSerializableValue(proxySerializer) + + override fun serialize(encoder: Encoder, value: Float32Vector3D) { + val vector = value as? Vector3DImpl ?: Vector3DImpl(value.x, value.y, value.z) + encoder.encodeSerializableValue(proxySerializer, vector) + } + } + + public fun vector(x: Float, y: Float, z: Float): Float32Vector3D = + Vector3DImpl(x, y, z) + + public fun vector(x: Number, y: Number, z: Number): Float32Vector3D = + vector(x.toFloat(), y.toFloat(), z.toFloat()) + + override val zero: Float32Vector3D by lazy { vector(0.0, 0.0, 0.0) } + + override fun norm(arg: Float32Vector3D): Double = sqrt(arg.x.pow(2) + arg.y.pow(2) + arg.z.pow(2)).toDouble() + + public fun Float32Vector3D.norm(): Double = norm(this) + + override fun Float32Vector3D.unaryMinus(): Float32Vector3D = vector(-x, -y, -z) + + override fun Float32Vector3D.distanceTo(other: Float32Vector3D): Double = (this - other).norm() + + override fun add(left: Float32Vector3D, right: Float32Vector3D): Float32Vector3D = + vector(left.x + right.x, left.y + right.y, left.z + right.z) + + override fun scale(a: Float32Vector3D, value: Double): Float32Vector3D = + vector(a.x * value, a.y * value, a.z * value) + + override fun Float32Vector3D.dot(other: Float32Vector3D): Double = + (x * other.x + y * other.y + z * other.z).toDouble() + + /** + * Compute vector product of [first] and [second]. The basis is assumed to be right-handed. + */ + public fun vectorProduct( + first: Float32Vector3D, + second: Float32Vector3D, + ): Float32Vector3D { + var x = 0.0 + var y = 0.0 + var z = 0.0 + + for (j in (0..2)) { + for (k in (0..2)) { + x += leviCivita(0, j, k) * first[j] * second[k] + y += leviCivita(1, j, k) * first[j] * second[k] + z += leviCivita(2, j, k) * first[j] * second[k] + } + } + + return vector(x, y, z) + } + + /** + * Vector product in a right-handed basis + */ + public infix fun Float32Vector3D.cross(other: Float32Vector3D): Float32Vector3D = vectorProduct(this, other) + + public val xAxis: Float32Vector3D = vector(1.0, 0.0, 0.0) + public val yAxis: Float32Vector3D = vector(0.0, 1.0, 0.0) + public val zAxis: Float32Vector3D = vector(0.0, 0.0, 1.0) +} + +public fun Float32Vector3D(x: Number, y: Number, z: Number): Float32Vector3D = Float32Space3D.vector(x, y, z) + +public val Float32Field.euclidean3D: Float32Space3D get() = Float32Space3D \ No newline at end of file diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt similarity index 66% rename from kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt rename to kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt index 3059cefe6..a8c7e1d17 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/Float64Space3D.kt @@ -1,9 +1,9 @@ /* - * Copyright 2018-2022 KMath contributors. + * Copyright 2018-2023 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.geometry +package space.kscience.kmath.geometry.euclidean3d import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName @@ -11,51 +11,33 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import space.kscience.kmath.linear.Point +import space.kscience.kmath.geometry.GeometrySpace +import space.kscience.kmath.geometry.Vector3D +import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.Norm import space.kscience.kmath.operations.ScaleOperations -import space.kscience.kmath.structures.Buffer import kotlin.math.pow import kotlin.math.sqrt -public interface Vector3D : Point, Vector { - public val x: T - public val y: T - public val z: T - override val size: Int get() = 3 +internal fun leviCivita(i: Int, j: Int, k: Int): Int = when { + // even permutation + i == 0 && j == 1 && k == 2 -> 1 + i == 1 && j == 2 && k == 0 -> 1 + i == 2 && j == 0 && k == 1 -> 1 + // odd permutations + i == 2 && j == 1 && k == 0 -> -1 + i == 0 && j == 2 && k == 1 -> -1 + i == 1 && j == 0 && k == 2 -> -1 - override operator fun get(index: Int): T = when (index) { - 0 -> x - 1 -> y - 2 -> z - else -> error("Accessing outside of point bounds") - } - - override operator fun iterator(): Iterator = listOf(x, y, z).iterator() -} - -public operator fun Vector3D.component1(): T = x -public operator fun Vector3D.component2(): T = y -public operator fun Vector3D.component3(): T = z - -public fun Buffer.asVector3D(): Vector3D = object : Vector3D { - init { - require(this@asVector3D.size == 3) { "Buffer of size 3 is required for Vector3D" } - } - - override val x: T get() = this@asVector3D[0] - override val y: T get() = this@asVector3D[1] - override val z: T get() = this@asVector3D[2] - - override fun toString(): String = this@asVector3D.toString() + else -> 0 } public typealias DoubleVector3D = Vector3D public typealias Float64Vector3D = Vector3D -public val DoubleVector3D.r: Double get() = Euclidean3DSpace.norm(this) +public val DoubleVector3D.r: Double get() = Float64Space3D.norm(this) -public object Euclidean3DSpace : GeometrySpace, ScaleOperations, +public object Float64Space3D : GeometrySpace, ScaleOperations, Norm { @Serializable @@ -103,21 +85,8 @@ public object Euclidean3DSpace : GeometrySpace, ScaleOperations< override fun DoubleVector3D.dot(other: DoubleVector3D): Double = x * other.x + y * other.y + z * other.z - private fun leviCivita(i: Int, j: Int, k: Int): Int = when { - // even permutation - i == 0 && j == 1 && k == 2 -> 1 - i == 1 && j == 2 && k == 0 -> 1 - i == 2 && j == 0 && k == 1 -> 1 - // odd permutations - i == 2 && j == 1 && k == 0 -> -1 - i == 0 && j == 2 && k == 1 -> -1 - i == 1 && j == 0 && k == 2 -> -1 - - else -> 0 - } - /** - * Compute vector product of [first] and [second]. The basis assumed to be right-handed. + * Compute vector product of [first] and [second]. The basis is assumed to be right-handed. */ public fun vectorProduct( first: DoubleVector3D, @@ -139,7 +108,7 @@ public object Euclidean3DSpace : GeometrySpace, ScaleOperations< } /** - * Vector product with right basis + * Vector product with the right basis */ public infix fun DoubleVector3D.cross(other: DoubleVector3D): Vector3D = vectorProduct(this, other) @@ -147,3 +116,5 @@ public object Euclidean3DSpace : GeometrySpace, ScaleOperations< public val yAxis: DoubleVector3D = vector(0.0, 1.0, 0.0) public val zAxis: DoubleVector3D = vector(0.0, 0.0, 1.0) } + +public val Float64Field.euclidean3D: Float64Space3D get() = Float64Space3D diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt similarity index 89% rename from kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt rename to kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt index 688386e3b..deb8b0a93 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/rotations3D.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/euclidean3d/rotations3D.kt @@ -1,15 +1,16 @@ /* - * Copyright 2018-2022 KMath contributors. + * Copyright 2018-2023 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.geometry +package space.kscience.kmath.geometry.euclidean3d import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.complex.Quaternion -import space.kscience.kmath.complex.QuaternionField +import space.kscience.kmath.complex.QuaternionAlgebra import space.kscience.kmath.complex.normalized import space.kscience.kmath.complex.reciprocal +import space.kscience.kmath.geometry.* import space.kscience.kmath.linear.LinearSpace import space.kscience.kmath.linear.Matrix import space.kscience.kmath.linear.linearSpace @@ -31,7 +32,7 @@ public val Quaternion.theta: Radians get() = (kotlin.math.acos(normalized().w) * public fun Quaternion.Companion.fromRotation(theta: Angle, vector: DoubleVector3D): Quaternion { val s = sin(theta / 2) val c = cos(theta / 2) - val norm = with(Euclidean3DSpace) { vector.norm() } + val norm = with(Float64Space3D) { vector.norm() } return Quaternion(c, vector.x * s / norm, vector.y * s / norm, vector.z * s / norm) } @@ -50,9 +51,9 @@ public val Quaternion.vector: DoubleVector3D } /** - * Rotate a vector in a [Euclidean3DSpace] + * Rotate a vector in a [Float64Space3D] */ -public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, q: Quaternion): DoubleVector3D = with(QuaternionField) { +public fun Float64Space3D.rotate(vector: DoubleVector3D, q: Quaternion): DoubleVector3D = with(QuaternionAlgebra) { val p = vector.toQuaternion() (q * p * q.reciprocal).vector } @@ -61,10 +62,10 @@ public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, q: Quaternion): Doubl * Use a composition of quaternions to create a rotation */ @UnstableKMathAPI -public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, composition: QuaternionField.() -> Quaternion): DoubleVector3D = - rotate(vector, QuaternionField.composition()) +public fun Float64Space3D.rotate(vector: DoubleVector3D, composition: QuaternionAlgebra.() -> Quaternion): DoubleVector3D = + rotate(vector, QuaternionAlgebra.composition()) -public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, matrix: Matrix): DoubleVector3D { +public fun Float64Space3D.rotate(vector: DoubleVector3D, matrix: Matrix): DoubleVector3D { require(matrix.colNum == 3 && matrix.rowNum == 3) { "Square 3x3 rotation matrix is required" } return with(Float64Field.linearSpace) { matrix.dot(vector).asVector3D() } } @@ -76,7 +77,7 @@ public fun Euclidean3DSpace.rotate(vector: DoubleVector3D, matrix: Matrix = Float64Field.linearSpace, ): Matrix { - val s = QuaternionField.norm(this).pow(-2) + val s = QuaternionAlgebra.norm(this).pow(-2) return linearSpace.matrix(3, 3)( 1.0 - 2 * s * (y * y + z * z), 2 * s * (x * y - z * w), 2 * s * (x * z + y * w), 2 * s * (x * y + z * w), 1.0 - 2 * s * (x * x + z * z), 2 * s * (y * z - x * w), diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt index ea46ab90f..2ed02182d 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/floatPrecision.kt @@ -6,6 +6,10 @@ package space.kscience.kmath.geometry import space.kscience.kmath.geometry.GeometrySpace.Companion.DEFAULT_PRECISION +import space.kscience.kmath.geometry.euclidean2d.Float64Space2D +import space.kscience.kmath.geometry.euclidean2d.Float64Vector2D +import space.kscience.kmath.geometry.euclidean3d.Float64Space3D +import space.kscience.kmath.geometry.euclidean3d.Float64Vector3D /** * Float equality within given [precision] @@ -36,7 +40,7 @@ public fun V.equalsVector( public fun Float64Vector2D.equalsVector( other: Float64Vector2D, precision: Double = DEFAULT_PRECISION, -): Boolean = equalsVector(Euclidean2DSpace, other, precision) +): Boolean = equalsVector(Float64Space2D, other, precision) /** * Vector equality using Euclidian L2 norm and given [precision] @@ -44,7 +48,7 @@ public fun Float64Vector2D.equalsVector( public fun Float64Vector3D.equalsVector( other: Float64Vector3D, precision: Double = DEFAULT_PRECISION, -): Boolean = equalsVector(Euclidean3DSpace, other, precision) +): Boolean = equalsVector(Float64Space3D, other, precision) /** * Line equality using [GeometrySpace.norm] provided by the [space] and given [precision] diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt new file mode 100644 index 000000000..49ceda04c --- /dev/null +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2018-2023 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.geometry + +import space.kscience.kmath.complex.Quaternion +import space.kscience.kmath.complex.QuaternionAlgebra +import space.kscience.kmath.complex.conjugate +import space.kscience.kmath.complex.normalized +import space.kscience.kmath.geometry.euclidean3d.Float32Space3D +import space.kscience.kmath.geometry.euclidean3d.Float32Vector3D +import space.kscience.kmath.geometry.euclidean3d.theta +import kotlin.math.asin +import kotlin.math.atan2 +import kotlin.math.pow + +public operator fun Quaternion.times(other: Quaternion): Quaternion = QuaternionAlgebra.multiply(this, other) + +public operator fun Quaternion.div(other: Quaternion): Quaternion = QuaternionAlgebra.divide(this, other) + +public fun Quaternion.power(number: Number): Quaternion = QuaternionAlgebra.power(this, number) + +/** + * Linear interpolation between [from] and [to] in spherical space + */ +public fun QuaternionAlgebra.slerp(from: Quaternion, to: Quaternion, fraction: Double): Quaternion = + (to / from).pow(fraction) * from + +public fun QuaternionAlgebra.angleBetween(q1: Quaternion, q2: Quaternion): Angle = (q1.conjugate * q2).theta + +public val Quaternion.inclination: Radians get() = asin(2 * (w * y - x * z)).radians + +public val Quaternion.azimuth: Angle get() = atan2(2 * (w * z + x * y), 1 - 2 * (y.pow(2) + z.pow(2))).radians.normalized() + +public infix fun Quaternion.dot(other: Quaternion): Double = w * other.w + x * other.x + y * other.y + z * other.z + + +private fun Quaternion.normalizedToEuler(): Float32Vector3D { + val roll = atan2(2 * y * w + 2 * x * z, 1 - 2 * y * y - 2 * z * z); + val pitch = atan2(2 * x * w - 2 * y * z, 1 - 2 * x * x - 2 * z * z); + val yaw = asin(2 * x * y + 2 * z * w); + + return Float32Vector3D(roll, pitch, yaw) +} + +/** + * Quaternion to XYZ Cardan angles + */ +public fun Quaternion.toEuler(): Float32Vector3D = if (QuaternionAlgebra.norm(this) == 0.0) { + Float32Space3D.zero +} else { + normalized().normalizedToEuler() +} \ No newline at end of file diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean2DSpaceTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean2DSpaceTest.kt index 22cbee6f0..7c5ee3485 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean2DSpaceTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean2DSpaceTest.kt @@ -5,6 +5,7 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean2d.Float64Space2D import kotlin.math.sqrt import kotlin.test.Test import kotlin.test.assertEquals @@ -12,12 +13,12 @@ import kotlin.test.assertEquals internal class Euclidean2DSpaceTest { @Test fun zero() { - assertVectorEquals(Euclidean2DSpace.vector(0.0, 0.0), Euclidean2DSpace.zero) + assertVectorEquals(Float64Space2D.vector(0.0, 0.0), Float64Space2D.zero) } @Test fun norm() { - with(Euclidean2DSpace) { + with(Float64Space2D) { assertEquals(0.0, norm(zero)) assertEquals(1.0, norm(vector(1.0, 0.0))) assertEquals(sqrt(2.0), norm(vector(1.0, 1.0))) @@ -27,7 +28,7 @@ internal class Euclidean2DSpaceTest { @Test fun dotProduct() { - with(Euclidean2DSpace) { + with(Float64Space2D) { assertEquals(0.0, zero dot zero) assertEquals(0.0, zero dot vector(1.0, 0.0)) assertEquals(0.0, vector(-2.0, 0.001) dot zero) @@ -44,7 +45,7 @@ internal class Euclidean2DSpaceTest { @Test fun add() { - with(Euclidean2DSpace) { + with(Float64Space2D) { assertVectorEquals( vector(-2.0, 0.001), vector(-2.0, 0.001) + zero @@ -58,7 +59,7 @@ internal class Euclidean2DSpaceTest { @Test fun multiply() { - with(Euclidean2DSpace) { + with(Float64Space2D) { assertVectorEquals(vector(-4.0, 0.0), vector(-2.0, 0.0) * 2) assertVectorEquals(vector(4.0, 0.0), vector(-2.0, 0.0) * -2) assertVectorEquals(vector(300.0, 0.0003), vector(100.0, 0.0001) * 3) diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean3DSpaceTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean3DSpaceTest.kt index 20e112ad1..f881deb1b 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean3DSpaceTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Euclidean3DSpaceTest.kt @@ -5,18 +5,19 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean3d.Float64Space3D import kotlin.test.Test import kotlin.test.assertEquals internal class Euclidean3DSpaceTest { @Test fun zero() { - assertVectorEquals(Euclidean3DSpace.vector(0.0, 0.0, 0.0), Euclidean3DSpace.zero) + assertVectorEquals(Float64Space3D.vector(0.0, 0.0, 0.0), Float64Space3D.zero) } @Test fun distance() { - with(Euclidean3DSpace) { + with(Float64Space3D) { assertEquals(0.0, zero.distanceTo(zero)) assertEquals(1.0, zero.distanceTo(vector(1.0, 0.0, 0.0))) assertEquals(kotlin.math.sqrt(5.000001), vector(1.0, -2.0, 0.001).distanceTo(zero)) @@ -31,7 +32,7 @@ internal class Euclidean3DSpaceTest { @Test fun norm() { - with(Euclidean3DSpace) { + with(Float64Space3D) { assertEquals(0.0, zero.norm()) assertEquals(1.0, vector(1.0, 0.0, 0.0).norm()) assertEquals(kotlin.math.sqrt(3.0), vector(1.0, 1.0, 1.0).norm()) @@ -41,7 +42,7 @@ internal class Euclidean3DSpaceTest { @Test fun dotProduct() { - with(Euclidean3DSpace) { + with(Float64Space3D) { assertEquals(0.0, zero dot zero) assertEquals(0.0, zero dot vector(1.0, 0.0, 0.0)) assertEquals(0.0, vector(1.0, -2.0, 0.001) dot zero) @@ -57,7 +58,7 @@ internal class Euclidean3DSpaceTest { } @Test - fun add() = with(Euclidean3DSpace) { + fun add() = with(Float64Space3D) { assertVectorEquals( vector(1.0, -2.0, 0.001), vector(1.0, -2.0, 0.001) + zero @@ -69,19 +70,19 @@ internal class Euclidean3DSpaceTest { } @Test - fun multiply() = with(Euclidean3DSpace) { + fun multiply() = with(Float64Space3D) { assertVectorEquals(vector(2.0, -4.0, 0.0), vector(1.0, -2.0, 0.0) * 2) } @Test - fun vectorProduct() = with(Euclidean3DSpace) { + fun vectorProduct() = with(Float64Space3D) { assertVectorEquals(zAxis, vectorProduct(xAxis, yAxis)) assertVectorEquals(zAxis, xAxis cross yAxis) assertVectorEquals(-zAxis, vectorProduct(yAxis, xAxis)) } @Test - fun doubleVectorProduct() = with(Euclidean3DSpace) { + fun doubleVectorProduct() = with(Float64Space3D) { val a = vector(1, 2, -3) val b = vector(-1, 0, 1) val c = vector(4, 5, 6) diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionAlongTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionAlongTest.kt index cc74b06e3..9b3f40c4c 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionAlongTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionAlongTest.kt @@ -5,13 +5,15 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean2d.Float64Space2D +import space.kscience.kmath.geometry.euclidean3d.Float64Space3D import kotlin.test.Test import kotlin.test.assertTrue internal class ProjectionAlongTest { @Test fun projectionIntoYEqualsX() { - with(Euclidean2DSpace) { + with(Float64Space2D) { val normal = vector(-2.0, 2.0) val base = vector(2.3, 2.3) @@ -26,7 +28,7 @@ internal class ProjectionAlongTest { @Test fun projectionOntoLine() { - with(Euclidean2DSpace) { + with(Float64Space2D) { val a = 5.0 val b = -3.0 val c = -15.0 @@ -42,11 +44,11 @@ internal class ProjectionAlongTest { } @Test - fun projectOntoPlane() = with(Euclidean3DSpace){ + fun projectOntoPlane() = with(Float64Space3D){ val normal = vector(1.0, 3.5, 0.07) val base = vector(2.0, -0.0037, 11.1111) - with(Euclidean3DSpace) { + with(Float64Space3D) { val testDomain = (-10.0..10.0).generateList(0.43) for (x in testDomain) { for (y in testDomain) { diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionOntoLineTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionOntoLineTest.kt index 7c6c105cf..d3a9d1171 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionOntoLineTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/ProjectionOntoLineTest.kt @@ -5,13 +5,15 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean2d.Float64Space2D +import space.kscience.kmath.geometry.euclidean3d.Float64Space3D import kotlin.test.Test import kotlin.test.assertTrue internal class ProjectionOntoLineTest { @Test fun projectionIntoOx() { - with(Euclidean2DSpace) { + with(Float64Space2D) { val ox = Line(zero, vector(1.0, 0.0)) grid(-10.0..10.0, -10.0..10.0, 0.15).forEach { (x, y) -> @@ -22,7 +24,7 @@ internal class ProjectionOntoLineTest { @Test fun projectionIntoOy() { - with(Euclidean2DSpace) { + with(Float64Space2D) { val line = Line(zero, vector(0.0, 1.0)) grid(-10.0..10.0, -10.0..10.0, 0.15).forEach { (x, y) -> @@ -33,7 +35,7 @@ internal class ProjectionOntoLineTest { @Test fun projectionIntoYEqualsX() { - with(Euclidean2DSpace) { + with(Float64Space2D) { val line = Line(zero, vector(1.0, 1.0)) assertVectorEquals(zero, projectToLine(zero, line)) @@ -47,7 +49,7 @@ internal class ProjectionOntoLineTest { @Test fun projectionOntoLine2d() { - with(Euclidean2DSpace) { + with(Float64Space2D) { val a = 5.0 val b = -3.0 val c = -15.0 @@ -62,7 +64,7 @@ internal class ProjectionOntoLineTest { } @Test - fun projectionOntoLine3d() = with(Euclidean3DSpace) { + fun projectionOntoLine3d() = with(Float64Space3D) { val line = Line( base = vector(1.0, 3.5, 0.07), direction = vector(2.0, -0.0037, 11.1111) diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt index 33a9bcc01..1d354bf72 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/RotationTest.kt @@ -7,6 +7,7 @@ package space.kscience.kmath.geometry import space.kscience.kmath.complex.Quaternion import space.kscience.kmath.complex.normalized +import space.kscience.kmath.geometry.euclidean3d.* import space.kscience.kmath.structures.Float64Buffer import space.kscience.kmath.testutils.assertBufferEquals import kotlin.test.Test @@ -14,7 +15,7 @@ import kotlin.test.Test class RotationTest { @Test - fun differentRotations() = with(Euclidean3DSpace) { + fun differentRotations() = with(Float64Space3D) { val vector = vector(1.0, 1.0, 1.0) val q = Quaternion(1.0, 2.0, -3.0, 4.0).normalized() val rotatedByQ = rotate(vector, q) @@ -36,7 +37,7 @@ class RotationTest { @Test fun fromRotation() { - val q = Quaternion.fromRotation(0.3.radians, Euclidean3DSpace.vector(1.0, 1.0, 1.0)) + val q = Quaternion.fromRotation(0.3.radians, Float64Space3D.vector(1.0, 1.0, 1.0)) assertBufferEquals(Float64Buffer(0.9887711, 0.0862781, 0.0862781, 0.0862781), q) } diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt index 0db06f4c8..af27b5e00 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt @@ -6,12 +6,13 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean2d.Float64Space2D import space.kscience.kmath.operations.toList import kotlin.test.Test import kotlin.test.assertEquals internal class Vector2DTest { - private val vector = Euclidean2DSpace.vector(1.0, -7.999) + private val vector = Float64Space2D.vector(1.0, -7.999) @Test fun size() { diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt index 1c8607838..b10e650a6 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt @@ -5,12 +5,13 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean3d.Float64Space3D import space.kscience.kmath.operations.toList import kotlin.test.Test import kotlin.test.assertEquals internal class Vector3DTest { - private val vector = Euclidean3DSpace.vector(1.0, -7.999, 0.001) + private val vector = Float64Space3D.vector(1.0, -7.999, 0.001) @Test fun size() { diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt index c62af3cd3..521ce9e75 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/testUtils.kt @@ -5,6 +5,8 @@ package space.kscience.kmath.geometry +import space.kscience.kmath.geometry.euclidean2d.DoubleVector2D +import space.kscience.kmath.geometry.euclidean3d.DoubleVector3D import kotlin.math.abs import kotlin.test.assertEquals diff --git a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt index df8a01208..027318f99 100644 --- a/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt +++ b/kmath-tensorflow/src/main/kotlin/space/kscience/kmath/tensorflow/DoubleTensorFlowAlgebra.kt @@ -16,6 +16,7 @@ import space.kscience.kmath.expressions.Symbol import space.kscience.kmath.nd.ColumnStrides import space.kscience.kmath.nd.ShapeND import space.kscience.kmath.nd.StructureND +import space.kscience.kmath.operations.FieldOps import space.kscience.kmath.operations.Float64Field import space.kscience.kmath.operations.PowerOperations @@ -32,7 +33,8 @@ internal fun ShapeND.toLongArray(): LongArray = LongArray(size) { get(it).toLong public class DoubleTensorFlowAlgebra internal constructor( graph: Graph, -) : TensorFlowAlgebra(graph), PowerOperations> { +) : TensorFlowAlgebra(graph), FieldOps>, PowerOperations> { override val elementAlgebra: Float64Field get() = Float64Field From 7a4e9e70f99dcb215b349d4842448b36ba0096b1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 22 Sep 2023 08:21:14 +0300 Subject: [PATCH 34/34] add some quaternion operations --- .../space/kscience/kmath/ejml/_generated.kt | 10 +++++-- .../kmath/geometry/quaternionOperations.kt | 29 +++++++++++-------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt index f48ab4c19..984f1619b 100644 --- a/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt +++ b/kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/_generated.kt @@ -19,13 +19,19 @@ import org.ejml.sparse.csc.factory.DecompositionFactory_DSCC import org.ejml.sparse.csc.factory.DecompositionFactory_FSCC import org.ejml.sparse.csc.factory.LinearSolverFactory_DSCC import org.ejml.sparse.csc.factory.LinearSolverFactory_FSCC -import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.linear.* import space.kscience.kmath.linear.Matrix +import space.kscience.kmath.UnstableKMathAPI import space.kscience.kmath.nd.StructureFeature -import space.kscience.kmath.operations.Float32Field +import space.kscience.kmath.structures.Float64 +import space.kscience.kmath.structures.Float32 import space.kscience.kmath.operations.Float64Field +import space.kscience.kmath.operations.Float32Field +import space.kscience.kmath.operations.DoubleField +import space.kscience.kmath.operations.FloatField import space.kscience.kmath.operations.invoke +import space.kscience.kmath.structures.Float64Buffer +import space.kscience.kmath.structures.Float32Buffer import space.kscience.kmath.structures.DoubleBuffer import space.kscience.kmath.structures.FloatBuffer import kotlin.reflect.KClass diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt index 49ceda04c..b6ad5009e 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/quaternionOperations.kt @@ -28,22 +28,27 @@ public fun Quaternion.power(number: Number): Quaternion = QuaternionAlgebra.powe public fun QuaternionAlgebra.slerp(from: Quaternion, to: Quaternion, fraction: Double): Quaternion = (to / from).pow(fraction) * from +/** + * Scalar angle between two quaternions + */ public fun QuaternionAlgebra.angleBetween(q1: Quaternion, q2: Quaternion): Angle = (q1.conjugate * q2).theta -public val Quaternion.inclination: Radians get() = asin(2 * (w * y - x * z)).radians - -public val Quaternion.azimuth: Angle get() = atan2(2 * (w * z + x * y), 1 - 2 * (y.pow(2) + z.pow(2))).radians.normalized() - +/** + * Euclidean product of two quaternions + */ public infix fun Quaternion.dot(other: Quaternion): Double = w * other.w + x * other.x + y * other.y + z * other.z - -private fun Quaternion.normalizedToEuler(): Float32Vector3D { - val roll = atan2(2 * y * w + 2 * x * z, 1 - 2 * y * y - 2 * z * z); - val pitch = atan2(2 * x * w - 2 * y * z, 1 - 2 * x * x - 2 * z * z); - val yaw = asin(2 * x * y + 2 * z * w); - - return Float32Vector3D(roll, pitch, yaw) -} +// +///** +// * Convert a quaternion to XYZ Cardan angles assuming it is normalized. +// */ +//private fun Quaternion.normalizedToEuler(): Float32Vector3D { +// val roll = atan2(2 * y * w + 2 * x * z, 1 - 2 * y * y - 2 * z * z) +// val pitch = atan2(2 * x * w - 2 * y * z, 1 - 2 * x * x - 2 * z * z) +// val yaw = asin(2 * x * y + 2 * z * w) +// +// return Float32Vector3D(roll, pitch, yaw) +//} /** * Quaternion to XYZ Cardan angles