Skip to content

Conversation

fancidev
Copy link
Contributor

Reference issue

Closes gh-20994.

What does this implement/fix?

If u or v supplied to cosine() or correlation() is complex, issue a DeprecationWarning.

Additional information

Before this PR, these functions do not reject or warn about complex inputs explicitly. However, ValueError, ComplexWarning, or TypeError may occur down the code path depending on the value of the inputs. And even if no error occurs, the returned answer is usually not meaningful. In addition, the cdist/pdist counterparts of these distance metrics only support real input.

To ensure consistency and well-defined behavior, this PR emits a DeprecationWarning when either u or v is complex. (w is not checked because a TypeError is always raised if w is complex.) The plan is to reject complex input in a future release.

See the referenced issue as well as gh-21001 for more details.

@lucascolley lucascolley changed the title Deprecate complex input to spatial.distance.cosine() and correlation() DEP: spatial: deprecate complex input to cosine and correlation Jun 30, 2024
@lucascolley lucascolley added the deprecated Items related to behavior that has been deprecated label Jun 30, 2024
Copy link
Member

@lucascolley lucascolley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @fancidev, can you make a corresponding post on Discourse?

Copy link
Member

@j-bowhay j-bowhay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @fancidev some suggestions

@fancidev
Copy link
Contributor Author

thanks @fancidev, can you make a corresponding post on Discourse?

Done.

Copy link
Member

@j-bowhay j-bowhay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry should have read the issue first, complex inputs currently raise an error so do we need to deprecate? I think we could probably go straight to raising a nicer error. wdyt @fancidev @lucascolley

@fancidev
Copy link
Contributor Author

Sorry should have read the issue first, complex inputs currently raise an error so do we need to deprecate? I think we could probably go straight to raising a nicer error. wdyt @fancidev @lucascolley

Right now the functions raise a warning or an error depending on the value of the input. There could also be some well-crafted complex input where the functions do not raise either a warning or an error. But the bottom line is, whether a warning or not, the returned value is “arbitrary” in the sense that it’s just whatever the code happens to produce but nothing meaningful.

Therefore, In terms of giving a meaningful result, I believe the functions never actually “worked” for complex input. So I’d also be very happy to reject complex input straight away without going through the deprecation phase.

@j-bowhay
Copy link
Member

Sorry should have read the issue first, complex inputs currently raise an error so do we need to deprecate? I think we could probably go straight to raising a nicer error. wdyt @fancidev @lucascolley

Right now the functions raise a warning or an error depending on the value of the input. There could also be some well-crafted complex input where the functions do not raise either a warning or an error. But the bottom line is, whether a warning or not, the returned value is “arbitrary” in the sense that it’s just whatever the code happens to produce but nothing meaningful.

Therefore, In terms of giving a meaningful result, I believe the functions never actually “worked” for complex input. So I’d also be very happy to reject complex input straight away without going through the deprecation phase.

Thanks for the summary. I would only be in favour of skipping deprecation if, in its current state, the function raises a meaningless error for all complex inputs. I gather from your reply this is not the case so I think we should proceed with deprecation.

@lucascolley lucascolley added the needs-release-note a maintainer should add a release note written by a reviewer/author to the wiki label Jul 1, 2024
Copy link
Member

@j-bowhay j-bowhay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry one more thing, can you add a node under the relevant parameter docstrings like this

scipy/scipy/special/_basic.py

Lines 2696 to 2698 in 8934389

.. deprecated:: 1.14.0
``exact=True`` is deprecated for non-integer `N` and `k` and will raise an
error in SciPy 1.16.0

@tylerjereddy tylerjereddy added this to the 1.15.0 milestone Jul 3, 2024
Copy link
Contributor

@tylerjereddy tylerjereddy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely looks like a step in the right direction for spatial, based on the associated discussions.

Not sure how consistently we've been adding tests for deprecations, but this seems to fail before/pass after, though is a bit ugly due to all the errors and suppressions involved:

