@@ -185,17 +185,11 @@ def unbounded_and_init():
185185 (rosengrad , BB ()), # 5
186186 (rosengrad , Broyden (0.5 )), # 6
187187 (rosenboth , HybridFixed (BFGS ())), # 7
188- (rosenboth , HybridFixed (SR1 ())), # 8
189- (rosenboth , HybridFixed (BFGS (init_with_hess = True ))), # 9
190- (rosenboth , HybridFixed (SR1 (init_with_hess = True ))), # 10
188+ (rosenboth , HybridFixed (SR1 ())), # 8 # 10
191189 (rosenboth , HybridFraction (BFGS ())), # 11
192190 (rosenboth , HybridFraction (SR1 ())), # 12
193- (rosenboth , HybridFraction (BFGS (init_with_hess = True ))), # 13
194- (rosenboth , HybridFraction (SR1 (init_with_hess = True ))), # 14
195191 (fletcher , FX (BFGS ())), # 15
196192 (fletcher , FX (SR1 ())), # 16
197- (fletcher , FX (BFGS (init_with_hess = True ))), # 17
198- (fletcher , FX (SR1 (init_with_hess = True ))), # 18
199193 (fletcher , SSM (0.0 )), # 19
200194 (fletcher , SSM (0.5 )), # 20
201195 (fletcher , SSM (1.0 )), # 21
@@ -494,3 +488,140 @@ def test_wrong_options():
494488 verbose = logging .INFO ,
495489 options = {Options .SUBSPACE_DIM : '2D' },
496490 )
491+
492+
493+ def test_hess0_initialization ():
494+ """
495+ Test that hess0 parameter correctly initializes Hessian approximation.
496+ """
497+ lb , ub , x0 = finite_bounds_include_optimum ()
498+ fun = rosengrad
499+ fun_with_hess = rosenboth
500+
501+ # Test 1: Verify hess0 is used when provided with hessian_update
502+ custom_hess0 = np .eye (len (x0 )) * 10.0
503+ opt_with_hess0 = Optimizer (
504+ fun ,
505+ ub = ub ,
506+ lb = lb ,
507+ verbose = logging .WARNING ,
508+ options = {Options .MAXITER : 1 }, # Only run one iteration
509+ hessian_update = BFGS (),
510+ )
511+ opt_with_hess0 .minimize (x0 , hess0 = custom_hess0 )
512+ assert opt_with_hess0 .hess is not None
513+
514+ # Test 2: Verify default initialization when hess0 is not provided
515+ opt_without_hess0 = Optimizer (
516+ fun ,
517+ ub = ub ,
518+ lb = lb ,
519+ verbose = logging .WARNING ,
520+ options = {Options .MAXITER : 1 },
521+ hessian_update = BFGS (),
522+ )
523+ opt_without_hess0 .minimize (x0 )
524+
525+ # Test 3: Verify hess0 has correct dimensions
526+ wrong_dim_hess0 = np .eye (len (x0 ) + 1 )
527+ opt_wrong_dim = Optimizer (
528+ fun ,
529+ ub = ub ,
530+ lb = lb ,
531+ verbose = logging .WARNING ,
532+ hessian_update = BFGS (),
533+ )
534+ with pytest .raises (ValueError ):
535+ opt_wrong_dim .minimize (x0 , hess0 = wrong_dim_hess0 )
536+
537+ # Test 4: Verify hess0 works with different update schemes
538+ for happ_class in [BFGS , DFP , SR1 , Broyden ]:
539+ happ = happ_class () if happ_class != Broyden else Broyden (phi = 0.5 )
540+ custom_hess = np .eye (len (x0 )) * 5.0
541+ opt = Optimizer (
542+ fun ,
543+ ub = ub ,
544+ lb = lb ,
545+ verbose = logging .WARNING ,
546+ options = {Options .MAXITER : 2 , Options .FATOL : 0 },
547+ hessian_update = happ ,
548+ )
549+ opt .minimize (x0 , hess0 = custom_hess )
550+ assert opt .iteration >= 1 , f'Failed for { happ_class .__name__ } '
551+
552+ # Test 5: Verify hess0 is ignored when no hessian_update is provided
553+ opt_no_update = Optimizer (
554+ fun_with_hess ,
555+ ub = ub ,
556+ lb = lb ,
557+ verbose = logging .WARNING ,
558+ options = {Options .MAXITER : 1 },
559+ )
560+ hess0_ignored = np .eye (len (x0 )) * 100.0
561+ opt_no_update .minimize (x0 , hess0 = hess0_ignored )
562+
563+ # Test 6: Test initialization with exact Hessian
564+ opt_hess_init = Optimizer (
565+ fun_with_hess ,
566+ ub = ub ,
567+ lb = lb ,
568+ verbose = logging .WARNING ,
569+ options = {Options .MAXITER : 10 , Options .FATOL : 1e-8 },
570+ hessian_update = HybridFixed (BFGS ()),
571+ )
572+ opt_hess_init .minimize (x0 , hess0 = 'hess' )
573+ iterations_with_hess = opt_hess_init .iteration
574+
575+ # Compare with BFGS without using initial Hessian
576+ opt_no_hess_init = Optimizer (
577+ fun ,
578+ ub = ub ,
579+ lb = lb ,
580+ verbose = logging .WARNING ,
581+ options = {Options .MAXITER : 10 , Options .FATOL : 1e-8 },
582+ hessian_update = BFGS (),
583+ )
584+ opt_no_hess_init .minimize (x0 )
585+ iterations_without_hess = opt_no_hess_init .iteration
586+
587+ # Using exact Hessian for initialization should help convergence
588+ assert iterations_with_hess <= iterations_without_hess or (
589+ opt_hess_init .converged and opt_no_hess_init .converged
590+ ), 'Hessian initialization should help convergence'
591+
592+ # Test 8: Verify hess0 affects convergence behavior
593+ true_hess_at_x0 = np .array (
594+ [
595+ [1200 * x0 [0 ] ** 2 - 400 * x0 [1 ] + 2 , - 400 * x0 [0 ]],
596+ [- 400 * x0 [0 ], 200 ],
597+ ]
598+ )
599+
600+ opt_good_init = Optimizer (
601+ fun ,
602+ ub = ub ,
603+ lb = lb ,
604+ verbose = logging .WARNING ,
605+ options = {Options .MAXITER : 100 , Options .FATOL : 1e-8 },
606+ hessian_update = BFGS (),
607+ )
608+ opt_good_init .minimize (x0 , hess0 = true_hess_at_x0 )
609+ iterations_good = opt_good_init .iteration
610+
611+ # Use a poor initial Hessian approximation
612+ poor_hess = np .eye (len (x0 )) * 0.01
613+ opt_poor_init = Optimizer (
614+ fun ,
615+ ub = ub ,
616+ lb = lb ,
617+ verbose = logging .WARNING ,
618+ options = {Options .MAXITER : 100 , Options .FATOL : 1e-8 },
619+ hessian_update = BFGS (),
620+ )
621+ opt_poor_init .minimize (x0 , hess0 = poor_hess )
622+ iterations_poor = opt_poor_init .iteration
623+
624+ # Good initialization should converge in fewer or equal iterations
625+ assert iterations_good <= iterations_poor or (
626+ opt_good_init .converged and opt_poor_init .converged
627+ ), 'Good Hessian initialization should help convergence'
0 commit comments