scipy: BUG (?): box-cox related `BracketError`-s downstream in `sktime`
Describe your issue.
Since 1.11.0, the BoxCoxTransformer
in sktime
is failing with a BracketError
, which ultimately comes from optimize.brent
and optimize.fminbound
.
We would appreciate help with diagnosing the issue, as none of the more obvious fixes have helped.
Further details:
- the original code was a copy of
boxcox_normmax
inscipy
. Replacing it with currentboxcox_normmax
and/or using customoptimize.minimize_scalar
does not fix the error, see https://github.com/sktime/sktime/pull/4770. - the code works without any problems on
< 1.11.0
- specifying an initial bracket that should theoretically work (e.g.,
(1.0, 2.0)
) does not seem to help either.
PS: the docstrings in the current main
version were not too clear about the “should”, i.e., when should scipy
be called and when a de-novo implementation. The PR https://github.com/sktime/sktime/pull/4770 also fixes that.
Reproducing Code Example
With sktime 0.20.0
or earlier versions or current main
, and scipy 1.11.0
:
from sktime.transformations.series.boxcox import BoxCoxTransformer
from sktime.utils._testing.panel import _make_panel_X
X = _make_panel_X(
n_instances=7, n_columns=1, n_timepoints=10, random_state=42
)
est = BoxCoxTransformer()
est.fit(X)
sktime
bug report: https://github.com/sktime/sktime/issues/4769
Error message
BracketError: The algorithm terminated without finding a valid bracket. Consider trying different initial points.
Full traceback:
---------------------------------------------------------------------------
BracketError Traceback (most recent call last)
Cell In[2], line 10
4 X = _make_panel_X(
5 n_instances=7, n_columns=1, n_timepoints=10, random_state=42
6 )
8 est = BoxCoxTransformer()
---> 10 est.fit(X)
File C:\Workspace\sktime\sktime\transformations\base.py:439, in BaseTransformer.fit(self, X, y)
436 self._fit(X=X_inner, y=y_inner)
437 else:
438 # otherwise we call the vectorized version of fit
--> 439 self._vectorize("fit", X=X_inner, y=y_inner)
441 # this should happen last: fitted state is set to True
442 self._is_fitted = True
File C:\Workspace\sktime\sktime\transformations\base.py:1205, in BaseTransformer._vectorize(self, methodname, **kwargs)
1202 else:
1203 transformers_ = self.transformers_
-> 1205 self.transformers_ = X.vectorize_est(
1206 transformers_, method=methodname, **kwargs
1207 )
1208 return self
1210 if methodname in TRAFO_METHODS:
1211 # loop through fitted transformers one-by-one, and transform series/panels
File C:\Workspace\sktime\sktime\datatypes\_vectorize.py:584, in VectorizedDF.vectorize_est(self, estimator, method, args, args_rowvec, return_type, rowname_default, colname_default, varname_of_self, **kwargs)
581 args_i[varname_of_self] = group
583 est_i_method = getattr(est_i, method)
--> 584 est_i_result = est_i_method(**args_i)
586 if group_name is None:
587 group_name = rowname_default
File C:\Workspace\sktime\sktime\transformations\base.py:436, in BaseTransformer.fit(self, X, y)
434 # we call the ordinary _fit if no looping/vectorization needed
435 if not vectorization_needed:
--> 436 self._fit(X=X_inner, y=y_inner)
437 else:
438 # otherwise we call the vectorized version of fit
439 self._vectorize("fit", X=X_inner, y=y_inner)
File C:\Workspace\sktime\sktime\transformations\series\boxcox.py:156, in BoxCoxTransformer._fit(self, X, y)
154 X = X.flatten()
155 if self.method != "guerrero":
--> 156 self.lambda_ = _boxcox_normmax(X, bounds=self.bounds, method=self.method)
157 else:
158 self.lambda_ = _guerrero(X, self.sp, self.bounds)
File C:\Workspace\sktime\sktime\transformations\series\boxcox.py:377, in _boxcox_normmax(x, bounds, brack, method)
374 raise ValueError("Method %s not recognized." % method)
376 optimfunc = methods[method]
--> 377 return optimfunc(x)
File C:\Workspace\sktime\sktime\transformations\series\boxcox.py:364, in _boxcox_normmax.._mle(x)
360 def _eval_mle(lmb, data):
361 # function to minimize
362 return -boxcox_llf(lmb, data)
--> 364 return optimizer(_eval_mle, args=(x,))
File C:\Workspace\sktime\sktime\transformations\series\boxcox.py:322, in _make_boxcox_optimizer..optimizer(func, args)
321 def optimizer(func, args):
--> 322 return optimize.brent(func, brack=brack, args=args)
File c:\ProgramData\Anaconda3\envs\sktime-skbase-311\Lib\site-packages\scipy\optimize\_optimize.py:2641, in brent(func, args, brack, tol, full_output, maxiter)
2569 """
2570 Given a function of one variable and a possible bracket, return
2571 a local minimizer of the function isolated to a fractional precision
(...)
2637
2638 """
2639 options = {'xtol': tol,
2640 'maxiter': maxiter}
-> 2641 res = _minimize_scalar_brent(func, brack, args, **options)
2642 if full_output:
2643 return res['x'], res['fun'], res['nit'], res['nfev']
File c:\ProgramData\Anaconda3\envs\sktime-skbase-311\Lib\site-packages\scipy\optimize\_optimize.py:2678, in _minimize_scalar_brent(func, brack, args, xtol, maxiter, disp, **unknown_options)
2675 brent = Brent(func=func, args=args, tol=tol,
2676 full_output=True, maxiter=maxiter, disp=disp)
2677 brent.set_bracket(brack)
-> 2678 brent.optimize()
2679 x, fval, nit, nfev = brent.get_result(full_output=True)
2681 success = nit < maxiter and not (np.isnan(x) or np.isnan(fval))
File c:\ProgramData\Anaconda3\envs\sktime-skbase-311\Lib\site-packages\scipy\optimize\_optimize.py:2448, in Brent.optimize(self)
2445 def optimize(self):
2446 # set up for optimization
2447 func = self.func
-> 2448 xa, xb, xc, fa, fb, fc, funcalls = self.get_bracket_info()
2449 _mintol = self._mintol
2450 _cg = self._cg
File c:\ProgramData\Anaconda3\envs\sktime-skbase-311\Lib\site-packages\scipy\optimize\_optimize.py:2417, in Brent.get_bracket_info(self)
2415 xa, xb, xc, fa, fb, fc, funcalls = bracket(func, args=args)
2416 elif len(brack) == 2:
-> 2417 xa, xb, xc, fa, fb, fc, funcalls = bracket(func, xa=brack[0],
2418 xb=brack[1], args=args)
2419 elif len(brack) == 3:
2420 xa, xb, xc = brack
File c:\ProgramData\Anaconda3\envs\sktime-skbase-311\Lib\site-packages\scipy\optimize\_optimize.py:3047, in bracket(func, xa, xb, args, grow_limit, maxiter)
3045 e = BracketError(msg)
3046 e.data = (xa, xb, xc, fa, fb, fc, funcalls)
-> 3047 raise e
3049 return xa, xb, xc, fa, fb, fc, funcalls
BracketError: The algorithm terminated without finding a valid bracket. Consider trying different initial points.
SciPy/NumPy/Python version and system information
1.11.0 1.25.0 sys.version_info(major=3, minor=11, micro=3, releaselevel='final', serial=0)
Build Dependencies:
blas:
detection method: pkgconfig
found: true
include directory: /c/opt/64/include
lib directory: /c/opt/64/lib
name: openblas
openblas configuration: USE_64BITINT= DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS=
NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP= SKYLAKEX MAX_THREADS=2
pc file directory: c:/opt/64/lib/pkgconfig
version: 0.3.21.dev
lapack:
detection method: pkgconfig
found: true
include directory: /c/opt/64/include
lib directory: /c/opt/64/lib
name: openblas
openblas configuration: USE_64BITINT= DYNAMIC_ARCH=1 DYNAMIC_OLDER= NO_CBLAS=
NO_LAPACK= NO_LAPACKE= NO_AFFINITY=1 USE_OPENMP= SKYLAKEX MAX_THREADS=2
pc file directory: c:/opt/64/lib/pkgconfig
version: 0.3.21.dev
pybind11:
detection method: config-tool
include directory: unknown
name: pybind11
version: 2.10.4
Compilers:
c:
commands: cc
linker: ld.bfd
name: gcc
version: 10.3.0
c++:
commands: c++
linker: ld.bfd
name: gcc
version: 10.3.0
cython:
commands: cython
linker: cython
name: cython
version: 0.29.35
fortran:
commands: gfortran
linker: ld.bfd
name: gcc
version: 10.3.0
pythran:
include directory: C:\Users\runneradmin\AppData\Local\Temp\pip-build-env-12ywd98f\overlay\Lib\site-packages/pythran
version: 0.13.1
Machine Information:
build:
cpu: x86_64
endian: little
family: x86_64
system: windows
cross-compiled: false
host:
cpu: x86_64
endian: little
family: x86_64
system: windows
Python Information:
path: C:\Users\runneradmin\AppData\Local\Temp\cibw-run-uqg6lkbl\cp311-win_amd64\build\venv\Scripts\python.exe
version: '3.11'
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 26 (26 by maintainers)
Ok, @mdhaber, so can you confirm your suggestion: I should just make the negative examples length 3?