From b86cc2b4ac86f57d21ae0dc1d7b46254784ceefa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 16:17:23 +0000 Subject: [PATCH 1/4] Initial plan From 7c685cf789994bc61128fbe0af8a16080dc725f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 16:43:08 +0000 Subject: [PATCH 2/4] Increase maxInitialTerms to support formula generation for A000033 Co-authored-by: ckrause <840744+ckrause@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ src/form/formula_gen.cpp | 2 +- tests/formula/formula.txt | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb00068..d750cbbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +### Enhancements + +* Increase maximum initial terms for formula generation from 10 to 20 to support programs with many stateful cells (e.g., A000033) + ### Bugfixes * Fix parsing error during stats generation diff --git a/src/form/formula_gen.cpp b/src/form/formula_gen.cpp index dfd3295d..d9d2c91a 100644 --- a/src/form/formula_gen.cpp +++ b/src/form/formula_gen.cpp @@ -20,7 +20,7 @@ FormulaGenerator::FormulaGenerator() incEval(interpreter), freeNameIndex(0), offset(0), - maxInitialTerms(10) {} + maxInitialTerms(20) {} std::string FormulaGenerator::newName() { std::string name = "a" + std::to_string(freeNameIndex); diff --git a/tests/formula/formula.txt b/tests/formula/formula.txt index a3507da7..34f7fb1a 100644 --- a/tests/formula/formula.txt +++ b/tests/formula/formula.txt @@ -4,6 +4,7 @@ A000012: a(n) = 1 A000023: a(n) = (n-1)*(2*a(n-2)+a(n-1))-a(n-1), a(3) = -2, a(2) = 2, a(1) = -1, a(0) = 1 A000027: a(n) = n A000032: a(n) = a(n-1)+a(n-2), a(2) = 3, a(1) = 1, a(0) = 2 +A000033: a(n) = b(n-1), b(n) = c(n-7), b(11) = 135494844, b(10) = 11338855, b(9) = 1036050, b(8) = 104256, b(7) = 11672, b(6) = 1477, b(5) = 210, b(4) = 40, b(3) = 4, b(2) = 3, b(1) = 2, b(0) = 0, c(n) = truncate((c(n-1)*(2*n+9)+c(n-2)*(7*n+37)+c(n-3)*(11*n+51)+c(n-4)*(10*n+40)+c(n-5)*(3*n+21)+c(n-6)*(-3*n+9)+c(n-7)*(-2*n+1)-2*c(n-8))/2), c(14) = 311462297063636041906, c(13) = 14173726082929399560, c(12) = 675799125332580020, c(11) = 33837677493828171, c(10) = 1783734385752162, c(9) = 99271367764480, c(8) = 5851140525680, c(7) = 366526605705, c(6) = 24498813794, c(5) = 1755206648, c(4) = 135494844, c(3) = 11338855, c(2) = 1036050, c(1) = 104256, c(0) = 11672 A000034: a(n) = n%2+1 A000035: a(n) = n%2 A000045: a(n) = a(n-1)+a(n-2), a(1) = 1, a(0) = 0 From b5a3f1541877c7fcb529883f87c6611257258e83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:03:14 +0000 Subject: [PATCH 3/4] Add early failure detection for programs requiring too many initial terms Co-authored-by: ckrause <840744+ckrause@users.noreply.github.com> --- CHANGELOG.md | 2 +- src/form/formula_gen.cpp | 18 +++++++++++++++++- tests/formula/formula.txt | 1 - 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d750cbbb..56258ada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Enhancements -* Increase maximum initial terms for formula generation from 10 to 20 to support programs with many stateful cells (e.g., A000033) +* Add early failure detection in formula generation for programs requiring too many initial terms ### Bugfixes diff --git a/src/form/formula_gen.cpp b/src/form/formula_gen.cpp index d9d2c91a..1b7b80f4 100644 --- a/src/form/formula_gen.cpp +++ b/src/form/formula_gen.cpp @@ -20,7 +20,7 @@ FormulaGenerator::FormulaGenerator() incEval(interpreter), freeNameIndex(0), offset(0), - maxInitialTerms(20) {} + maxInitialTerms(10) {} std::string FormulaGenerator::newName() { std::string name = "a" + std::to_string(freeNameIndex); @@ -551,6 +551,22 @@ bool FormulaGenerator::generateSingle(const Program& p) { numTerms[name] = getNumInitialTermsNeeded(cell, name, formula, incEval); } + // Early check: fail fast if base initial terms count is too high + // Variant simplification typically increases the count by 30-70%, so we + // check with a safety margin to avoid spending time on hopeless cases + int64_t maxNumTerms = 0; + for (const auto& it : numTerms) { + maxNumTerms = std::max(maxNumTerms, it.second); + } + // Reject early if base count > 70% of limit (leaves room for ~43% increase) + if (maxNumTerms > maxInitialTerms * 7 / 10) { + Log::get().debug("Base initial terms (" + std::to_string(maxNumTerms) + + ") likely to exceed limit (" + + std::to_string(maxInitialTerms) + + ") after variant simplification, failing fast"); + return false; + } + // find and choose alternative function definitions simplifyFormulaUsingVariants(formula, numTerms); diff --git a/tests/formula/formula.txt b/tests/formula/formula.txt index 34f7fb1a..a3507da7 100644 --- a/tests/formula/formula.txt +++ b/tests/formula/formula.txt @@ -4,7 +4,6 @@ A000012: a(n) = 1 A000023: a(n) = (n-1)*(2*a(n-2)+a(n-1))-a(n-1), a(3) = -2, a(2) = 2, a(1) = -1, a(0) = 1 A000027: a(n) = n A000032: a(n) = a(n-1)+a(n-2), a(2) = 3, a(1) = 1, a(0) = 2 -A000033: a(n) = b(n-1), b(n) = c(n-7), b(11) = 135494844, b(10) = 11338855, b(9) = 1036050, b(8) = 104256, b(7) = 11672, b(6) = 1477, b(5) = 210, b(4) = 40, b(3) = 4, b(2) = 3, b(1) = 2, b(0) = 0, c(n) = truncate((c(n-1)*(2*n+9)+c(n-2)*(7*n+37)+c(n-3)*(11*n+51)+c(n-4)*(10*n+40)+c(n-5)*(3*n+21)+c(n-6)*(-3*n+9)+c(n-7)*(-2*n+1)-2*c(n-8))/2), c(14) = 311462297063636041906, c(13) = 14173726082929399560, c(12) = 675799125332580020, c(11) = 33837677493828171, c(10) = 1783734385752162, c(9) = 99271367764480, c(8) = 5851140525680, c(7) = 366526605705, c(6) = 24498813794, c(5) = 1755206648, c(4) = 135494844, c(3) = 11338855, c(2) = 1036050, c(1) = 104256, c(0) = 11672 A000034: a(n) = n%2+1 A000035: a(n) = n%2 A000045: a(n) = a(n-1)+a(n-2), a(1) = 1, a(0) = 0 From 456c49528c66aa60e384fb3171fbc51b3af6eb65 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:51:00 +0000 Subject: [PATCH 4/4] Improve formula generation performance by imposing tighter limits in variant simplification Co-authored-by: ckrause <840744+ckrause@users.noreply.github.com> --- CHANGELOG.md | 2 +- src/form/formula_gen.cpp | 16 ---------------- src/form/variant.cpp | 10 ++++++++-- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56258ada..273d3485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ### Enhancements -* Add early failure detection in formula generation for programs requiring too many initial terms +* Improve formula generation performance by imposing tighter limits on variant simplification ### Bugfixes diff --git a/src/form/formula_gen.cpp b/src/form/formula_gen.cpp index 1b7b80f4..dfd3295d 100644 --- a/src/form/formula_gen.cpp +++ b/src/form/formula_gen.cpp @@ -551,22 +551,6 @@ bool FormulaGenerator::generateSingle(const Program& p) { numTerms[name] = getNumInitialTermsNeeded(cell, name, formula, incEval); } - // Early check: fail fast if base initial terms count is too high - // Variant simplification typically increases the count by 30-70%, so we - // check with a safety margin to avoid spending time on hopeless cases - int64_t maxNumTerms = 0; - for (const auto& it : numTerms) { - maxNumTerms = std::max(maxNumTerms, it.second); - } - // Reject early if base count > 70% of limit (leaves room for ~43% increase) - if (maxNumTerms > maxInitialTerms * 7 / 10) { - Log::get().debug("Base initial terms (" + std::to_string(maxNumTerms) + - ") likely to exceed limit (" + - std::to_string(maxInitialTerms) + - ") after variant simplification, failing fast"); - return false; - } - // find and choose alternative function definitions simplifyFormulaUsingVariants(formula, numTerms); diff --git a/src/form/variant.cpp b/src/form/variant.cpp index 9d2becb9..e629279e 100644 --- a/src/form/variant.cpp +++ b/src/form/variant.cpp @@ -39,7 +39,7 @@ bool VariantsManager::update(Variant new_variant) { return false; } const auto num_terms = new_variant.definition.numTerms(); - if (num_terms > 500) { // magic number + if (num_terms > 200) { // limit term count to reduce complexity Log::get().debug("Skipping variant with " + std::to_string(num_terms) + " terms"); return false; // too many terms @@ -49,6 +49,10 @@ bool VariantsManager::update(Variant new_variant) { // return false; // } auto& vs = variants[new_variant.func]; + // limit number of variants per function to reduce O(n^4) complexity + if (vs.size() >= 20) { + return false; + } // prevent rapid increases of variant sizes if (!std::all_of(vs.begin(), vs.end(), [new_variant](const Variant& v) { return v.used_funcs.size() + 1 >= new_variant.used_funcs.size(); @@ -196,7 +200,9 @@ bool simplifyFormulaUsingVariants( Formula& formula, std::map& num_initial_terms) { VariantsManager manager(formula, num_initial_terms); bool found = false; - while (manager.numVariants() < 200) { // magic number + size_t iterations = 0; + while (manager.numVariants() < 100 && iterations < 50) { // tighter limits + iterations++; if (findVariants(manager)) { found = true; } else {