Hello everyone,
I’m getting a type error from a generator function which i do not understand.
Here is a minimal example:
import numba as nb
@nb.njit("int64()")
def generator_test():
for i in range(5):
yield i
print(list(generator_test()))
Which will give you:
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No conversion from none to int64 for '$54return_value.1', defined at None
File "questions.py", line 9:
def generator_test():
for i in range(5):
^
The error does not occur if i remove the type signature from the decorator. Another workaround which I am currently using is ending the function with return -1 after the for-loop:
import numba as nb
@nb.njit("int64()")
def generator_test():
for i in range(5):
yield i
return -1
print(list(generator_test()))
My original function should output only unsigned integers and end when the generator is exausted. My goal is using an 32-bit unsigned integer type in the type signature, to reduce memory usage.
Please tell me if you need to see the full traceback.
I look forward to any explanations!
Your function returns None so Numba is complaining that you’ve specified the return type as an integer. Yielding and returning are not the same, and we don’t have a way to specify what type is yield in the signature, as far as I am aware.
Try using "none()" for the signature instead.
However, on a broader point - are you only concerned about memory usage as the reason for the signature? I think it will make negligible difference, and you are probably better just omitting the signature and letting Numba infer it.
Also you can let numba infer it and then print nopython_signatures to see what signature was inferred
Thank you very much for your replies!
@gmarkall since in pure Python a generator function like this has no return value, but is terminated with a StopIteration exception, I did not expect a return value here either.
I want to use this function in another function in which I fill a large array based on two other large arrays. I have rewritten the construction of the array into a generator function, because I get a MemoryError on my laptop when I first create the array from the two large arrays, as I need more memory when caculating the resulting array than I have available. Calculating the values after another worked out but there is still a lot of memory in use.
I plan to publish the package on GitHub soon! I will then share the release here so that you can better understand what I have done.
Thank you for your reply!
If I do that, i get an empty list
import numba as nb
@nb.njit()
def generator_test():
for i in range(5):
yield i
print(generator_test.nopython_signatures)
prints []
Thanks again @gmarkall & @nelson2005!
Your answers pointed me in the right direction. Raising StopIteration after the for-loop, allowed me to use the type-Signature of an unsigned 32 bit integer!
import numba as nb
@nb.njit("uint32()")
def generator_test():
for i in range(5):
yield i
raise StopIteration
print(generator_test.nopython_signatures)
print(list(generator_test()))
But it returns an int64 type-signature:
[() -> int64 generator(func=<function generator_test at 0x000001DC68895580>, args=(), has_finalizer=True)]
[0, 1, 2, 3, 4]
Edit:
Ok, but this is due to using range instead of numpy’s arange!
Here is my final solution:
import numba as nb
import numpy as np
@nb.njit("uint32()")
def generator_test():
for i in np.arange(5, dtype=nb.uint32):
yield i
raise StopIteration
print(generator_test.nopython_signatures)
print(list(generator_test()))
Will print:
[() -> uint32 generator(func=<function generator_test at 0x000002777FF554E0>, args=(), has_finalizer=True)]
[0, 1, 2, 3, 4]
Right, there’s no deduced signature until you call the function.
Of course, thank you!
Now I’m even more confused! If I go to the original version without type signature and without StopIteration and look at the nopython_signatures after calling the function, I get exactly the same signature as with StopIteration Exception and defined type signature:
import numba as nb
@nb.njit()
def generator_test_old():
for i in range(5):
yield i
generator_test_old()
print(generator_test_old.nopython_signatures)
prints:
[() -> int64 generator(func=<function generator_test_old at 0x00000206DF2D54E0>, args=(), has_finalizer=True)]
But if I define “int64()” as type signature without StopIteration, I get the type error from my original question.
Another question I have is why nopython_signature shows a deduced signature for a function without type signature only after it was called but if I add the type signature “uint32()” and keep range I receive an “int64” type signature before calling it. So numba knows in this case, that range will output “int64” before the function was called?
The type signatures you see are those that have been compiled. If you provide a signature then the function will be compiled for the given signature immediately. If you don’t, then variants for each set of argument types are compiled at the time of each call with new argument types - see
Eager Compilation.
Regarding your original example without StopIteration, casting the yielded value to int32 also gives you the solution you were looking for (as I understand it) without a type signature:
import numba as nb
@nb.njit
def generator_test_old():
for i in range(5):
yield nb.int32(i)
generator_test_old()
print(generator_test_old.nopython_signatures)
gives:
$ python repro.py
[() -> int32 generator(func=<function generator_test_old at 0x73cd967984a0>, args=(), has_finalizer=True)]