--- a/scipy/spatial/tests/test_distance.py
+++ b/scipy/spatial/tests/test_distance.py
@@ -1639,6 +1639,15 @@ class TestSomeDistanceFunctions:
             dist = wcorrelation(x, y)
             assert_almost_equal(dist, 1.0 - np.dot(xm, ym) / (norm(xm) * norm(ym)))
 
+
+    @pytest.mark.filterwarnings('ignore:Casting complex')
+    @pytest.mark.parametrize("func", [correlation, cosine])
+    def test_corr_dep_complex(self, func):
+        arr = np.array([1+2j, 3+4j])
+        with pytest.raises(ValueError):
+            with pytest.deprecated_call(match="Complex `u` and `v` are deprecated"):
+                func(arr, arr)
+

Perhaps more trouble than it is worth, not sure, especially if the suppressions are different on NumPy 1.x vs. 2.x (didn't check).

@tylerjereddy
Copy link
Contributor

@fancidev
Copy link
Contributor Author

fancidev commented Jul 4, 2024

@j-bowhay I added the deprecation message to the doc.

@tylerjereddy

Not sure how consistently we've been adding tests for deprecations, but this seems to fail before/pass after, though is a bit ugly due to all the errors and suppressions involved.

I'm happy to add (or not to add) the tests. Since complex input is never a supported usage nor generates meaningful result, I don't find an urge to test deprecation. On the other hand, there appears no harm to add one, if only for coverage. Please let me know!

@j-bowhay
Copy link
Member

j-bowhay commented Jul 5, 2024

@j-bowhay I added the deprecation message to the doc.

@tylerjereddy

Not sure how consistently we've been adding tests for deprecations, but this seems to fail before/pass after, though is a bit ugly due to all the errors and suppressions involved.

I'm happy to add (or not to add) the tests. Since complex input is never a supported usage nor generates meaningful result, I don't find an urge to test deprecation. On the other hand, there appears no harm to add one, if only for coverage. Please let me know!

I would prefer the test to be added too

@fancidev
Copy link
Contributor Author

fancidev commented Jul 8, 2024

Following @tylerjereddy 's sample code, I added a test case for the DeprecationWarning for complex input.

I used real-valued complex number in the test case, for two reasons. First, it prevents raising a ValueError, making the test slightly simpler. Second, the match="..." part in pytest.deprecated_call(match="...") seems to be ignored if inside a raises context block -- putting anything for match still pass the test. But the keyword argument appears to work when outside of a raises block.

message = (
"Complex `u` and `v` are deprecated and will raise an error in "
"SciPy 1.17.0.")
warnings.warn(message, DeprecationWarning, stacklevel=1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
warnings.warn(message, DeprecationWarning, stacklevel=1)
warnings.warn(message, DeprecationWarning, stacklevel=2)

Copy link
Member

@j-bowhay j-bowhay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @fancidev just the stacklevel that needs to be corrected then this is good to go

@fancidev
Copy link
Contributor Author

Thanks @fancidev just the stacklevel that needs to be corrected then this is good to go

Done!

@j-bowhay j-bowhay merged commit a9656b5 into scipy:main Jul 10, 2024
@j-bowhay j-bowhay mentioned this pull request Jul 10, 2024
32 tasks
@fancidev fancidev deleted the cosine-distance branch July 10, 2024 22:41
ev-br pushed a commit to ev-br/scipy that referenced this pull request Jul 14, 2024
…cipy#21085)

* spatial.distance: deprecate complex input to cosine() and correlation().

* Minor refactoring.

* Add deprecation message to cosine and correlation doc.

* Add test case for DeprecationWarning.

* Change stacklevel to 2.
@lucascolley lucascolley removed the needs-release-note a maintainer should add a release note written by a reviewer/author to the wiki label Jun 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
deprecated Items related to behavior that has been deprecated scipy.spatial
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: spatial.distance.cosine with complex arguments raises ValueError
4 participants