Skip to content

Expression-based iterator bounds: bounds as functions of other variables #64

Description

@djdarcy

Expression-based iterator bounds: bounds as functions of other variables

Problem

When searching across multiple power values, optimal search bounds decrease as the power increases. Currently users must manually adjust --max n for each run:

# From README — bounds decrease as power p increases
python prime-square-sum.py --expr "does_exist primesum(n,3) == trisum(666)" --max n:50000000
python prime-square-sum.py --expr "does_exist primesum(n,4) == trisum(666)" --max n:10000000
python prime-square-sum.py --expr "does_exist primesum(n,5) == trisum(666)" --max n:5000000

The pattern: 50M → 10M (÷5) → 5M (÷2). This follows a decreasing curve — as the power grows, prime powers grow faster, so fewer terms are needed to reach the target. Users must manually compute and type these bounds for each run.

Proposed solution

Allow iterator bounds to be expressions that reference other variables. When a bound contains a variable reference, it is evaluated dynamically per-iteration:

# Bound for n depends on p — decreases as p increases
python prime-square-sum.py \
    --expr "does_exist primesum(n,p) == trisum(666)" \
    --var p:3:8 \
    --max "n:50000000 / (p - 1)^2"

# With the curve 50M / (p-1)²:
#   p=3: 50M / 4  = 12.5M
#   p=4: 50M / 9  ≈  5.6M
#   p=5: 50M / 16 ≈  3.1M
#   p=6: 50M / 25 =  2.0M

Other possible curve shapes:

--max "n:50000000 * (1 / (p - 1))"    # Hyperbolic: 50M, 25M, 16.7M, 12.5M
--max "n:50000000 / 2^(p - 3)"        # Exponential halving: 50M, 25M, 12.5M
--max "n:int(50e6 / factorial(p-2))"   # Factorial decay (very aggressive)

The idea is to approximate the curve where computational feasibility drops off — something like 1/(p_max - n) or 1/(p-1)^k — so that at the base power you get the full max range, and it decreases naturally as powers grow.

Design considerations

  1. Expression evaluation: Reuse the existing expression grammar/parser from utils/grammar.py. Bounds expressions would support the same arithmetic operators and math functions already available.

  2. Variable resolution order: When bound for variable n references variable p, p must be the outer iterator (resolved first). This interacts with Multi-variable iteration strategies (product, parallel, adaptive) #42 (multi-variable iteration strategies) — variable dependency determines iteration order.

  3. Type handling: Bounds expressions should evaluate to integers for integer iterators. An implicit int() or floor() may be needed for expressions that produce floats.

  4. Static vs dynamic bounds: Current bounds are static (parsed once at startup). Expression bounds would need evaluation at each outer iteration step. Performance impact should be negligible since bound evaluation is O(1) vs the inner loop work.

  5. Validation: Bounds that evaluate to ≤ 0 or to non-numeric values should produce clear error messages.

CLI syntax options

# Option A: Inline expression in --max value (preferred — minimal syntax change)
--max "n:50e6 / (p-1)^2"

# Option B: Separate flag for expression bounds
--max-expr "n:50e6 / (p-1)^2"

# Option C: Use the existing expression parser notation
--max "n:{50e6 / (p-1)^2}"

Option A is simplest — detect whether the bound value contains variable references and switch to expression evaluation automatically.

equations.json schema extension

{
  "iterators": {
    "p": {"start": 3, "stop": 8, "step": 1},
    "n": {
      "start": 1,
      "stop": "50000000 / (p - 1)^2",
      "step": 1
    }
  }
}

When stop is a string (vs number), it is treated as an expression with variable references.

Acceptance criteria

  • --max accepts expressions containing variable references
  • Expressions evaluated per outer-iteration step
  • Works with existing math functions (log, sqrt, floor, etc.)
  • Clear error messages for invalid/non-numeric bound expressions
  • equations.json stop field accepts expression strings
  • Documentation and examples

Related issues

Analysis

See 2026-02-10__16-08-44__full-postmortem_issue-63-dynamic-cli-help-defaults.md §7 for the original observation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions