Repository: sktime/sktime
Pull request: [BUG] fix incorrect scitype inference in forecasting pipelines for polymorphic forecasters
Fixes #8218 

Problem statement:
[BUG] PytorchForecastingNBeats cannot be used within a TransformedTargetForecaster pipeline
**Describe the bug**
PytorchForecastingNBeats cannot be used within a TransformedTargetForecaster pipeline since it is not considered a "forecaster", but its first tag is "global_forecaster".

**To Reproduce**
```python
from sktime.forecasting.pytorchforecasting import PytorchForecastingNBeats
from sklearn.preprocessing import MinMaxScaler

model = PytorchForecastingNBeats()
# model._tags["object_type"] = "forecaster" # with this, it will work.

pipe = MinMaxScaler() * model
```

Error:
```
TypeError: estimators passed to TransformedTargetForecaster must be either transformer or forecaster
```

**Expected behavior**
Pipeline creation works.

**Versions**
Python dependencies:
          pip: 22.3.1
       sktime: 0.37.0
      sklearn: 1.6.1
       skbase: 0.12.2
        numpy: 1.23.5
        scipy: 1.10.0
       pandas: 1.5.3
   matplotlib: 3.7.0
       joblib: 1.2.0
        numba: 0.56.4
  statsmodels: 0.13.5
     pmdarima: 2.0.4
statsforecast: None
      tsfresh: None
      tslearn: None
        torch: 2.7.0
   tensorflow: None

Hints from issue discussion:
@fkiraly, how does sktime evaluate the line `pipe = MinMaxScaler() * model`? I understand that the `* ` operator is overridden—could you explain why this is the case?

Commit message: replace scitype with is_scitype

Patch to review:
diff --git a/sktime/forecasting/compose/_pipeline.py b/sktime/forecasting/compose/_pipeline.py
index f9281683260..a3fd3df93eb 100644
--- a/sktime/forecasting/compose/_pipeline.py
+++ b/sktime/forecasting/compose/_pipeline.py
@@ -12,7 +12,7 @@
 from sktime.forecasting.base._base import BaseForecaster
 from sktime.forecasting.base._delegate import _DelegatedForecaster
 from sktime.forecasting.base._fh import ForecastingHorizon
-from sktime.registry import is_scitype, scitype
+from sktime.registry import is_scitype
 from sktime.utils.validation.series import check_series
 from sktime.utils.warnings import warn
 
@@ -93,7 +93,11 @@ def _check_steps(self, estimators, allow_postproc=False):
         COERCIBLE_SCITYPES = set(COERCIBLE_SCITYPES) - set(ALLOWED_SCITYPES)
         ACCEPTED_SCITYPES = ALLOWED_SCITYPES + list(COERCIBLE_SCITYPES)
 
-        est_scitypes = [scitype(x) for x in estimators]
+        est_scitypes = []
+        for x in estimators:
+            # Check if it's a forecaster 
+            if is_scitype(x, "forecaster"):
+                est_scitypes.append("forecaster")
 
         if not all([x in ACCEPTED_SCITYPES for x in est_scitypes]):
             raise TypeError(
diff --git a/sktime/registry/_scitype.py b/sktime/registry/_scitype.py
index 207676e8081..3451ee208bc 100644
--- a/sktime/registry/_scitype.py
+++ b/sktime/registry/_scitype.py
@@ -174,4 +174,4 @@ def handle_output_format(detected_scitype):
     if len(scitypes) == 1 and not coerce_to_list:
         return scitypes[0]
 
-    return scitypes
+    return scitypes
\ No newline at end of file


Code review comments:
diff_hunk: @@ -93,7 +93,11 @@ def _check_steps(self, estimators, allow_postproc=False):
         COERCIBLE_SCITYPES = set(COERCIBLE_SCITYPES) - set(ALLOWED_SCITYPES)
         ACCEPTED_SCITYPES = ALLOWED_SCITYPES + list(COERCIBLE_SCITYPES)
 
-        est_scitypes = [scitype(x) for x in estimators]
+        est_scitypes = []
+        for x in estimators:
+            # Check if it's a forecaster 
path: sktime/forecasting/compose/_pipeline.py
text: @user1:
I do not think this is a good fix - it will only result in `["forecaster"]` and ignore the types of the other estimators.

diff_hunk: @@ -174,4 +174,4 @@ def handle_output_format(detected_scitype):
     if len(scitypes) == 1 and not coerce_to_list:
         return scitypes[0]
 
-    return scitypes
+    return scitypes
path: sktime/registry/_scitype.py
text: @user1:
this change is unnecessary and will break linting