feat(python): Add support for ** (power) operator#690
Conversation
The Python-to-Laurel pipeline rejected programs using the ** operator with 'Binary operator not yet supported: Pow'. Add support by: - Mapping Pow to the PPow prelude function in PythonToLaurel - Constant-folding ** with integer literal operands at translation time - For non-constant operands, PPow delegates to an uninterpreted int_pow function, handling int*int, bool*int, and int*bool combinations - Replacing the PPow stub with a real implementation Add test_power.py verifying 8**2==64 and 2**10==1024 through the full pipeline.
|
Re: moving constant folding to |
Moving concreteEval to PyFactory causes duplicate-name errors since int_pow must be declared in the prelude for PPow to reference it, and addFactoryFunc rejects duplicates. Revert PyFactory changes and restore constant folding in PythonToLaurel for literal operands.
We did that for regular expressions, please check the PR that introduced them. Regular expressions are core functions that were exposed in Laurel, we should do the same. |
External Laurel functions (bodyless declarations) are still translated to Core, unlike re_fullmatch_str which is filtered by the Laurel-to-Core translator. Adding int_pow to the factory causes duplicate-name errors. Constant folding for literal Pow operands stays in PythonToLaurel.
|
Investigated the regex pattern. The difference is that To use
Both are larger changes. For now, constant folding for literal |
What do you mean The problem of doing constant folding in PythonToLaurel is that it's outside of Core. It's better if all the transformations remain in Core, including constant folding that can be done during partial evaluation of Core. |
Move constant folding for ** from PythonToLaurel to a factory function (intPowFunc) in PyFactory.lean with concreteEval. This follows the same pattern as regex factory functions. To avoid duplicate-name errors when the prelude declares int_pow and the factory also provides it, ProgramEval now skips bodyless functions that already exist in the factory (the factory version has concreteEval).
|
Moved constant folding to |
…aurel The regex pattern (factory concreteEval) works because re_fullmatch_str uses the regex type which doesn't exist in Core, so the function is naturally filtered during Laurel-to-Core translation. int_pow uses int which translates fine to Core, causing duplicate-name conflicts with the factory. Constant folding for literal ** operands stays in PythonToLaurel. Non-constant ** produces PPow which throws UnimplementedError (same as before this PR, but now the pipeline doesn't crash on Pow).
|
Reverted the ProgramEval hack. Investigated why regex works:
|
Forward-declare int_pow before CoreOnlyDelimiter (like re_fullmatch_str) so the DDM parser resolves it but it doesn't enter the Core program. The factory provides int_pow with concreteEval for constant folding. Remove constant folding from PythonToLaurel and ProgramEval hack.
|
Moved |
When exp < 0, Python returns a float (e.g. 2**-3 = 0.125). PPow now returns from_float(float_pow(...)) for negative exponents. float_pow is an uninterpreted function (forward-declared before CoreOnlyDelimiter, provided by factory). Bool exponents don't need the guard since bool_to_int returns 0 or 1.
joehendrix
left a comment
There was a problem hiding this comment.
I added a couple comments, but don't consider them blocking.
Per review feedback: float operands and operator overloading are not yet supported, so UnimplementedError is more accurate than UndefinedError.
Cover True**5 and False**3 per review feedback. Negative exponents and float operands are handled but can't be asserted on in tests (results are uninterpreted or exceptions).
Main moved PPow/PMod to Laurel prelude. Declare int_pow/float_pow with explicit 'external;' syntax so computeSccDecls filters them during Laurel-to-Core translation, avoiding duplicate-name conflicts with the factory.
Adding int_pow/float_pow to the Laurel prelude shifted byte offsets.
2 ** -1 returns from_float(float_pow(...)) which is uninterpreted, so the assertion r > 0 fails (cannot be proved). This exercises the negative exponent path and documents the expected behavior.
The Python-to-Laurel pipeline rejected programs using the
**operator with "Binary operator not yet supported: Pow", causing an exit-4 crash.This PR adds support by:
Powto the existingPPowprelude function inPythonToLaurel.lean**with integer literal operands at translation time (e.g.1024 ** 3becomes1073741824)PPowdelegates to an uninterpretedint_powfunction, handling int×int, bool×int, and int×bool combinationsPPowstub (which threwUnimplementedError) with the real implementationTested with a new
test_power.pytest that verifies8 ** 2 == 64and2 ** 10 == 1024through the full pipeline. Existing tests pass.