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

Merged
margarita0303 merged 35 commits from dev into dev 2023-06-19 16:11:59 +03:00
Showing only changes of commit 346e2e97f2 - Show all commits

View File

@ -165,16 +165,15 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI
} }
var maxIterations = inputData.maxIterations var maxIterations = inputData.maxIterations
var epsilon1 = inputData.epsilons[0] // convergence tolerance for gradient var epsilon1 = inputData.epsilons[0]
var epsilon2 = inputData.epsilons[1] // convergence tolerance for parameters var epsilon2 = inputData.epsilons[1]
var epsilon3 = inputData.epsilons[2] // convergence tolerance for Chi-square var epsilon3 = inputData.epsilons[2]
var epsilon4 = inputData.epsilons[3] // determines acceptance of a L-M step var epsilon4 = inputData.epsilons[3]
var lambda0 = inputData.lambdas[0] // initial value of damping paramter, lambda var lambda0 = inputData.lambdas[0]
var lambdaUpFac = inputData.lambdas[1] // factor for increasing lambda var lambdaUpFac = inputData.lambdas[1]
var lambdaDnFac = inputData.lambdas[2] // factor for decreasing lambda var lambdaDnFac = inputData.lambdas[2]
var updateType = inputData.updateType // 1: Levenberg-Marquardt lambda update var updateType = inputData.updateType
// 2: Quadratic update
// 3: Nielsen's lambda update equations
if (inputData.nargin < 9) { if (inputData.nargin < 9) {
maxIterations = 10 * Npar maxIterations = 10 * Npar
epsilon1 = 1e-3 epsilon1 = 1e-3
@ -218,34 +217,30 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI
var lambda = 1.0 var lambda = 1.0
var nu = 1 var nu = 1
when (updateType) {
1 -> lambda = lambda0 // Marquardt: init'l lambda if (updateType == 1) {
else -> { // Quadratic and Nielsen lambda = lambda0 // Marquardt: init'l lambda
lambda = lambda0 * (makeColumnFromDiagonal(JtWJ)).max()!!
nu = 2
} }
else {
lambda = lambda0 * (makeColumnFromDiagonal(JtWJ)).max()
nu = 2
} }
X2Old = X2 // previous value of X2 X2Old = X2 // previous value of X2
var h: DoubleTensor var h: DoubleTensor
while (!stop && settings.iteration <= maxIterations) { //--- Start Main Loop while (!stop && settings.iteration <= maxIterations) {
settings.iteration += 1 settings.iteration += 1
// incremental change in parameters // incremental change in parameters
h = when (updateType) { h = if (updateType == 1) { // Marquardt
1 -> { // Marquardt val solve = solve(JtWJ.plus(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda)).as2D(), JtWdy)
val solve =
solve(JtWJ.plus(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda)).as2D(), JtWdy)
solve.asDoubleTensor() solve.asDoubleTensor()
} } else { // Quadratic and Nielsen
else -> { // Quadratic and Nielsen
val solve = solve(JtWJ.plus(lmEye(Npar).div(1 / lambda)).as2D(), JtWdy) val solve = solve(JtWJ.plus(lmEye(Npar).div(1 / lambda)).as2D(), JtWdy)
solve.asDoubleTensor() solve.asDoubleTensor()
} }
}
var pTry = (p + h).as2D() // update the [idx] elements 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
@ -269,7 +264,6 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI
val alpha = 1.0 val alpha = 1.0
if (updateType == 2) { // Quadratic if (updateType == 2) { // Quadratic
// One step of quadratic line update in the h direction for minimum X2 // 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))) val alpha = JtWdy.transpose().dot(h) / ((X2Try.minus(X2)).div(2.0).plus(2 * JtWdy.transpose().dot(h)))
h = h.dot(alpha) h = h.dot(alpha)
pTry = p.plus(h).as2D() // update only [idx] elements pTry = p.plus(h).as2D() // update only [idx] elements
@ -287,7 +281,6 @@ public fun DoubleTensorAlgebra.levenbergMarquardt(inputData: LMInput): LMResultI
.dot(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy)) .dot(makeMatrixWithDiagonal(makeColumnFromDiagonal(JtWJ)).div(1 / lambda).dot(h).plus(JtWdy))
X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0]
} }
else -> { else -> {
val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy)) val tmp = h.transposed().dot(h.div(1 / lambda).plus(JtWdy))
X2.minus(X2Try).as2D()[0, 0] / abs(tmp.as2D()).as2D()[0, 0] 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) lmMatxAns = lmMatx(inputData.func, t, pOld, yOld, dX2.toInt(), J, p, inputData.realValues, weight, dp, settings)
// decrease lambda ==> Gauss-Newton method // decrease lambda ==> Gauss-Newton method
JtWJ = lmMatxAns[0] JtWJ = lmMatxAns[0]
JtWdy = lmMatxAns[1] JtWdy = lmMatxAns[1]
X2 = lmMatxAns[2][0, 0] X2 = lmMatxAns[2][0, 0]