Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Levenberg-Marquardt algorithm and svd Golub-Kahan #513

Merged
merged 35 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9e141db
Merge pull request #2 from SciProgCentre/dev
margarita0303 Dec 10, 2022
a74a780
Merge remote-tracking branch 'origin/dev' into dev
margarita0303 May 2, 2023
a962707
Merge branch 'SciProgCentre:dev' into dev
margarita0303 May 2, 2023
a020859
Merge remote-tracking branch 'origin/dev' into dev
margarita0303 May 2, 2023
10f84bd
added function solve
margarita0303 May 3, 2023
19c1af1
added helper functions for levenberg-marquardt algorithm
margarita0303 May 3, 2023
89a5522
added new svd algorithm (Golub Kahan) and used by default for svd
margarita0303 May 3, 2023
b526f9a
added Levenberg-Marquardt algorithm + test
margarita0303 May 4, 2023
64e5633
fixed error for chi_sq and added more complete output for lm
margarita0303 May 7, 2023
cfe8e9b
done TODOs, deleted prints and added type of convergence to output of lm
margarita0303 May 7, 2023
acff855
Merge branch 'dev' into dev
SPC-code May 9, 2023
a18fa01
added parameter check in tests
margarita0303 May 26, 2023
ce16946
added streaming version of LM
margarita0303 May 26, 2023
e738fbc
typo fixed
margarita0303 May 26, 2023
20c20a3
y_dat added generation
margarita0303 May 27, 2023
33cb317
added examples and tests
margarita0303 May 28, 2023
1afb0d0
fixed time for js tests for lm
margarita0303 May 29, 2023
3a18175
Merge pull request #3 from margarita0303/streaming_lm_algorithm
margarita0303 May 29, 2023
47600df
tests changed
margarita0303 Jun 5, 2023
2ead722
Merge pull request #4 from margarita0303/streaming_lm_algorithm
margarita0303 Jun 5, 2023
f65a463
Merge branch 'dev' into dev
margarita0303 Jun 5, 2023
8d81d2d
move lm-algorithm from DoubleTensorAlgebra as extension
margarita0303 Jun 5, 2023
c017d58
Merge remote-tracking branch 'origin/dev' into dev
margarita0303 Jun 5, 2023
963e14b
move enums
margarita0303 Jun 6, 2023
29d392a
fix problem with imports
margarita0303 Jun 6, 2023
1ed40cd
fix problem with imports
margarita0303 Jun 6, 2023
0c7f569
add documentation for enum TypeOfConvergence
margarita0303 Jun 6, 2023
cac5b51
made class for settings private and removed settings as input from a …
margarita0303 Jun 6, 2023
162e37c
removed extra comments, unnecessary variables, renaming variables and…
margarita0303 Jun 6, 2023
e8dafad
the input data is placed in a separate class, to which the documentat…
margarita0303 Jun 7, 2023
0655642
add documentation to the main function levenbergMarquardt
margarita0303 Jun 7, 2023
346e2e9
add minor fixes
margarita0303 Jun 7, 2023
f91b018
add assertEquals to middle and difficult test
margarita0303 Jun 7, 2023
ef4335b
use function types for input func
margarita0303 Jun 7, 2023
5f26903
fix mistake in streaming version
margarita0303 Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.LMInput
import space.kscience.kmath.tensors.core.levenbergMarquardt
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 exampleNumber = 1

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) {
p_init[i, 0] = (p_example[i, 0] + 0.9)
}

var t = t_example
val y_dat = y_hat
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)
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 inputData = LMInput(::funcDifficultForLm,
p_init.as2D(),
t,
y_dat,
weight,
dp,
p_min.as2D(),
p_max.as2D(),
opts[1].toInt(),
doubleArrayOf(opts[2], opts[3], opts[4], opts[5]),
doubleArrayOf(opts[6], opts[7], opts[8]),
opts[9].toInt(),
10,
1)

val result = DoubleTensorAlgebra.levenbergMarquardt(inputData)

println("Parameters:")
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.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
println("$x $y")
}

println("Сhi_sq:")
println(result.resultChiSq)
println("Number of iterations:")
println(result.iterations)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.DoubleTensorAlgebra
import space.kscience.kmath.tensors.core.LMInput
import space.kscience.kmath.tensors.core.levenbergMarquardt
import kotlin.math.roundToInt

fun main() {
val startedData = getStartDataForFuncEasy()
val inputData = LMInput(::funcEasyForLm,
DoubleTensorAlgebra.ones(ShapeND(intArrayOf(4, 1))).as2D(),
startedData.t,
startedData.y_dat,
startedData.weight,
startedData.dp,
startedData.p_min,
startedData.p_max,
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)

val result = DoubleTensorAlgebra.levenbergMarquardt(inputData)

println("Parameters:")
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.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
println("$x $y")
}

println("Сhi_sq:")
println(result.resultChiSq)
println("Number of iterations:")
println(result.iterations)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* 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.LMInput
import space.kscience.kmath.tensors.core.levenbergMarquardt
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 exampleNumber = 1

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)
}

var t = t_example
val y_dat = y_hat
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)
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(),
t,
y_dat,
weight,
dp,
p_min.as2D(),
p_max.as2D(),
opts[1].toInt(),
doubleArrayOf(opts[2], opts[3], opts[4], opts[5]),
doubleArrayOf(opts[6], opts[7], opts[8]),
opts[9].toInt(),
10,
1)

val result = DoubleTensorAlgebra.levenbergMarquardt(inputData)

println("Parameters:")
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.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
println("$x $y")
}

println("Сhi_sq:")
println(result.resultChiSq)
println("Number of iterations:")
println(result.iterations)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 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.LMInput
import space.kscience.kmath.tensors.core.levenbergMarquardt
import kotlin.random.Random
import kotlin.reflect.KFunction3

fun streamLm(lm_func: (MutableStructure2D<Double>, MutableStructure2D<Double>, Int) -> (MutableStructure2D<Double>),
startData: StartDataLm, launchFrequencyInMs: Long, numberOfLaunches: Int): Flow<MutableStructure2D<Double>> = flow{

var example_number = startData.example_number
var p_init = startData.p_init
var t = startData.t
var 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 opts = startData.opts

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.levenbergMarquardt(inputData)
emit(result.resultParameters)
delay(launchFrequencyInMs)
inputData.realValues = generateNewYDat(y_dat, 0.1)
inputData.startParameters = result.resultParameters
if (!isEndless) steps -= 1
}
}

fun generateNewYDat(y_dat: MutableStructure2D<Double>, delta: Double): MutableStructure2D<Double>{
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
y_dat_new[i, 0] = y_dat[i, 0] + randomEps
}
return y_dat_new
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.*
import kotlin.math.roundToInt

suspend fun main(){
val startData = getStartDataForFuncDifficult()
// Создание потока:
val lmFlow = streamLm(::funcDifficultForLm, startData, 0, 100)
var initialTime = System.currentTimeMillis()
var lastTime: Long
val launches = mutableListOf<Long>()
// Запуск потока
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()}")
}
Loading