Repository: statsmodels/statsmodels

Issue:
BUG: ExponentialSmoothing is returning NaNs from forecast method without any error
#### Describe the bug

ExponentialSmoothing is returning NaNs from forecast method without raising any error or warning on particular data with particular set of parameters. This behaviour is non-deterministic as it's connected with `use_basinhopping` parameter (Basin-hopping is a stochastic algorithm)

#### Code Sample producing described behaviour
```
import pandas as pd
from pandas import Timestamp
from statsmodels.tsa.api import ExponentialSmoothing 

data = pd.DataFrame({'data': {Timestamp('2010-03-31 00:00:00', freq='Q-DEC'): 80,
                              Timestamp('2010-06-30 00:00:00', freq='Q-DEC'): 113,
                              Timestamp('2010-09-30 00:00:00', freq='Q-DEC'): 84,
                              Timestamp('2010-12-31 00:00:00', freq='Q-DEC'): 78,
                              Timestamp('2011-03-31 00:00:00', freq='Q-DEC'): 47,
                              Timestamp('2011-06-30 00:00:00', freq='Q-DEC'): 65,
                              Timestamp('2011-09-30 00:00:00', freq='Q-DEC'): 77,
                              Timestamp('2011-12-31 00:00:00', freq='Q-DEC'): 56,
                              Timestamp('2012-03-31 00:00:00', freq='Q-DEC'): 47,
                              Timestamp('2012-06-30 00:00:00', freq='Q-DEC'): 70,
                              Timestamp('2012-09-30 00:00:00', freq='Q-DEC'): 47,
                              Timestamp('2012-12-31 00:00:00', freq='Q-DEC'): 34,
                              Timestamp('2013-03-31 00:00:00', freq='Q-DEC'): 44,
                              Timestamp('2013-06-30 00:00:00', freq='Q-DEC'): 40,
                              Timestamp('2013-09-30 00:00:00', freq='Q-DEC'): 63,
                              Timestamp('2013-12-31 00:00:00', freq='Q-DEC'): 54,
                              Timestamp('2014-03-31 00:00:00', freq='Q-DEC'): 43,
                              Timestamp('2014-06-30 00:00:00', freq='Q-DEC'): 53,
                              Timestamp('2014-09-30 00:00:00', freq='Q-DEC'): 88,
                              Timestamp('2014-12-31 00:00:00', freq='Q-DEC'): 67,
                              Timestamp('2015-03-31 00:00:00', freq='Q-DEC'): 46,
                              Timestamp('2015-06-30 00:00:00', freq='Q-DEC'): 91,
                              Timestamp('2015-09-30 00:00:00', freq='Q-DEC'): 133,
                              Timestamp('2015-12-31 00:00:00', freq='Q-DEC'): 123,
                              Timestamp('2016-03-31 00:00:00', freq='Q-DEC'): 131,
                              Timestamp('2016-06-30 00:00:00', freq='Q-DEC'): 130,
                              Timestamp('2016-09-30 00:00:00', freq='Q-DEC'): 117,
                              Timestamp('2016-12-31 00:00:00', freq='Q-DEC'): 91,
                              Timestamp('2017-03-31 00:00:00', freq='Q-DEC'): 72,
                              Timestamp('2017-06-30 00:00:00', freq='Q-DEC'): 108,
                              Timestamp('2017-09-30 00:00:00', freq='Q-DEC'): 122,
                              Timestamp('2017-12-31 00:00:00', freq='Q-DEC'): 114,
                              Timestamp('2018-03-31 00:00:00', freq='Q-DEC'): 70,
                              Timestamp('2018-06-30 00:00:00', freq='Q-DEC'): 71,
                              Timestamp('2018-09-30 00:00:00', freq='Q-DEC'): 90,
                              Timestamp('2018-12-31 00:00:00', freq='Q-DEC'): 82,
                              Timestamp('2019-03-31 00:00:00', freq='Q-DEC'): 67,
                              Timestamp('2019-06-30 00:00:00', freq='Q-DEC'): 112}})

model_params = {'damped': True, 'seasonal':'add', 'seasonal_periods':4, 'trend': 'add', 'endog':data['data'], 'freq': 'Q-DEC'}

#This block of code needs to be run several times as the output is not always np.nan as the result is non-deterministic
model = ExponentialSmoothing(**model_params)
model_ = model.fit(**{'use_basinhopping':True}) 
forec = model_.forecast(12)
print(forec) 
```

