Cannot unify int64 and array(float64, 1d, C) for 'n.2'

I’m trying to make a simple Raytracer with Numba, but I have this error and can’t find the specific line that is causing it or how to solve it:

Traceback (most recent call last):
  File "C:\Users\Yoyo\projects\experiments\nrt.py", line 123, in <module>
    main()
  File "C:\Users\Yoyo\projects\experiments\nrt.py", line 115, in main
    render(arr, SX, SY, PROJ_DIST, CAM_POS, CAM_COORDS, RADIUS)
  File "C:\Users\Yoyo\projects\experiments\venv\lib\site-packages\numba\core\dispatcher.py", line 468, in _compile_for_args
    error_rewrite(e, 'typing')
  File "C:\Users\Yoyo\projects\experiments\venv\lib\site-packages\numba\core\dispatcher.py", line 409, in error_rewrite
    raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Cannot unify int64 and array(float64, 1d, C) for 'n.2', defined at C:\Users\Yoyo\projects\experiments\nrt.py (81)

File "nrt.py", line 81:
def render(arr, sx, sy, proj_dist, cam_pos, cam_coords, radius):
    <source elided>
            for n in range(AA_V_SAMPLES):
                for m in range(AA_H_SAMPLES):
                ^

During: typing of assignment at C:\Users\Yoyo\projects\experiments\nrt.py (81)

File "nrt.py", line 81:
def render(arr, sx, sy, proj_dist, cam_pos, cam_coords, radius):
    <source elided>
            for n in range(AA_V_SAMPLES):
                for m in range(AA_H_SAMPLES):
                ^

You can see my source code here: experiments/nrt.py at main · HenrYxZ/experiments · GitHub

Or here:

from math import sqrt
from numba import njit
import numpy as np
from PIL import Image


import utils


SCREEN_WIDTH = 280
SCREEN_HEIGHT = 210
COLOR_CHANNELS = 3
AA_H_SAMPLES = 1
AA_V_SAMPLES = 1
AA_SAMPLES = AA_V_SAMPLES * AA_H_SAMPLES

RADIUS = 0.5
SPHERE_POS = np.array((0.0, 0.0, 0.0))
SX = 2.0
SY = 1.5
PROJ_DIST = 2.0
CAM_POS = np.array((0.0, 0.0, -2.0))
CAM_COORDS = np.array((
    (1.0, 0.0, 0.0),    # n0
    (0.0, 1.0, 0.0),    # n1
    (0.0, 0.0, 1.0)     # n2
))
MAT_COL = np.array((128 / 255.0, 0.0, 128 / 255.0))
C0 = MAT_COL * 0.2     # Lowest possible material color
BG_COLOR = np.zeros(COLOR_CHANNELS)
LIGHT_DIR = utils.normalize(np.array([-0.3, 1.0, -0.5]))

rng = np.random.default_rng()


@njit
def project_pixel(sx, sy, n0, n1, h, w, pixel_coords, p00):
    """
    Get the projection point for a given screen pixel

    Args:
        sx:
        sy:
        n0:
        n1:
        h:
        w:
        pixel_coords (tuple[float]):
        p00 (ndarray):

    Returns:
        ndarray: The projection point in 3D coordinates
    """
    xp = (pixel_coords[0] / w) * sx
    yp = ((h - pixel_coords[1]) / h) * sy
    pp = p00 + n0 * xp + n1 * yp
    return pp


@njit
def intersect(pp, nr, pos, radius) -> float:
    diff = pp - pos
    b = np.dot(nr, diff)
    c = np.dot(diff, diff) - radius ** 2
    discriminant = b ** 2 - c
    if b > 0 or discriminant < 0:
        return -1.0
    t = -b - sqrt(discriminant)
    return t


@njit
def render(arr, sx, sy, proj_dist, cam_pos, cam_coords, radius):
    h, w, _ = arr.shape
    n0, n1, n2 = cam_coords
    p00 = cam_pos + (n0 * (-sx / 2) + n1 * (-sy / 2) + n2 * proj_dist)
    for j in range(h):
        for i in range(w):
            color = np.zeros(COLOR_CHANNELS)
            for n in range(AA_V_SAMPLES):
                for m in range(AA_H_SAMPLES):
                    offsets = rng.random(2)
                    pixel_coords = (
                        i + (m + offsets[0]) / AA_H_SAMPLES,
                        j + (n + offsets[1]) / AA_V_SAMPLES
                    )
                    # Project pixel to point in 3D coordinates
                    pp = project_pixel(
                        sx, sy, n0, n1, h, w, pixel_coords, p00
                    )
                    # Shoot ray
                    nr = utils.normalize(pp - cam_pos)
                    t = intersect(cam_pos, nr, SPHERE_POS, radius)
                    # print(t)
                    if t > 0:
                        p_hit = cam_pos + nr * t
                        n = utils.normalize(p_hit - SPHERE_POS)
                        color += np.maximum(np.dot(n, LIGHT_DIR) * MAT_COL, C0)
                    else:
                        color += BG_COLOR
            color = color / AA_SAMPLES
            final_color = np.empty_like(color)
            np.round(color * 255, 0, final_color)
            arr[j, i, 0] = int(final_color[0])
            arr[j, i, 1] = int(final_color[1])
            arr[j, i, 2] = int(final_color[2])


def main():
    arr = np.zeros(
        (SCREEN_HEIGHT, SCREEN_WIDTH, COLOR_CHANNELS), dtype=np.uint8
    )
    timer = utils.Timer()
    with timer:
        render(arr, SX, SY, PROJ_DIST, CAM_POS, CAM_COORDS, RADIUS)
    img = Image.fromarray(arr)
    img_path = "docs/nrt_output.png"
    img.save(img_path)
    print(f"Finished rendering and saved image in {img_path}")


if __name__ == '__main__':
    main()

I guess it could be the last line of the render function where I try to cast a float 3D array to a 3D uint8 array (RGB)

1 Like

(It works fine in regular Python)

does the type of any of the local variables change during the course of running this program?

In particular, is ‘n’ always assigned the same type?

1 Like

Yes as @nelson2005 already suggests, it’s probably the reuse of n at line 97:
n = utils.normalize(p_hit - SPHERE_POS)

1 Like

Thank you guys, that was the problem

1 Like