Skip to content

Support for GNU Scientific Library#986

Open
pbrubeck wants to merge 3 commits intoinducer:mainfrom
pbrubeck:pbrubeck/hypergeometric
Open

Support for GNU Scientific Library#986
pbrubeck wants to merge 3 commits intoinducer:mainfrom
pbrubeck:pbrubeck/hypergeometric

Conversation

@pbrubeck
Copy link

@pbrubeck pbrubeck commented Mar 4, 2026

No description provided.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds initial support for calling GNU Scientific Library (GSL) special functions from Loopy’s C target infrastructure (currently wired into the GNU-libc-enabled C target).

Changes:

  • Introduces GSLCallable to map Loopy callables to gsl_sf_* functions and emit required C preambles.
  • Adds get_gsl_callables() (currently exposing hyperg_2F1) and registers these callables in CWithGNULibcASTBuilder.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines 1646 to +1647
callables.update(get_gnu_libc_callables())
callables.update(get_gsl_callables())
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Registering GSL callables for CWithGNULibcASTBuilder makes ExecutableCWithGNULibcTarget kernels that use gsl_sf_* likely fail at build/link time unless the CCompiler is configured with the correct include_dirs and libraries (e.g. gsl, often also gslcblas). Consider providing a dedicated target/compiler preset for GSL (or updating this target) that wires in the required toolchain options, or otherwise surfacing a clear error when GSL headers/libs aren't available.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

This is a fair point. Here I'm abusing of the GNULibc classes, for which we should only register functions that depend on GNULibc. Should I create separate GSL counterparts?

Copy link
Owner

Choose a reason for hiding this comment

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

I'm not sure I love either solution. I'm not sure I want a thicket of derived classes, but I agree that adding this by default here isn't awesome.

It might be best to leave it to the user to have their own derived class. Of course, this would be easier if known_callables lived in TargetBase, not the ASTBuilder. Let me see if that's a refactor that AI can do: #988.

Comment on lines +894 to +898
def get_gsl_callables():
# Support special functions from
# https://www.gnu.org/software/gsl/doc/html/specfunc.html
func_ids = ["hyperg_2F1"]
return {id_: GSLCallable(id_) for id_ in func_ids}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

New callable support (hyperg_2F1 via GSL) is introduced here, but there is no corresponding test (compare the existing test_glibc_bessel_functions coverage). Adding a test that compiles/runs an ExecutableCWithGNULibcTarget kernel calling hyperg_2F1 and validates against a reference (e.g. scipy.special.hyp2f1/mpmath), with a skip if GSL isn't available, would help prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +861 to +867
arg_num_to_dtype = {cast("int", id): t for id, t in arg_id_to_dtype.items()}
arg_num_to_dtype[-1] = arg_num_to_dtype[max(arg_num_to_dtype)]

name_in_target = f"gsl_sf_{name}"
return (self.copy(name_in_target=name_in_target,
arg_id_to_dtype=constantdict(arg_num_to_dtype)),
clbl_inf_ctx)
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

with_types does not validate the expected signature for GSL special functions (e.g. hyperg_2F1 is a fixed-arity function) and currently sets the return dtype to the dtype of the last argument via max(arg_num_to_dtype). This can mis-specialize the callable (wrong return type, accepting wrong number/types of args) and can also raise a ValueError on max() for invalid/empty calls. Consider explicitly enforcing the correct arity and specializing both argument and return dtypes to the actual GSL signature (typically double), relying on Loopy's casting to convert inputs when needed.

Copilot uses AI. Check for mistakes.
arg_id_to_dtype=constantdict(arg_num_to_dtype)),
clbl_inf_ctx)

def generate_preambles(self, target):
Copy link
Owner

Choose a reason for hiding this comment

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

Could you add the missing annotations (here and below)? (bpr will also complain I think.)

Copy link
Owner

Choose a reason for hiding this comment

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

Would it make sense to test this? (Not saying we should, just raising the possibility.)

Comment on lines +857 to +859
for id in arg_id_to_dtype:
if not isinstance(id, int):
raise LoopyError(f"'{name}' can take only positional arguments")
Copy link
Owner

Choose a reason for hiding this comment

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

If you do this as a list comprehension, you may not have to cast below.

Comment on lines +861 to +867
arg_num_to_dtype = {cast("int", id): t for id, t in arg_id_to_dtype.items()}
arg_num_to_dtype[-1] = arg_num_to_dtype[max(arg_num_to_dtype)]

name_in_target = f"gsl_sf_{name}"
return (self.copy(name_in_target=name_in_target,
arg_id_to_dtype=constantdict(arg_num_to_dtype)),
clbl_inf_ctx)
Copy link
Owner

Choose a reason for hiding this comment

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

There's probably some argument type validation missing here. I'm willing to believe that this uses overloads for types (C11-style), but that's probably still a finite set?

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
return {id_: GNULibcCallable(id_) for id_ in func_ids}


def get_gsl_callables():
Copy link
Owner

Choose a reason for hiding this comment

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

Add this to the documentation somewhere.

Comment on lines 1646 to +1647
callables.update(get_gnu_libc_callables())
callables.update(get_gsl_callables())
Copy link
Owner

Choose a reason for hiding this comment

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

I'm not sure I love either solution. I'm not sure I want a thicket of derived classes, but I agree that adding this by default here isn't awesome.

It might be best to leave it to the user to have their own derived class. Of course, this would be easier if known_callables lived in TargetBase, not the ASTBuilder. Let me see if that's a refactor that AI can do: #988.

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