| 2019-09-30 00:00:00 | nan |
| 2019-12-31 00:00:00 | nan |
| 2020-03-31 00:00:00 | nan |
| 2020-06-30 00:00:00 | nan |
| 2020-09-30 00:00:00 | nan |
| 2020-12-31 00:00:00 | nan |
| 2021-03-31 00:00:00 | nan |
| 2021-06-30 00:00:00 | nan |
| 2021-09-30 00:00:00 | nan |
| 2021-12-31 00:00:00 | nan |
| 2022-03-31 00:00:00 | nan |
| 2022-06-30 00:00:00 | nan |

#### Expected Output
Output should not contains np.nan


#### Output of ``import statsmodels.api as sm; sm.show_versions()``

<details>
INSTALLED VERSIONS
------------------
Python: 3.7.2.final.0
OS: Darwin 18.7.0 Darwin Kernel Version 18.7.0: Sat Oct 12 00:02:19 PDT 2019; root:xnu-4903.278.12~1/RELEASE_X86_64 x86_64
byteorder: little
LC_ALL: None
LANG: None

Statsmodels
===========

Installed: 0.10.1 

Required Dependencies
=====================

cython: 0.29.13 
numpy: 1.14.6 
scipy: 1.2.2 
pandas: 0.24.1 
    dateutil: 2.8.0 
patsy: 0.5.1 

Optional Dependencies
=====================
matplotlib: 3.1.1
    backend: module://ipykernel.pylab.backend_inline 
cvxopt: Not installed
joblib: 0.13.2 

Developer Tools
================

IPython: 7.8.0 
    jinja2: 2.10.1 
sphinx: 
    pygments: 2.4.2 
pytest: 5.0.1 
virtualenv: Not installed
</details>



Discussion:
Thanks for the report! The problem appears to be that a `NaN` is showing up in the `initial_slope`.
It looks like the problem is that basinhopping can sometimes select `damping_slope` exactly equal to zero, in which case there is a division by zero problem. Should be an easy fix.

Resolving patch:
diff --git a/statsmodels/tsa/holtwinters.py b/statsmodels/tsa/holtwinters.py
--- a/statsmodels/tsa/holtwinters.py
+++ b/statsmodels/tsa/holtwinters.py
@@ -949,7 +949,7 @@ def _predict(self, h=None, smoothing_level=None, smoothing_slope=None,
                        'smoothing_seasonal': gamma,
                        'damping_slope': phi if damped else np.nan,
                        'initial_level': lvls[0],
-                       'initial_slope': b[0] / phi,
+                       'initial_slope': b[0] / phi if phi > 0 else 0,
                        'initial_seasons': s[:m],
                        'use_boxcox': use_boxcox,
                        'lamda': lamda,


Test patch:
diff --git a/statsmodels/tsa/tests/test_holtwinters.py b/statsmodels/tsa/tests/test_holtwinters.py
--- a/statsmodels/tsa/tests/test_holtwinters.py
+++ b/statsmodels/tsa/tests/test_holtwinters.py
@@ -522,3 +522,17 @@ def test_integer_array(reset_randomstate):
     y = y.astype(np.long)
     res = ExponentialSmoothing(y,trend='add').fit()
     assert res.params['smoothing_level'] != 0.0
+
+
+def test_damping_slope_zero():
+    endog = np.arange(10)
+    mod = ExponentialSmoothing(endog, trend='add', damped=True)
+    res1 = mod.fit(smoothing_level=1, smoothing_slope=0.0, damping_slope=1e-20)
+    pred1 = res1.predict(start=0)
+    assert_allclose(pred1, np.r_[0., np.arange(9)], atol=1e-10)
+
+    res2 = mod.fit(smoothing_level=1, smoothing_slope=0.0, damping_slope=0)
+    pred2 = res2.predict(start=0)
+    assert_allclose(pred2, np.r_[0., np.arange(9)], atol=1e-10)
+
+    assert_allclose(pred1, pred2, atol=1e-10)