Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to disable Wrap-around for Numpy Number Types

Tags:

python

numpy

I would like for example uint types to hit 0 rather than wrap-around on subtraction - is it possible to alter this behaviour from python without writing a new c-type? (For example for subtracting two uint8 numpy arrays) ?

like image 535
user3467349 Avatar asked Mar 17 '23 16:03

user3467349


2 Answers

There is no integer overflow/underflow detection in Numpy, but you could test for this yourself either before or after the calculation. m0 does this after the calculation, as shown below.

import numpy as np
import timeit

a = np.arange(0, 251, 50, dtype=np.uint8)
b = np.array(a[::-1])

def m0(a, b):
    x = (a-b)
    x[b>a] = 0

def m1(a, b):
    c = (a.astype(np.int16) - b).clip(0, 255).astype(np.uint8)

m0(a, b)
m1(a, b)

a = np.array(a.repeat(1000))  # repeat and copy (copy since I don't remember whether repeat is an indexing trick)
b = np.array(b.repeat(1000))

N = 1000000
print timeit.timeit("m0(a, b)", setup="from __main__ import m0, m1, a, b", number=N)
# 1.7734951973
print timeit.timeit("m1(a, b)", setup="from __main__ import m0, m1, a, b", number=N)
# 3.6973798275

I also compare the times to @Qlaus's approach, which is also good, and they are comparable (you shouldn't take the 50% speed gain here very seriously, but instead test them both for your specific application). On the other hand, the subtraction itself takes about 0.6 of the times above, so with this removed, the m0 solution is about 3x faster. But on the other hand, usually if you do one operation that will underflow or overflow you will be doing several, and for those cases, using int16 wil likely be fastest. In the end, testing for your actual case will be the best approach to optimizing.

like image 53
tom10 Avatar answered Mar 20 '23 05:03

tom10


Numpy does not do overflow handling on array types. See this discussion or this related question.

If you do not care about speed so much you could save your results in an int16 first and then clip it and write it back to an uint8 array.

import numpy as np
a = np.arange(5, dtype=np.uint8)
b = np.ones(5, dtype=np.uint8) * 2
print(a-b)  # [254 255   0   1   2]

c = (a.astype(np.int16) - b).clip(0, 255).astype(np.uint8)
print(c)  # [0, 0, 0, 1, 2]
like image 35
Qlaus Avatar answered Mar 20 '23 06:03

Qlaus