Repository: sktime/sktime
Pull request #8399 (2025-06-17 02:51:21)

[MNT] [Dependabot](deps): Update pytorch-forecasting requirement from <1.4.0,>=1.0.0 to >=1.0.0,<1.5.0
Updates the requirements on [pytorch-forecasting](https://github.com/sktime/pytorch-forecasting) to permit the latest version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/sktime/pytorch-forecasting/releases">pytorch-forecasting's releases</a>.</em></p>
<blockquote>
<h2>v1.4.0</h2>
<h2>What's Changed</h2>
<p>Feature and maintenance update.</p>
<p><a href="https://github.com/sktime/pytorch-forecasting/blob/main/CHANGELOG.md">See full changelog</a></p>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/gbilleyPeco"><code>@​gbilleyPeco</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1750">sktime/pytorch-forecasting#1750</a></li>
<li><a href="https://github.com/pietsjoh"><code>@​pietsjoh</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1399">sktime/pytorch-forecasting#1399</a></li>
<li><a href="https://github.com/MartinoMensio"><code>@​MartinoMensio</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1579">sktime/pytorch-forecasting#1579</a></li>
<li><a href="https://github.com/phoeenniixx"><code>@​phoeenniixx</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1811">sktime/pytorch-forecasting#1811</a></li>
<li><a href="https://github.com/cngmid"><code>@​cngmid</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1827">sktime/pytorch-forecasting#1827</a></li>
<li><a href="https://github.com/Marcrb2"><code>@​Marcrb2</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1518">sktime/pytorch-forecasting#1518</a></li>
<li><a href="https://github.com/jobs-git"><code>@​jobs-git</code></a> made their first contribution in <a href="https://redirect.github.com/sktime/pytorch-forecasting/pull/1864">sktime/pytorch-forecasting#1864</a></li>
</ul>
<h2>All Contributors</h2>
<p><a href="https://github.com/agobbifbk"><code>@​agobbifbk</code></a>,
<a href="https://github.com/Borda"><code>@​Borda</code></a>,
<a href="https://github.com/cngmid"><code>@​cngmid</code></a>,
<a href="https://github.com/fkiraly"><code>@​fkiraly</code></a>,
<a href="https://github.com/fnhirwa"><code>@​fnhirwa</code></a>,
<a href="https://github.com/gbilleyPeco"><code>@​gbilleyPeco</code></a>,
<a href="https://github.com/jobs-git"><code>@​jobs-git</code></a>,
<a href="https://github.com/Marcrb2"><code>@​Marcrb2</code></a>,
<a href="https://github.com/MartinoMensio"><code>@​MartinoMensio</code></a>,
<a href="https://github.com/phoeenniixx"><code>@​phoeenniixx</code></a>,
<a href="https://github.com/pietsjoh"><code>@​pietsjoh</code></a>,
<a href="https://github.com/PranavBhatP"><code>@​PranavBhatP</code></a></p>
<p><strong>Full Changelog</strong>: <a href="https://github.com/sktime/pytorch-forecasting/compare/v1.3.0...v1.4.0">https://github.com/sktime/pytorch-forecasting/compare/v1.3.0...v1.4.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a href="https://github.com/sktime/pytorch-forecasting/compare/v1.0.0...v1.4.0">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/pyproject.toml b/pyproject.toml
index 3e306350b..9c3838dcb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -294,7 +294,7 @@ dl = [
   "torch; (sys_platform != 'darwin' or python_version != '3.13')",
   'transformers[torch]<4.41.0; python_version < "3.12"',
   'pykan>=0.2.1,<0.2.9; python_version > "3.9.7"',
-  "pytorch-forecasting>=1.0.0,<1.4.0; (sys_platform != 'darwin' or python_version != '3.13')",
+  "pytorch-forecasting>=1.0.0,<1.5.0; (sys_platform != 'darwin' or python_version != '3.13')",
   'lightning>=2.0; python_version < "3.12"',
   'gluonts>=0.14.3; python_version < "3.12"',
   'einops>0.7.0; python_version < "3.12"',
diff --git a/sktime/performance_metrics/forecasting/probabilistic/_classes.py b/sktime/performance_metrics/forecasting/probabilistic/_classes.py
index 104391574..31dd4aa53 100644
--- a/sktime/performance_metrics/forecasting/probabilistic/_classes.py
+++ b/sktime/performance_metrics/forecasting/probabilistic/_classes.py
@@ -679,7 +679,9 @@ class EmpiricalCoverage(_BaseProbaForecastingErrorMetric):
 
         y_true_np = np.tile(y_true_np, no_scores)
 
-        truth_array = (y_true_np > lower).astype(int) * (y_true_np < upper).astype(int)
+        truth_array = (y_true_np >= lower).astype(int) * (y_true_np <= upper).astype(
+            int
+        )
 
         out_df = pd.DataFrame(
             truth_array, columns=pd.MultiIndex.from_product([vars, scores])


Test patch:
diff --git a/sktime/performance_metrics/forecasting/probabilistic/tests/test_probabilistic_metrics.py b/sktime/performance_metrics/forecasting/probabilistic/tests/test_probabilistic_metrics.py
index 9c1d389ea..8e5994bfe 100644
--- a/sktime/performance_metrics/forecasting/probabilistic/tests/test_probabilistic_metrics.py
+++ b/sktime/performance_metrics/forecasting/probabilistic/tests/test_probabilistic_metrics.py
@@ -316,3 +316,39 @@ def test_multioutput_weighted(metric, score_average):
         assert isinstance(eval_loss_by_index, pd.DataFrame)
         assert eval_loss_by_index.shape == (expected_timepoints, no_expected_scores)
         assert eval_loss_by_index.columns.to_list() == expected_score_ix
+
+
+@pytest.mark.skipif(
+    not run_test_module_changed(["sktime.performance_metrics"]),
+    reason="Run if performance_metrics module has changed.",
+)
+@pytest.mark.parametrize(
+    "y_true_vals, lower_vals, upper_vals",
+    [
+        ([2.0], [2.0], [3.0]),  # True value equals lower boundary
+        ([3.0], [2.0], [3.0]),  # True value equals upper boundary
+        ([2.0, 3.0], [2.0, 2.0], [3.0, 3.0]),  # Both boundaries
+    ],
+)
+def test_empirical_coverage_boundary_equality(y_true_vals, lower_vals, upper_vals):
+    """Test EmpiricalCoverage when true values equal interval boundaries.
+
+    This test ensures that when a true value equals the lower or upper
+    boundary of a prediction interval, it is correctly counted as covered
+    (i.e., uses non-strict inequalities).
+    """
+    y_true = pd.Series(y_true_vals)
+    y_pred = pd.DataFrame(
+        {
+            ("var1", 0.8, "lower"): lower_vals,
+            ("var1", 0.8, "upper"): upper_vals,
+        }
+    )
+
+    metric = EmpiricalCoverage()
+    coverage = metric(y_true, y_pred)
+
+    assert coverage == 1.0, (
+        f"Expected coverage 1.0 but got {coverage}. "
+        "True values on interval boundaries should be counted as covered."
+    )