@@ -439,6 +439,8 @@ def __init__(
439439 if self .options is None :
440440 self .options = ScipyOptimizer .get_default_options (self )
441441 self .tol = tol
442+ #: maximum walltime in seconds
443+ self ._maxtime_seconds : float | None = None
442444
443445 def __repr__ (self ) -> str :
444446 rep = f"<{ self .__class__ .__name__ } method={ self .method } "
@@ -601,6 +603,20 @@ def fun(x):
601603 if hessp is not None :
602604 hess = None
603605
606+ # Set callback for handling timelimit if necessary
607+ callback = None
608+ if self ._maxtime_seconds is not None and np .isfinite (
609+ self ._maxtime_seconds
610+ ):
611+ start_time = time .time ()
612+
613+ def callback (* args , ** kwargs ):
614+ elapsed_time = time .time () - start_time
615+ if elapsed_time >= self ._maxtime_seconds :
616+ raise StopIteration (
617+ f"Maximum time { self ._maxtime_seconds } s exceeded."
618+ )
619+
604620 # optimize
605621 res = scipy .optimize .minimize (
606622 fun = fun ,
@@ -612,6 +628,7 @@ def fun(x):
612628 bounds = bounds ,
613629 options = self .options ,
614630 tol = self .tol ,
631+ callback = callback ,
615632 )
616633 # extract fval/grad from result
617634 grad = getattr (res , "jac" , None )
@@ -670,6 +687,41 @@ def set_maxiter(self, iterations: int) -> None:
670687 else :
671688 self .options ["maxiter" ] = iterations
672689
690+ def supports_maxtime (self ) -> bool :
691+ """
692+ Check whether optimizer supports time limits.
693+
694+ Returns
695+ -------
696+ True if optimizer supports setting a maximum wall time,
697+ False otherwise.
698+ """
699+ # TNC neither supports time limits nor callback functions
700+ return self .method .lower () != "tnc"
701+
702+ def set_maxtime (self , seconds : float ) -> None :
703+ """
704+ Set the maximum wall time for optimization.
705+
706+ Parameters
707+ ----------
708+ seconds
709+ Maximum wall time in seconds.
710+
711+ Raises
712+ ------
713+ NotImplementedError
714+ If the optimizer does not support time limits.
715+ """
716+ if not self .supports_maxtime ():
717+ raise NotImplementedError (
718+ f"{ self .__class__ .__name__ } method { self .method } does not "
719+ "support time limits. "
720+ f"Check supports_maxtime() before calling set_maxtime()."
721+ )
722+
723+ self ._maxtime_seconds = seconds
724+
673725
674726class IpoptOptimizer (Optimizer ):
675727 """Use Ipopt (https://pypi.org/project/cyipopt/) for optimization."""
0 commit comments