Skip to content

LinearCombination: higher-dimension capable LLVM implementation#3533

Open
kmantel wants to merge 1 commit into
PrincetonUniversity:develfrom
kmantel:llvm-combine
Open

LinearCombination: higher-dimension capable LLVM implementation#3533
kmantel wants to merge 1 commit into
PrincetonUniversity:develfrom
kmantel:llvm-combine

Conversation

@kmantel
Copy link
Copy Markdown
Collaborator

@kmantel kmantel commented May 6, 2026

supports parameter specifications as scalar, len-1 vector, or N-dim array matching shape of input (weights, exponents) or output (scale, offset)

(len-M vector elementwise for (M, ...) shape arrays is intended to work, but does not work in non-compiled PNL)

expected = np.prod(tmp, axis=0) * scale + offset

# wider tolerances needed for fp32
np.testing.assert_allclose(res, expected, rtol=3e-5, atol=2e-7)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

This PR causes the following changes to the html docs (ubuntu-latest-3.11):

No differences!

...

See CI logs for the full diff.

@kmantel kmantel requested a review from jvesely May 6, 2026 10:52
supports parameter specifications as scalar, len-1 vector, or N-dim
array matching shape of input (weights, exponents)
or output (scale, offset)

(len-M vector elementwise for (M, ...) shape arrays is intended to work,
but does not work in non-compiled PNL)
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

This PR causes the following changes to the html docs (ubuntu-latest-3.11):

No differences!

...

See CI logs for the full diff.

self._gen_llvm_combine(builder, ctx=ctx, vi=arg_in, vo=arg_out, params=params)
return builder

def _llvm_combine_body(self, builder, ctx, vi, vo, val_f, pow_f, comb_op, params, indices):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

All code generating functions should be prefixed with _gen_llvm

if len(param_type) == 1 and not isinstance(param_type.element, pnlvm.ir.ArrayType):
index = [ctx.int32_ty(0)]

if not isinstance(index, list):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It might be better to guarantee that index/indices is always a list.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I did that trying to deal with using lists as index in LinearCombination (ex L1568 exponent = self._gen_llvm_load_param(ctx, b, params, EXPONENTS, in_idx, 1.0) without changing other TransformFunctions or overriding _gen_llvm_load_param and couldn't think of something better. I'm fine changing it if it's better to do one of those or something else I haven't thought of.

offset = self._gen_llvm_load_param(ctx, builder, params, OFFSET, index, -0.0)
def _gen_llvm_function_body(self, ctx, builder, params, _, arg_in, arg_out, *, tags: frozenset):
# Sometimes we arg_out to 2d array
if self.defaults.variable.ndim == self.defaults.value.ndim:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It'd be better to use compiled data types rather than python dimensionality to maintain a single source of truth.


if isinstance(ptro.type.pointee, pnlvm.ir.ArrayType):
with pnlvm.helpers.array_ptr_loop(builder, ptro, f"combine_axis{len(indices)}") as (b, idx):
self._llvm_combine_body(b, ctx, vi, vo, val_f, pow_f, comb_op, params, [*indices, idx])
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Rather that recursing here, it might be clearer to use recursive_iterate_arrays over the output array.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Can this be used when exponents/weights are either scalar or matching the input shape? Not sure about it, but it seems that since recursive_iterate_arrays yields gep results, it would need to conditionally include or exclude those parameter vals in the recursive_iterate_arrays calls, and need to be repeated per combination.

# random 1/-1
weights = 2 * (np.round(RANDh_V['weights'][variable.shape]) - .5)
if exponents == 'A':
exponents = RANDh_V['exponents'][variable.shape]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why can't the above be passed as parametrized arguments?

Copy link
Copy Markdown
Collaborator Author

@kmantel kmantel May 12, 2026

Choose a reason for hiding this comment

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

They could be generated in the test too.

Otherwise, I think the issue is that they need to match [parts of] the variable shape, but AFAIK there isn't a way to couple parametrization vars (as in, only do (var1,exp1), (var2,exp2)... and not also (var1,exp2), (var2,exp1)), so I'd still need to pass some function or dict to get the correctly shape value out.

But yeah I can also pre-calculate these a bit better.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants