Skip to content

Refactor calcfc_internal to remove nested function#218

Closed
nbelakovski wants to merge 1 commit into
libprima:mainfrom
nbelakovski:refactor_calcfc_internal
Closed

Refactor calcfc_internal to remove nested function#218
nbelakovski wants to merge 1 commit into
libprima:mainfrom
nbelakovski:refactor_calcfc_internal

Conversation

@nbelakovski
Copy link
Copy Markdown
Contributor

This was done to support building on musl back when we were hoping to include the Python bindings into scipy. Even though we will likely use gcc14 to support musl for any potential release of the bindings as a separate package (gcc14 with -ftramponine-impl=heap allows for nested functions), we can refactor this code to not require a nested function in the first place, which I think is a preferable solution.

Of course this doesn't remove the nested functions in the bindings themselves, which we need to keep, but if we have the opportunity to avoid using a nested function I think we should do so.

This is in support of efforts to integrate PRIMA with SciPy. They
provide wheels for musl, and in musl you cannot have nested functions in
Fortran (at least, not without gcc 14, which is cutting edge, and
therefore it's not practical for scipy to switch to it).
@nbelakovski
Copy link
Copy Markdown
Contributor Author

Separately I am working on some other PRs to merge the changes in my repo back into this one. Stay tuned for those over the next couple days.

@zaikunzhang
Copy link
Copy Markdown
Member

zaikunzhang commented Mar 28, 2025

Thank you, Nickolai.

However, I would like to propose another solution for the nested functions:

  1. Keep the code under fortran/ unchanged.

  2. Put the changed code in some directory under python/. This involves only two or three files, right?

  3. When building the Python wheels, use the files in 2 as needed.

Alternatively, we can also include a script in python/tools/ to produce the modified files before the building.

The reason for this solution is as follows.

The code of PRIMA is supposed to be the reference implementation, which is its name (if we only need something that works, then Powell's F77 version is fine and we would not need PRIMA at all, but I suppose you know very well that Powell's code does not work well as a reference implementation). The nested subroutine is indeed the mathematically correct implementation. We should not sacrifice the correctness due to compiler issues. If we change the code, then 10 years later, when gcc-14.0+ becomes the most popular compilers, we need to change it back to the correct version. This makes the code not future-proof, contradicting the "Why" of PRIMA:

The focus is to implement these methods in a structured and modularized way so that they are understandable, maintainable, extendable, fault-tolerant, and future-proof.

Similar things have already been done in the Fortran version and MATLAB binding. PRIMA never sacrifices correctness and clarity for compiler's fault, which is not a strange thing to me (See also https://github.com/zequipe/test_compiler).

Thank you.

@nbelakovski
Copy link
Copy Markdown
Contributor Author

Why is the nested function mathematically correct? Or maybe a better question, why is refactoring the code to not be a nested function mathematically incorrect?

@zaikunzhang
Copy link
Copy Markdown
Member

zaikunzhang commented Mar 28, 2025

Because evaluate is supposed to evaluate the objective function or the constraint function. It should take a function and return its value, not only in the Fortran implementation but also others, as this is the mathematical and English definition of evaluate.

We also have to take into account the consistency among the solvers. See how evaluate is defined and works in other solvers.

@nbelakovski
Copy link
Copy Markdown
Contributor Author

I think it's better to avoid compiling with an executable stack if possible. Removing the nested function here means that we could do that when compiling the Fortran code without bindings (since, as we discussed, we prefer to keep the nested functions in the bindings because the only alternative is a global variable which breaks thread safety). That said, it's not something that anyone has complained about so I don't feel particularly strongly about it at the moment and I'm ok with closing this PR until someone complains about it (in which case we can try to direct them to gcc 14's -ftrampoline-impl-heap first before considering this refactor).

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.

2 participants