Repository: sktime/sktime

Issue:
[ENH] Pass `pmdarima` and `prophet` models' `fit_params` to constructor
Native (not `sktime`) `pmdarima` models `ARIMA` and `AutoARIMA`, and `Prophet` (in`prophet`, not `sktime`) have some parameters which can be passed via `fit_params` (natively in `pmdarima` or `prophet`).
Users should be able to pass those parameters through the constructor of the `sktime` interface classes.
We should:

* check whether we are missing any parameters in the constructors that can be passed as `fit_params`. (if none are missing, we do not need to do the second step for that class) 
    - [x] `Prophet`
    - [ ] `ARIMA`
    - [ ] `AutoARIMA` 
* if yes, add them to the constructor and ensure they are passed on to the interfaced model. The recipe is as below.
    - [x] `Prophet`
    - [ ] `ARIMA`
    - [ ] `AutoARIMA` 
 
Good first issue since recipe is available.

---
Recipe for interfacing models with `fit_params` or any other `fit` args, in sktime:

* pass the parameter(s) to the constructor (the `__init__` method), using explicit parameter names and default, not `**kwargs`
* write it to `self` in the constructor
* in `_fit`, read it from `self` and pass it to the wrapped model's `fit` method
* ensure docstring is complete and defaults documented (optimally same as in wrapped estimator)

This allows to expose all parameter choices via the scikit-learn compatible interface (which relies on the constructor to expose all parameter choices).

For an example of this, you can look at `smoothing_level` in `ExponentialSmoothing`.


Discussion:
Can I work on this issue @fkiraly ?
@Ris-Bali yes pls 🙂
FYI @jasminelaw, duplicate of your #3687.

Contributions appreciated!

Resolving patch:
diff --git a/sktime/forecasting/base/adapters/_fbprophet.py b/sktime/forecasting/base/adapters/_fbprophet.py
--- a/sktime/forecasting/base/adapters/_fbprophet.py
+++ b/sktime/forecasting/base/adapters/_fbprophet.py
@@ -116,11 +116,12 @@ def _fit(self, y, X, fh):
             df["cap"] = self.growth_cap
             df["floor"] = self.growth_floor
 
+        fit_kwargs = self.fit_kwargs or {}
         if self.verbose:
-            self._forecaster.fit(df=df)
+            self._forecaster.fit(df=df, **fit_kwargs)
         else:
             with _suppress_stdout_stderr():
-                self._forecaster.fit(df=df)
+                self._forecaster.fit(df=df, **fit_kwargs)
 
         return self
 
diff --git a/sktime/forecasting/fbprophet.py b/sktime/forecasting/fbprophet.py
--- a/sktime/forecasting/fbprophet.py
+++ b/sktime/forecasting/fbprophet.py
@@ -46,7 +46,7 @@ class Prophet(_ProphetAdapter):
         trend. If 'logistic' specified float for 'growth_cap' must be provided.
     growth_floor: float, default=0
         Growth saturation minimum value.
-        Used only if  `growth="logistic"`, has no effect otherwise
+        Used only if `growth="logistic"`, has no effect otherwise
         (if `growth` is not `"logistic"`).
     growth_cap: float, default=None
         Growth saturation maximum aka carrying capacity.
@@ -111,6 +111,10 @@ class Prophet(_ProphetAdapter):
     stan_backend: str or None, default=None
         str as defined in StanBackendEnum. If None, will try to
         iterate over all available backends and find the working one.
+    fit_kwargs: dict or None, default=None
+        Dict with args for Prophet.fit().
+        These are additional arguments passed to the optimizing or sampling
+        functions in Stan.
 
     References
     ----------
@@ -158,6 +162,7 @@ def __init__(
         uncertainty_samples=1000,
         stan_backend=None,
         verbose=0,
+        fit_kwargs=None,
     ):
         self.freq = freq
         self.add_seasonality = add_seasonality
@@ -182,6 +187,7 @@ def __init__(
         self.uncertainty_samples = uncertainty_samples
         self.stan_backend = stan_backend
         self.verbose = verbose
+        self.fit_kwargs = fit_kwargs
 
         super().__init__()
 
@@ -233,5 +239,6 @@ def get_test_params(cls, parameter_set="default"):
             "daily_seasonality": False,
             "uncertainty_samples": 10,
             "verbose": False,
+            "fit_kwargs": {"seed": 12345},
         }
         return params


Test patch:
diff --git a/sktime/forecasting/tests/test_prophet.py b/sktime/forecasting/tests/test_prophet.py
--- a/sktime/forecasting/tests/test_prophet.py
+++ b/sktime/forecasting/tests/test_prophet.py
@@ -3,7 +3,9 @@
 # copyright: sktime developers, BSD-3-Clause License (see LICENSE file)
 """
 
-__author__ = ["fkiraly"]
+__author__ = ["fkiraly", "tpvasconcelos"]
+
+from unittest import mock
 
 import pandas as pd
 import pytest
@@ -77,3 +79,28 @@ def test_prophet_period_fh(convert_to_datetime):
     else:
         assert isinstance(y_pred.index, pd.PeriodIndex)
         assert (y_pred.index == fh_index).all()
+
+
+@pytest.mark.skipif(
+    not run_test_for_class(Prophet),
+    reason="run test only if softdeps are present and incrementally (if requested)",
+)
+@pytest.mark.parametrize(
+    "fit_kwargs", [None, {"foo": "bar"}, {"foo1": "bar1", "foo2": "bar2"}]
+)
+def test_prophet_fit_kwargs_are_passed_down(fit_kwargs: dict):
+    """Test that the `fit_kwargs` hyperparameter is passed down to Prophet.fit()."""
+    from sktime.datasets import load_airline
+    from sktime.forecasting.fbprophet import Prophet
+
+    y = load_airline()
+    with mock.patch("prophet.forecaster.Prophet.fit") as mock_fit:
+        forecaster = Prophet(fit_kwargs=fit_kwargs)
+        forecaster.fit(y)
+        mock_fit.assert_called_once()
+        assert mock_fit.call_args.args == ()
+        call_kwargs = mock_fit.call_args.kwargs
+        # `df` should always be one of the arguments but
+        # we don't care about its actual value here
+        call_kwargs.pop("df")
+        assert call_kwargs == (fit_kwargs or {})