Skip to content

Navigation Menu

Sign in
Appearance settings
Sign up
Appearance settings

BUG: fix uint input for triu_indices and deprecate non-int - #30869

#30869
Merged
seberg merged 5 commits into
numpy:mainnumpy/numpy:mainfrom
antareepsarkar:triu_indicesantareepsarkar/numpy:triu_indicesCopy head branch name to clipboard
Mar 4, 2026
Merged

BUG: fix uint input for triu_indices and deprecate non-int#30869
seberg merged 5 commits into
numpy:mainnumpy/numpy:mainfrom
antareepsarkar:triu_indicesantareepsarkar/numpy:triu_indicesCopy head branch name to clipboard

Conversation

@antareepsarkar

@antareepsarkar antareepsarkar commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

resolves #29488
Does not raise an error when any or all of of n, m and k are unsigned integers.

Comment thread numpy/lib/_twodim_base_impl.py Outdated
Comment thread numpy/lib/_twodim_base_impl.py Outdated
Comment thread numpy/lib/_twodim_base_impl.py Outdated

m = convert_if_unsigned(m)
n = convert_if_unsigned(n)
k = convert_if_unsigned(k)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally we don't like to force users who aren't affected by issues to pay the cost of fixing an issue. It seems better to fix this another way...

Comment thread numpy/lib/_twodim_base_impl.py Outdated
def convert_if_unsigned(val):
if (hasattr(val, "dtype") and issubdtype(val.dtype, unsignedinteger)
and ndim(val) == 0):
return operator.index(val)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

just a general note: this function should be defined at module level. You only do stuff like this if you're defining a closure that captures values in the enclusing scope. This function doesn't do that.

@ngoldbaum ngoldbaum Feb 24, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I guess this is basically @seberg's solution from #29488 (comment) but it's not quite as clean as he envisioned.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I get it and agree that the code should have been cleaner. Please tell me what approach can I take for this solution.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sorry, not sure offhand

@antareepsarkar antareepsarkar Feb 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually, I did not see any way of solving this without checking for the dtype (without changing the current behavior).
And, in the first commit I did not define any function but just checked for n's and m's dtype. But, then I thought that if k is an unsigned integer, it will again raise an error.
So, I defined the function to reduce the lines of code.
Now, if I remove the function and check the dtype of n, m and k because these kinds of conditional statements are used in some places, will it be okay?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe see the other PR, you could even pick up there. It was already very good, there was just one question that we either have to decide to break:

np.tri(3.5)

or add a DeprecationWarning. I would probably not add a helper, except maybe to give the warning itself (because we need it in a few places here). All it needs is a try/except.

It would be awesome if you could take the branch from the other closed PR and add commits to implement the DeprecationWarning.

@seberg

seberg commented Feb 26, 2026

Copy link
Copy Markdown
Member

@antareepsarkar can you please add a test (ideally in test_deprecations.py) for the now deprecated behavior.
You'll find that the current code doesn't do the same thing as the old one. Also, float isn't special at all here, you should use int() and if that doesn't fail give a generic warning (I forgot what we used to use in years past for this type of things, but something along "cannot convert {type(val).name} safely to an integer, this will raise an error in future versions (Deprecated NumPy 2.4)").

It isn't actually about floats as such, float is just the most likely kind of object coming in hitting this.

Also add a comment # Deprecated NumPy 2.4, 2026-02-26 or similar, that is just to make it easier to keep track of deprecations.

@antareepsarkar

antareepsarkar commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

@antareepsarkar can you please add a test (ideally in test_deprecations.py) for the now deprecated behavior. You'll find that the current code doesn't do the same thing as the old one. Also, float isn't special at all here, you should use int() and if that doesn't fail give a generic warning (I forgot what we used to use in years past for this type of things, but something along "cannot convert {type(val).name} safely to an integer, this will raise an error in future versions (Deprecated NumPy 2.4)").

It isn't actually about floats as such, float is just the most likely kind of object coming in hitting this.

Also add a comment # Deprecated NumPy 2.4, 2026-02-26 or similar, that is just to make it easier to keep track of deprecations.

@seberg
I wanted to ask about the stacklevel. Since, we are deprecating, we need to warn for all functions, triu_indices, tril_indices, etc. All these functions call tri. Again, tri can be called by itself.
Probably, patching tri is the best thing. But, I'm getting confused about what should the stacklevel be?

@seberg

seberg commented Feb 26, 2026

Copy link
Copy Markdown
Member

There is actually a new mechanism that I think we can use now: skip_file_prefixes=.

Otherwise, it would be 2 typically, then +1 for every layer of helper function (the smallest one we can find), so in this case 3. But, the skip function is nicer (and the only thing that I might worry about is if I thought it was a warning that could happen in very hot paths, this isn't).

@antareepsarkar

Copy link
Copy Markdown
Contributor Author

There is actually a new mechanism that I think we can use now: skip_file_prefixes=.

Otherwise, it would be 2 typically, then +1 for every layer of helper function (the smallest one we can find), so in this case 3. But, the skip function is nicer (and the only thing that I might worry about is if I thought it was a warning that could happen in very hot paths, this isn't).

Yes, but warnings at present require stacklevel to be present. So, I can change the behaviour to allow skip_file_prefixes(in this PR?) or just let stacklevel be 3. Just asking to you so that I can be sure what to do before starting.

@seberg

seberg commented Feb 26, 2026

Copy link
Copy Markdown
Member

Is this from the ruff check or another test? I think the ruff check should not complain when skip_file_prefixes is used, but we used to have a very old custom check, which I suspect we can just retire now.

So yeah, I think we can go with skip_file_prefixes if you like, at the risk that you (or I, or someone else) might have to make one more tweak here or in another PR before tests pass.

@antareepsarkar

Copy link
Copy Markdown
Contributor Author

@seberg
I did the changes. I have added skip_file_prefixes(should that be done in another PR?) in test_warnings.py.
Please review and tell if anything should be changed.

@seberg seberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, great you tracked down how to keep CI passing! I would like to go back to the old operator.index pattern with a try/except though.

Comment thread numpy/lib/_twodim_base_impl.py Outdated
if dtype_not_int and not isinstance(n, int):
warnings.warn(
(f"Cannot convert {type(n).__name__} safely to an integer."
"This will raise an error in future versions(Deprecated NumPy 2.4)"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
"This will raise an error in future versions(Deprecated NumPy 2.4)"),
"This will raise an error in future versions (Deprecated NumPy 2.5)"),

Comment thread numpy/lib/_twodim_base_impl.py Outdated
if hasattr(n, "dtype") and n.dtype.kind in "iu":
dtype_not_int = False

if dtype_not_int and not isinstance(n, int):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit, add a # Deprecated NumPy 2.5, 2026-...

Comment thread numpy/lib/_twodim_base_impl.py Outdated
dtype_not_int = True

if hasattr(n, "dtype") and n.dtype.kind in "iu":
dtype_not_int = False

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I suspect you have a reason, but why did you get away from operator.index?

What I wanted to nudge towards (sorry, sometimes I try to be a bit fuzzy), was to do:

try:
    n = operator.index(n)
except TypeError:
    n = int(n)
    # successfully converted, but not via `operator.index()`
    warnings.warn(...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought you wanted to use int() rather than operator.index #30869 (comment).
I will use operator.index now. The thing is, if we do int(n), it takes the floor of floats. But, previously the ceil was being taken i.e. now np.tri(3.14) will be np.tri(3) but before the change it was np.tri(4). So, it will be kind of a breaking change.
I just want to be sure from you what needs to be done in this case.

@antareepsarkar antareepsarkar Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Actually, I think we can just warn in the except block and not do anything as we just want to warn as of now. Anyways, it's upto you to decide what should be done.

@seberg seberg Feb 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah! I had not realized that the arange led to the ceil being taken.

Sorry, probably the earlier iteration was actually the closest and I didn't think right. If operator.index() fails, we should just keep doing exactly what we were doing before.
(And I think that was right as nothing at all, I guess... sorry for slipping into thinking that wasn't right)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks.
Now, I have kind of a clear picture of what to do.

@antareepsarkar

antareepsarkar commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

@seberg
Probably, the last commit has some problems(for example, if I put a string as an input, it shows both the warning and the error).

So, as you said, I will make things as before.

Please tell me whether this needs a release note or not. And, does both the fix for unsigned integers and deprecation need a release note.

Comment thread numpy/lib/_twodim_base_impl.py Outdated

"""

if isinstance(k, unsignedinteger):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This unsignedinteger check is new, it looks a bit like a left-over?

As to your note about giving warning + error. Great observation! I could live with it, but you are right it isn't great in the, maybe unlikely, case anyone actually relies on the TypeError.
This would not have been a problem if int() wasn't a regression due to ceil.

I suppose we could change things to do something like:

try:
    n = operator.index(n)
    k = operator.index(k)
except TypeError:
    deprecation_warning = True
else:
    deprecation_warning = False

<old code at least until it converts>

if deprecation_warning:
    _give_int_warning()

Although if we want to print the type, we would have to remember which failed and the above doesn't do it.

Maybe you want to try that and decide for yourself if it it doesn't get too annoying? These functions themself fairly simply, so I think it would be fine.

(We have been iterating a quite quickly, I probably should slow down a bit :))

@antareepsarkar

Copy link
Copy Markdown
Contributor Author

@seberg
Sorry, just forgot to put the comment for deprecated.
I think that using list will not affect speed. There are only 3 iterations at max.

Please tell me whether things need to be changed.

@seberg seberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, two comments, but honestly this is fine and if you prefer I might just push something (it's my problem if I have the urge to change it to something I feel is slightly prettier :)).


# To make sure unsigned integers, etc. get converted to int
if isinstance(k, integer):
k = operator.index(k)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you just do the try/except dance here, then that seems just as well? The worst case would be that the deprecation warning is given twice.

(that will also mean we'll start to reject e.g. NumPy booleans just like in tri itself. -- for better or worse)

Comment thread numpy/lib/_twodim_base_impl.py Outdated
m = m.astype(dtype, copy=False)

# warn for all deprecated
for i in w:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, FWIW, it seems fine to me to just the single first warning. Which then allows you to write it as:

warning_for_type = False
try: ...
except:
    warning_for_type = warning_for_type or type(N)

@antareepsarkar

Copy link
Copy Markdown
Contributor Author

So, I just did the changes you told. I am just trying to avoid the double warning for np.triu_indices.

@antareepsarkar
antareepsarkar requested a review from seberg March 2, 2026 13:50
@antareepsarkar

Copy link
Copy Markdown
Contributor Author

@seberg
Are the changes okay?

@seberg seberg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah, thanks @antareepsarkar. let's go with this.

@seberg seberg changed the title BUG: fix unsigned integer input for triu_indices BUG: fix unsigned integer input for triu_indices and deprecate non-integer use Mar 4, 2026
@seberg seberg changed the title BUG: fix unsigned integer input for triu_indices and deprecate non-integer use BUG: fix unsigned int input for triu_indices and deprecate non-ints Mar 4, 2026
@seberg seberg changed the title BUG: fix unsigned int input for triu_indices and deprecate non-ints BUG: fix uint input for triu_indices and deprecate non-int Mar 4, 2026
@seberg
seberg merged commit fcbb4c0 into numpy:main Mar 4, 2026
79 checks passed
@antareepsarkar
antareepsarkar deleted the triu_indices branch March 4, 2026 10:59
sabasiddique1 pushed a commit to sabasiddique1/numpy that referenced this pull request Mar 4, 2026
resolves numpy#29488

Does not raise an error when any or all of of n, m and k are unsigned integers by converting them
to integers.
If inputs do not convert via `operator.index()` give a DeprecationWarning.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: np.triu_indices raises OverflowError with np.uint64 input while np.tril_indices does not

4 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.