Repository: sktime/sktime

Issue:
[DOC] change default documentation version from `latest` (`main` branch) to `stable` (latest tag) in read-the-docs configuration
Currently the [documentation website](https://www.sktime.net) defaults to `latest`, i.e. the `main` branch. So, it is possible that it shows some estimators or arguments which are not yet released and won't be available for the general users who download from `PyPI` or `conda-forge`.

Read-the docs allow to change the default version, and it can be considered to switch to `stable` to refer to [latest semantic version](https://docs.readthedocs.io/en/stable/versions.html). This is likely to be what's used by most of the direct users of `sktime` (i.e. not through another framework whose dependency is `sktime`).

To make the change, the read-the-docs owner can log in, go to `Admin`, go to `Advanced Settings`, change `Default version` and save the changes. It's likely to trigger a new build and then will be in effect once it's complete.

Comments:

Patch:
diff --git a/docs/source/conf.py b/docs/source/conf.py
index be43df43a..21b7f73e4 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -49,6 +49,7 @@ extensions = [
     "myst_parser",
     "sphinx_design",
     "sphinx_issues",
+    "versionwarning.extension",
 ]
 
 # Recommended by sphinx_design when using the MyST Parser
@@ -435,3 +436,15 @@ intersphinx_mapping = {
 
 # -- Options for _todo extension ----------------------------------------------
 todo_include_todos = False
+
+# sphinx-version-warning config
+versionwarning_messages = {
+    "latest": (
+        "This document is for the development version. "
+        'For the stable version documentation, see <a href="/en/stable/">here</a>.'
+    )
+}
+
+# Show warning at top of page
+versionwarning_body_selector = "div.document"
+versionwarning_banner_title = ""
diff --git a/pyproject.toml b/pyproject.toml
index 9f31dc096..93acba733 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -128,6 +128,7 @@ docs = [
     "sphinx_issues==1.2.0",
     "sphinx-gallery==0.6.0",
     "sphinx-design==0.3.0",
+    "sphinx-version-warning",
     "sphinx==4.1.1",
     "tabulate",
 ]
diff --git a/sktime/forecasting/base/_base.py b/sktime/forecasting/base/_base.py
index e09e0ff28..c25c19d28 100644
--- a/sktime/forecasting/base/_base.py
+++ b/sktime/forecasting/base/_base.py
@@ -1129,10 +1129,7 @@ class BaseForecaster(BaseEstimator):
         y_pred = self.predict(fh=fh, X=X)
 
         if not type(y_pred) == type(y):
-            raise TypeError(
-                "y must have same type, dims, index as expected predict return. "
-                f"expected type {type(y_pred)}, but found {type(y)}"
-            )
+            y = convert_to(y, self._y_mtype_last_seen)
 
         y_res = y - y_pred
 


Test patch:
diff --git a/sktime/forecasting/base/tests/test_base_bugs.py b/sktime/forecasting/base/tests/test_base_bugs.py
index 6a92d3037..17d69c598 100644
--- a/sktime/forecasting/base/tests/test_base_bugs.py
+++ b/sktime/forecasting/base/tests/test_base_bugs.py
@@ -9,9 +9,11 @@ from sktime.forecasting.model_selection import (
     ExpandingWindowSplitter,
     ForecastingGridSearchCV,
 )
+from sktime.forecasting.naive import NaiveForecaster
 from sktime.forecasting.reconcile import ReconcilerForecaster
 from sktime.forecasting.trend import PolynomialTrendForecaster
 from sktime.transformations.hierarchical.aggregate import Aggregator
+from sktime.transformations.series.difference import Differencer
 from sktime.utils._testing.hierarchical import _make_hierarchical
 from sktime.utils.validation._dependencies import _check_estimator_deps
 
@@ -58,3 +60,17 @@ def test_heterogeneous_get_fitted_params():
 
     reconciler.fit(y_agg)
     reconciler.get_fitted_params()  # triggers an error pre-fix
+
+
+def test_predict_residuals_conversion():
+    """Regression test for bugfix #4766, related to predict_residuals internal type."""
+    from sktime.datasets import load_longley
+    from sktime.forecasting.model_selection import temporal_train_test_split
+
+    y, X = load_longley()
+    y_train, y_test, X_train, X_test = temporal_train_test_split(y, X)
+    pipe = Differencer() * NaiveForecaster()
+    pipe.fit(y=y_train, X=X_train, fh=[1, 2, 3, 4])
+    result = pipe.predict_residuals()
+
+    assert type(result) == type(y_train)