Skip to content

Conversation

@mtsokol
Copy link
Member

@mtsokol mtsokol commented Sep 24, 2025

This PR introduces dense level logic, for interpreters and Numba backend codegen.

@mtsokol mtsokol force-pushed the ms/dense-level branch 2 times, most recently from e02cd84 to 3551c2a Compare September 27, 2025 14:42
@mtsokol mtsokol self-assigned this Sep 27, 2025
@mtsokol mtsokol added the enhancement New feature or request label Sep 27, 2025
@mtsokol mtsokol changed the title [WIP] Dense Level Dense Level Sep 27, 2025
@mtsokol
Copy link
Member Author

mtsokol commented Sep 27, 2025

Hi @willow-ahrens,
I managed to lower notation to assembly with FiberTensor and Dense levels. Here are some questions:

  • How can I access strides of a Fiber tensor? Is it even possible? I think it's part of a dense level only but in our assembly strides are associated with the Tensor object.

Matmul lowered with tensor(dense(dense(element))) inputs:

<Outdated>

@mtsokol
Copy link
Member Author

mtsokol commented Oct 3, 2025

Hi @willow-ahrens,
Would you have spare time to give it a first look?

matmul with 2-dim dense format looks now similar to Julia's version:

def matmul(C: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))), A: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))), B: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0))))) -> FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))):
    m: ExtentFType(start=int64, end=int64) = dimension(A, 0)
    n: ExtentFType(start=int64, end=int64) = dimension(B, 1)
    p: ExtentFType(start=int64, end=int64) = dimension(A, 1)
    A_: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))) = A
    A__buf: np_buf_t(float64) = A_.lvl.lvl.lvl.val
    A__buf_slot: np_buf_t(float64) = unpack(A__buf)
    B_: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))) = B
    B__buf: np_buf_t(float64) = B_.lvl.lvl.lvl.val
    B__buf_slot: np_buf_t(float64) = unpack(B__buf)
    C_: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))) = C
    C__buf: np_buf_t(float64) = C_.lvl.lvl.lvl.val
    C__buf_slot: np_buf_t(float64) = unpack(C__buf)
    for i in range(0, length(slot(C__buf_slot, np_buf_t(float64)))):
        store(slot(C__buf_slot, np_buf_t(float64)), i, 0.0)
    for i in range(m.start, m.end):
        i__pos: int64 = add(0, mul(A_.lvl.stride, i))
        i__pos_2: int64 = add(0, mul(C_.lvl.stride, i))
        for k in range(p.start, p.end):
            k__pos: int64 = add(i__pos, mul(A_.lvl.lvl.stride, k))
            k__pos_2: int64 = add(0, mul(B_.lvl.stride, k))
            for j in range(n.start, n.end):
                j__pos: int64 = add(k__pos_2, mul(B_.lvl.lvl.stride, j))
                j__pos_2: int64 = add(i__pos_2, mul(C_.lvl.lvl.stride, j))
                a_ik: float64 = load(slot(A__buf_slot, np_buf_t(float64)), k__pos)
                b_kj: float64 = load(slot(B__buf_slot, np_buf_t(float64)), j__pos)
                c_ij: float64 = mul(a_ik, b_kj)
                store(slot(C__buf_slot, np_buf_t(float64)), j__pos_2, add(load(slot(C__buf_slot, np_buf_t(float64)), j__pos_2), c_ij))
    repack(C__buf_slot)
    matmul_return: FiberTensorFType(DenseLevelFType(DenseLevelFType(ElementLevelFType(fv=0.0)))) = C
    return matmul_return

ntn.Variable(
name,
BufferizedNDArrayFType(
type(val)(
Copy link
Member Author

Choose a reason for hiding this comment

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

That's something to address as a separate larger task

@mtsokol mtsokol marked this pull request as ready for review October 4, 2025 20:13
val.ndim,
TupleFType.from_tuple(val.shape_type),
),
val.from_kwargs(val.to_kwargs()),
Copy link
Member Author

Choose a reason for hiding this comment

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

Proper handling of the format inference is a separate larger task (to map traits.jl), here I only convert it to a "dict of attributes" where I can override some of them (for example promoted dtype from two inputs).

levels_to_add = [
idx for idx, f in enumerate(result_fields) if f not in fields
]
result_rep = result_rep.add_levels(levels_to_add)
Copy link
Member Author

Choose a reason for hiding this comment

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

As in the comment above - temporary selection of format (including removing/adding levels), this will be moved to a separate module.

]
return fmt(*args)
}
return fmt(**kwargs)
Copy link
Member Author

Choose a reason for hiding this comment

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

formats now have "multiple constructors", whether we construct from Numba or in the facing API

def __init__(
self,
arr: np.ndarray | NumpyBuffer,
val: np.ndarray | NumpyBuffer,
Copy link
Member Author

Choose a reason for hiding this comment

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

Standarization of names, to match FiberTensor

Comment on lines +286 to +287
shape: tuple[np.integer, ...] | None = None,
strides: tuple[np.integer, ...] | None = None,
Copy link
Member Author

Choose a reason for hiding this comment

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

For simulating multiple constructors.

@mtsokol
Copy link
Member Author

mtsokol commented Oct 4, 2025

@willow-ahrens So right now E2E Numba matmul runs with bufferized and fiber_tensor(dense(... formats.

One part here might look like a bit of a mess, namely from_kwargs and to_kwargs but it's a temporary solution for figuring out output format for mapjoin and aggregates. I can override certain parameters this way, e.g. dtype.

@willow-ahrens willow-ahrens self-requested a review October 18, 2025 01:53
Copy link
Member

@willow-ahrens willow-ahrens left a comment

Choose a reason for hiding this comment

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

Overall, this looks good. I'd like it to pass tests, work for C codegen, and implement the freeze, thaw, etc. I'm more than happy to take a pass at this this week. Let's sync up next tuesday on next steps.

"""

@property
@abstractmethod
Copy link
Member

Choose a reason for hiding this comment

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

I think the level needs to know this stuff.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, right - it should still be there, done.

return tns

def lower_thaw(self, ctx, tns, op):
raise NotImplementedError
Copy link
Member

Choose a reason for hiding this comment

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

We'll need an implementation of these methods. I can work on it.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks!

val_lvl = asm.GetAttr(val, asm.Literal("lvl"))
return self.lvl_t.asm_unpack(ctx, var_n, val_lvl)

def get_fields_class(self, tns, buf_s, nind, pos, op):
Copy link
Member

Choose a reason for hiding this comment

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

Is this also temporary?

Copy link
Member Author

Choose a reason for hiding this comment

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

In the def unfurl in FiberTensorFType I need to have Fields class instance so this is a proposition of an abstraction that levels would implement. So for the current form the answer is no, that won't be temporary.

@mtsokol
Copy link
Member Author

mtsokol commented Nov 15, 2025

@willow-ahrens Thank you for taking a look through these changes! I know that this PR is a nightmare - with these temporary hacks for suitable reps and rather vague shape considering that looplets and sparse level are still to come.

Now all tests (and mypy) pass. In test_codegen.py the test_e2e_numba uses dense(dense(element)) format.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants