@@ -93,16 +93,18 @@ public NonlinearMinimizationResult Minimum(IObjectiveModel objective, Vector<dou
9393 if ( objective == null )
9494 throw new ArgumentNullException ( nameof ( objective ) ) ;
9595
96+ var objectiveModel = objective . CreateNew ( ) ;
97+
9698 ValidateBounds ( initialGuess , lowerBound , upperBound , scales ) ;
9799
98- objective . SetParameters ( initialGuess , isFixed ) ;
100+ objectiveModel . SetParameters ( initialGuess , isFixed ) ;
99101
100- ExitCondition exitCondition = ExitCondition . None ;
102+ var exitCondition = ExitCondition . None ;
101103
102104 // First, calculate function values and setup variables
103105 var P = ProjectToInternalParameters ( initialGuess ) ; // current internal parameters
104106 Vector < double > Pstep ; // the change of parameters
105- var RSS = EvaluateFunction ( objective , P ) ; // Residual Sum of Squares = R'R
107+ var RSS = EvaluateFunction ( objectiveModel , P ) ; // Residual Sum of Squares = 1/2 R'R
106108
107109 if ( maximumIterations < 0 )
108110 {
@@ -113,7 +115,7 @@ public NonlinearMinimizationResult Minimum(IObjectiveModel objective, Vector<dou
113115 if ( double . IsNaN ( RSS ) )
114116 {
115117 exitCondition = ExitCondition . InvalidValues ;
116- return new NonlinearMinimizationResult ( objective , - 1 , exitCondition ) ;
118+ return new NonlinearMinimizationResult ( objectiveModel , - 1 , exitCondition ) ;
117119 }
118120
119121 // When only function evaluation is needed, set maximumIterations to zero,
@@ -129,8 +131,7 @@ public NonlinearMinimizationResult Minimum(IObjectiveModel objective, Vector<dou
129131 }
130132
131133 // Evaluate gradient and Hessian
132- var ( Gradient , Hessian ) = EvaluateJacobian ( objective , P ) ;
133- var diagonalOfHessian = Hessian . Diagonal ( ) ; // diag(H)
134+ var ( Gradient , Hessian ) = EvaluateJacobian ( objectiveModel , P ) ;
134135
135136 // if ||g||oo <= gtol, found and stop
136137 if ( Gradient . InfinityNorm ( ) <= gradientTolerance )
@@ -140,91 +141,181 @@ public NonlinearMinimizationResult Minimum(IObjectiveModel objective, Vector<dou
140141
141142 if ( exitCondition != ExitCondition . None )
142143 {
143- return new NonlinearMinimizationResult ( objective , - 1 , exitCondition ) ;
144+ return new NonlinearMinimizationResult ( objectiveModel , - 1 , exitCondition ) ;
144145 }
145146
146- double mu = initialMu * diagonalOfHessian . Max ( ) ; // μ
147- double nu = 2 ; // ν
148- int iterations = 0 ;
147+ // Initialize trust region boundary delta and damping parameter mu
148+ var delta = initialMu * P . L2Norm ( ) ; // Trust region boundary
149+ if ( delta == 0.0 ) delta = initialMu ;
150+ var mu = initialMu * Hessian . Diagonal ( ) . Max ( ) ; // Damping parameter μ
151+ var nu = 2.0 ; // Multiplication factor ν for mu updates
152+
153+ // Counters for successful and failed iterations
154+ var ncsuc = 0 ; // number of consecutive successful iterations
155+ var ncfail = 0 ; // number of consecutive failed iterations
156+
157+ // Flag for first iteration special handling
158+ var firstIteration = true ;
159+
160+ var iterations = 0 ;
149161 while ( iterations < maximumIterations && exitCondition == ExitCondition . None )
150162 {
151163 iterations ++ ;
152164
153165 while ( true )
154166 {
167+ // Store current Hessian diagonal for restoration if step is rejected
168+ var savedDiagonal = Hessian . Diagonal ( ) . Clone ( ) ;
169+
170+ // Add damping to Hessian: H + μI
155171 Hessian . SetDiagonal ( Hessian . Diagonal ( ) + mu ) ; // hessian[i, i] = hessian[i, i] + mu;
156172
157- // solve normal equations
173+ // Solve normal equations: (H + μI)Δp = -g
158174 Pstep = Hessian . Solve ( - Gradient ) ;
159175
160- // if ||ΔP|| <= xTol * (||P|| + xTol), found and stop
161- if ( Pstep . L2Norm ( ) <= stepTolerance * ( P . L2Norm ( ) + stepTolerance ) )
176+ // Calculate step size (norm)
177+ var pnorm = Pstep . L2Norm ( ) ;
178+
179+ // On first iteration, adjust the initial step bound
180+ if ( firstIteration )
181+ {
182+ delta = Math . Min ( delta , pnorm ) ;
183+ firstIteration = false ;
184+ }
185+
186+ // Check convergence on step size
187+ if ( pnorm <= stepTolerance * ( P . L2Norm ( ) + stepTolerance ) )
162188 {
163189 exitCondition = ExitCondition . RelativePoints ;
164190 break ;
165191 }
166192
167- var Pnew = P + Pstep ; // new parameters to test
168- // evaluate function at Pnew
169- var RSSnew = EvaluateFunction ( objective , Pnew ) ;
193+ // New parameter vector
194+ var Pnew = P + Pstep ;
170195
196+ // Evaluate function at new point
197+ var RSSnew = EvaluateFunction ( objectiveModel , Pnew ) ;
198+
199+ // Check for invalid results
171200 if ( double . IsNaN ( RSSnew ) )
172201 {
173202 exitCondition = ExitCondition . InvalidValues ;
174203 break ;
175204 }
176205
177- // calculate the ratio of the actual to the predicted reduction.
178- // ρ = (RSS - RSSnew) / (Δp'(μΔp - g))
179- var predictedReduction = Pstep . DotProduct ( mu * Pstep - Gradient ) ;
180- var rho = ( predictedReduction != 0 )
181- ? ( RSS - RSSnew ) / predictedReduction
182- : 0 ;
206+ // Compute the scaled actual reduction
207+ // actred = 1 - (fnorm1/fnorm)^2 if fnorm1 < fnorm, else -1
208+ var actred = ( RSSnew < RSS ) ? 1.0 - Math . Pow ( RSSnew / RSS , 2.0 ) : - 1.0 ;
209+
210+ // Compute predicted reduction metrics
211+ // In LMDER, wa3 = J'*(Q'*fvec), where J is the Jacobian at the current point
212+ // and the predicted reduction is ||J*p||^2 + ||sqrt(par)*D*p||^2
213+ var tempVec = Hessian . Multiply ( Pstep ) ;
214+ var temp1 = Pstep . DotProduct ( tempVec ) / RSS ;
215+ var temp2 = ( Math . Sqrt ( mu ) * pnorm ) / Math . Sqrt ( RSS ) ;
216+ var prered = temp1 + Math . Pow ( temp2 , 2.0 ) / 0.5 ;
217+ var dirder = - ( temp1 + Math . Pow ( temp2 , 2.0 ) ) ;
218+
219+ // Compute the ratio of actual to predicted reduction
220+ var ratio = ( prered != 0.0 ) ? actred / prered : 0.0 ;
221+
222+ // Update trust region based on reduction ratio
223+ if ( ratio < 0.0001 )
224+ {
225+ // Failure: ratio too small
226+ ncsuc = 0 ;
227+ ncfail ++ ;
228+
229+ mu = mu * nu ;
230+ nu = 2.0 * nu ;
231+
232+ delta = 0.25 * delta ;
233+ }
234+ else if ( ratio < 0.25 )
235+ {
236+ // Accept but shrink
237+ ncfail = 0 ;
238+ ncsuc = 0 ;
239+
240+ delta = 0.5 * delta ;
241+
242+ var temp = 1.0 - Math . Pow ( ( 2.0 * ratio - 1.0 ) , 3 ) ;
243+ temp = Math . Max ( temp , 1.0 / 3.0 ) ;
244+ mu = mu * temp ;
245+ }
246+ else if ( ratio < 0.75 )
247+ {
248+ // Accept + mild delta increase
249+ ncsuc ++ ;
250+ ncfail = 0 ;
251+
252+ delta = Math . Max ( delta , pnorm ) ;
253+
254+ var temp = 1.0 - Math . Pow ( ( 2.0 * ratio - 1.0 ) , 3 ) ;
255+ temp = Math . Max ( temp , 1.0 / 3.0 ) ;
256+ mu = mu * temp ;
257+ }
258+ else
259+ {
260+ // ratio >= 0.75
261+ ncsuc ++ ;
262+ ncfail = 0 ;
263+
264+ delta = Math . Max ( delta , 2.0 * pnorm ) ;
183265
184- if ( rho > 0.0 )
266+ var temp = 1.0 - Math . Pow ( ( 2.0 * ratio - 1.0 ) , 3 ) ;
267+ temp = Math . Max ( temp , 1.0 / 3.0 ) ;
268+ mu = mu * temp ;
269+ }
270+
271+ // Test for successful iteration
272+ if ( ratio >= 0.0001 )
185273 {
186- // accepted
274+ // Update parameters
187275 Pnew . CopyTo ( P ) ;
188276 RSS = RSSnew ;
189277
190- // update gradient and Hessian
191- ( Gradient , Hessian ) = EvaluateJacobian ( objective , P ) ;
192- diagonalOfHessian = Hessian . Diagonal ( ) ;
278+ // Recalculate gradient and Hessian at new point
279+ ( Gradient , Hessian ) = EvaluateJacobian ( objectiveModel , P ) ;
193280
194- // if ||g||_oo <= gtol, found and stop
281+ // Check convergence criteria
195282 if ( Gradient . InfinityNorm ( ) <= gradientTolerance )
196283 {
197284 exitCondition = ExitCondition . RelativeGradient ;
198285 }
199286
200- // if ||R||^2 < fTol, found and stop
201287 if ( RSS <= functionTolerance )
202288 {
203- exitCondition = ExitCondition . Converged ; // SmallRSS
289+ exitCondition = ExitCondition . Converged ;
204290 }
205291
206- mu = mu * Math . Max ( 1.0 / 3.0 , 1.0 - Math . Pow ( 2.0 * rho - 1.0 , 3 ) ) ;
207- nu = 2 ;
208-
209- break ;
292+ break ; // Exit inner loop, step accepted
210293 }
211294 else
212295 {
213- // rejected, increased μ
296+ // Step was rejected, restore original Hessian
297+ Hessian . SetDiagonal ( savedDiagonal ) ;
298+
299+ // Update mu and nu
214300 mu = mu * nu ;
215- nu = 2 * nu ;
301+ nu = 2.0 * nu ;
216302
217- Hessian . SetDiagonal ( diagonalOfHessian ) ;
303+ // If we're making no progress, exit the inner loop
304+ if ( ncfail >= 2 )
305+ {
306+ break ; // Exit inner loop, try a new Jacobian
307+ }
218308 }
219309 }
220310 }
221311
312+ // Check if max iterations reached
222313 if ( iterations >= maximumIterations )
223314 {
224315 exitCondition = ExitCondition . ExceedIterations ;
225316 }
226317
227- return new NonlinearMinimizationResult ( objective , iterations , exitCondition ) ;
318+ return new NonlinearMinimizationResult ( objectiveModel , iterations , exitCondition ) ;
228319 }
229320 }
230321}
0 commit comments