@@ -16,6 +16,7 @@ from libc.stdint cimport int64_t, int32_t, uint8_t, INT32_MAX
1616from libc.string cimport memcpy
1717from cpython cimport array
1818cimport numpy as cnp
19+ import contextlib
1920
2021import os
2122import numpy as np
@@ -446,6 +447,8 @@ cdef class opt_lim_const:
446447 The optimization_limits module variable should be of this class
447448
448449 """
450+ _attrs = (' max_iter' , ' max_time' , ' converged_min' , ' num_cores' , ' squarem' ,
451+ ' formatter' , ' outstream' )
449452 cdef:
450453 int64_t _max_iter
451454 double _max_time
@@ -454,6 +457,7 @@ cdef class opt_lim_const:
454457 bint _squarem
455458 object _formatter
456459 object _outstream
460+
457461 def __cinit__ (self , max_iter = 2046 , max_time = np.inf, converged_min = 1e-14 ,
458462 num_cores = os.cpu_count()// 2 , squarem = False , formatter = StdPrinter,
459463 outstream = lambda : sys.stdout ):
@@ -473,6 +477,7 @@ cdef class opt_lim_const:
473477 def max_iter (self ):
474478 """ Maximum number of iterations before ending optimization"""
475479 return self ._max_iter
480+
476481 @max_iter.setter
477482 def max_iter (self , max_iter ):
478483 assert max_iter > 0 and np.issubdtype(type (max_iter), np.integer), ValueError (" max_iter must be integer greater than 0" )
@@ -489,6 +494,7 @@ cdef class opt_lim_const:
489494
490495 """
491496 return self ._max_time
497+
492498 @max_time.setter
493499 def max_time (self , max_time ):
494500 assert max_time > 0 and np.issubdtype(type (max_time), np.floating), ValueError (" max_time must be float and greater than 0" )
@@ -507,7 +513,8 @@ cdef class opt_lim_const:
507513
508514 @property
509515 def num_cores (self ):
510- """ Number of cores to use in optimization
516+ """
517+ Number of cores to use in optimization
511518
512519 .. note::
513520
@@ -561,7 +568,7 @@ cdef class opt_lim_const:
561568
562569 def __repr__ (self ):
563570 return (f' Optimization limits:: num_cores: {self._num_cores}, max_iter: {self._max_iter}, '
564- f' converged_min: {self._converged_min}, max_time: {self._max_time},\n '
571+ f' converged_min: {self._converged_min}, max_time: {self._max_time},squarem={self._squarem} \n '
565572 f' formatter: {self._formatter.__qualname__}, outstream={self._outstream}' )
566573
567574 def _get_max_iter (self , max_iter ):
@@ -722,25 +729,132 @@ cdef class opt_lim_const:
722729 Parameters
723730 ----------
724731 outstream : Output Stream
725- Stream .
732+ Stream to assign as outptu stream .
726733
727734 Returns
728735 -------
729736 OutputStream
730- DESCRIPTION .
737+ default output stream object .
731738
732739 """
733740 if outstream is None :
734741 return self ._outstream()
735742 return outstream
743+
744+ def __call__ (self , **kwargs ):
745+ """
746+ Open a context manager, all keyword arguments must be valid attributes
747+ of optimization_limits. Values set in kwargs, or to optimization limits
748+ within the ``with`` block will be reset upon exciting the ``with`` block.
749+
750+ This can be used as follows::
751+
752+ with hm.optimization_limits(converged_min=1e-5):
753+ out = hm.EM_H2MM_C(init, index, times)
754+
755+
756+ Parameters
757+ ----------
758+ max_iter : int, optional
759+ Maximum number of iterations before ending optimization
760+ max_time : float, optional
761+ Maximum time of optimization.
762+ converged_min : float, optional
763+ Minimum difference between loglik of succesive iterations for a
764+ model to be considered converged
765+ num_cores : int, optional
766+ Number of cores to use in optimization.
767+ squarem : bool, optional
768+ Choice of whether or not to use SQUAREM (|Varadhan2008|) method of
769+ accelerating optimization using quadratic projection of 3 successive
770+ models.
771+ formatter : Printer
772+ A :class:`Printer` subclass, used to format result of print_func
773+ for display of optimiation progress
774+ outstream : Callable, optional
775+ Callable, when called, generates an output stream for where to
776+ print displayed progress
777+
778+ Returns
779+ -------
780+ OptlimContext
781+ Special context manager class for managing :class:opt_lim_const`
782+ This class only defines the ``__init__``, ``__enter__`` and ``__exit__``
783+ magic methods for use in with statemetns
784+ """
785+ for key in kwargs.keys():
786+ if key not in self ._attrs:
787+ raise TypeError (f" {key} is not a valid keyword argument for context manager" )
788+ cntx = OptlimContext(self )
789+ for key, value in kwargs.items():
790+ if value is None :
791+ continue
792+ try :
793+ self [key] = value
794+ except Exception as e:
795+ print (" exception raised" )
796+ for k, v in cntx._values.items():
797+ self [key] = value
798+ raise e
799+ return cntx
800+
801+
802+ cdef class OptlimContext:
803+ """
804+ Context manager class, upon enter, records the values of ``optlim`` and
805+ restores upont exit
806+
807+ Parameters
808+ ----------
809+ optlim : opt_lim_const
810+ The optimization_limits to restore after exiting the context manager.
811+ """
812+ cdef:
813+ opt_lim_const _optlim
814+ dict _values
815+
816+ def __cinit__ (self , opt_lim_const optlim ):
817+ self ._optlim = optlim
818+ self ._values = {key:optlim[key] for key in opt_lim_const._attrs}
819+
820+ def __enter__ (self ):
821+ return self ._optlim
822+
823+ def __exit__ (self , exc_type , exc_value , traceback ):
824+ for key, value in self ._values.items():
825+ self ._optlim[key] = value
826+
736827
737828def get_stdout ():
738829 """ Simple callable returns sys.stdout"""
739830 return sys.stdout
740831
741- # : Like rcParams, a place to set defaults for optimization limits
832+ # : This module variable stores default values for optimization limits.
833+ # : if the values are not supplied as keyword arguments to the optimization functions,
834+ # : ``H2MM_C`` will fall back to the values stored in this variable.
835+ # : The options can be accessed either as attributions or keys.
836+ # :
837+ # : Options are:
838+ # : - max_iter: maximum number of iterations to perform during optimization
839+ # : - max_time: maximum
840+ # : - converged_min: difference between loglik of iteration at which to terminate optimization
841+ # : (if difference is less than this value, the model is considered optimized)
842+ # : - num_cores: number of cores to use in optimization
843+ # : - squarem: Whether or not to use squarem acceleration
844+ # : - formatter: printer object, defines how to format optimization progress
845+ # : - outstream: output stream buffer
846+ # :
847+ # :
848+ # : Support context manager to temporarily set a value:
849+ # :
850+ # : .. code-block::
851+ # :
852+ # : with hm.optimization_limits(max_iter=1000):
853+ # : model = hm.EM_H2MM_C(model, index, time)
854+ # :
742855optimization_limits = opt_lim_const(max_iter = 2046 , max_time = np.inf,
743856 converged_min = 1e-14 , num_cores = os.cpu_count()// 2 ,
857+ squarem = False ,
744858 formatter = StdPrinter, outstream = get_stdout)
745859
746860cdef class ConvCodes:
@@ -1715,7 +1829,7 @@ cdef class h2mm_model:
17151829 print_stream = print_stream, print_formatter = print_formatter,
17161830 print_fmt_args = print_fmt_args, print_fmt_kwargs = print_fmt_kwargs,
17171831 max_time = max_time, converged_min = converged_min, num_cores = num_cores,
1718- reset_niter = reset_niter, ll = ll, gamma = gamma, opt_array = opt_array)
1832+ reset_niter = reset_niter, ll = ll, gamma = gamma, opt_array = opt_array, squarem = squarem )
17191833 if inplace:
17201834 # separate the models from ll_array / gamma
17211835 if gamma or ll:
0 commit comments