Skip to content

Commit 5070993

Browse files
authored
Fix PARI evaluation error for constant identity functions (#621)
1 parent 8e571d5 commit 5070993

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
* Integrate `zlib` for gzip decompression, replacing external `gzip`/`gunzip` tools
77
* Report broken b-files to LODA API server for automatic cache invalidation
88

9+
### Bugfixes
10+
11+
* Fix PARI evaluation error for formulas with constant identity functions (e.g., A026765)
12+
913
## v25.11.29
1014

1115
### Enhancements

src/form/formula_gen.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,9 @@ bool FormulaGenerator::generate(const Program& p, int64_t id, Formula& result,
782782
// replace simple references to recursive functions
783783
FormulaSimplify::replaceSimpleRecursiveRefs(result);
784784

785+
// replace constant identity functions (e.g., b(n) = b(n-1) with no base case)
786+
FormulaSimplify::replaceConstantIdentityFunctions(result);
787+
785788
// replace arithmetic & geometric progressions
786789
// TODO: check if this is really needed here
787790
FormulaSimplify::replaceArithmeticProgressions(formula);

src/form/formula_simplify.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@
77
#include "form/expression_util.hpp"
88
#include "form/formula_util.hpp"
99

10+
// Helper function to replace all function calls by name with a replacement
11+
// expression
12+
void replaceFunctionByName(Expression& expr, const std::string& funcName,
13+
const Expression& replacement) {
14+
// First recurse into children
15+
for (auto& child : expr.children) {
16+
replaceFunctionByName(child, funcName, replacement);
17+
}
18+
// Then check if this expression matches
19+
if (expr.type == Expression::Type::FUNCTION && expr.name == funcName) {
20+
expr = replacement;
21+
}
22+
}
23+
1024
// Helper function to collect simple functions from formula
1125
std::set<std::string> collectSimpleFunctions(const Formula& formula) {
1226
std::set<std::string> funcs;
@@ -560,3 +574,87 @@ void FormulaSimplify::replaceSimpleRecursiveRefs(Formula& formula) {
560574
processedRecursiveFuncs.insert(funcName);
561575
}
562576
}
577+
578+
// Helper function to check if a function is a constant identity (f(n) = f(n-k))
579+
bool isConstantIdentityFunction(const Formula& formula,
580+
const std::string& funcName) {
581+
// Find the general definition (the one with a parameter, not a constant)
582+
Expression funcKey = ExpressionUtil::newFunction(funcName);
583+
auto it = formula.entries.find(funcKey);
584+
if (it == formula.entries.end()) {
585+
return false; // No general definition found
586+
}
587+
588+
const auto& rhs = it->second;
589+
590+
// Check if RHS is f(n-k) where k > 0
591+
if (rhs.type != Expression::Type::FUNCTION || rhs.children.size() != 1) {
592+
return false;
593+
}
594+
595+
// Must reference itself
596+
if (rhs.name != funcName) {
597+
return false;
598+
}
599+
600+
const auto& arg = rhs.children.front();
601+
602+
// Check if argument is n-k where k > 0
603+
Number offset;
604+
if (!extractArgumentOffset(arg, offset)) {
605+
return false;
606+
}
607+
608+
// Must be a backwards reference (offset < 0)
609+
if (offset >= Number::ZERO) {
610+
return false;
611+
}
612+
613+
// Check that there are no initial terms (base cases) for this function
614+
for (const auto& entry : formula.entries) {
615+
if (entry.first.type == Expression::Type::FUNCTION &&
616+
entry.first.name == funcName && entry.first.children.size() == 1 &&
617+
entry.first.children.front().type == Expression::Type::CONSTANT) {
618+
// Found an initial term - this is not a constant identity function
619+
return false;
620+
}
621+
}
622+
623+
return true;
624+
}
625+
626+
bool FormulaSimplify::replaceConstantIdentityFunctions(Formula& formula) {
627+
// Collect all function names
628+
auto funcs = FormulaUtil::getDefinitions(formula, Expression::Type::FUNCTION);
629+
630+
// Find constant identity functions
631+
std::set<std::string> constantFuncs;
632+
for (const auto& funcName : funcs) {
633+
if (isConstantIdentityFunction(formula, funcName)) {
634+
constantFuncs.insert(funcName);
635+
}
636+
}
637+
638+
if (constantFuncs.empty()) {
639+
return false;
640+
}
641+
642+
// Replace references to constant identity functions with 0
643+
Expression zero = ExpressionUtil::newConstant(0);
644+
for (const auto& funcName : constantFuncs) {
645+
// Remove all entries for this function
646+
FormulaUtil::removeFunctionEntries(formula, funcName);
647+
648+
// Replace all references to funcName(...) with 0 in other entries
649+
for (auto& entry : formula.entries) {
650+
replaceFunctionByName(entry.second, funcName, zero);
651+
}
652+
}
653+
654+
// Normalize after replacements
655+
for (auto& entry : formula.entries) {
656+
ExpressionUtil::normalize(entry.second);
657+
}
658+
659+
return true;
660+
}

src/form/formula_simplify.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,21 @@ class FormulaSimplify {
8282
* references
8383
*/
8484
static void replaceSimpleRecursiveRefs(Formula& formula);
85+
86+
/**
87+
* Replaces constant identity functions with constant 0.
88+
*
89+
* This method identifies functions of the form f(n) = f(n-k) with no base
90+
* case (no initial term like f(0) = c). Such functions represent constant
91+
* values that were initialized to 0 during formula generation. Without
92+
* this simplification, they would cause infinite recursion in PARI/GP.
93+
* For example:
94+
* b(n) = b(n-1) (with no b(0) = ...)
95+
* Will be replaced with: constant 0 everywhere b is referenced
96+
*
97+
* @param formula The formula to simplify by replacing constant identity
98+
* functions
99+
* @return true if any replacements were made
100+
*/
101+
static bool replaceConstantIdentityFunctions(Formula& formula);
85102
};

0 commit comments

Comments
 (0)