Skip to content

Growth domain: X*X and X^2 give different normal forms (non-minimal, safe) #1088

Description

@isPANN

Background

The growth domain (src/growth.rs) computes a Big-O normal form as an antichain of monomials. Two operators that are mathematically identical — X * X (Expr::Mul) and X^2 (Expr::Pow) — produce different normal forms when X is a multi-term antichain:

$ pred-sym big-o "(n + m)^2"
O(m^2 + n^2)
$ pred-sym big-o "(n + m)*(n + m)"
O(m*n + m^2 + n^2)
$ pred-sym compare "(n+m)*(n+m)" "(n+m)^2"
Big-O equal: false

Root cause: Pow(X, 2) uses the raise-each-term rule (a deliberate widening introduced so sqrt((n-m)^2) ≍ n+m), which yields {n², m²}. Mul(X, X) uses the general antichain product, which yields all cross terms {n², n·m, m²}. The cross term n·m is asymptotically dominated by the sum n² + m² (AM–GM: n·m ≤ (n²+m²)/2), but the antichain order is per-term — no single term dominates n·m — so it is kept.

Why this is not a soundness bug (but worth fixing)

Both results are valid upper bounds — Mul merely over-approximates, Pow gives the tighter (and also valid) answer. Dominance decisions stay safe (an over-approximation never under-states growth), so find_dominated_rules and the measured search's pre-flight guard remain sound. The observable costs are:

  1. pred-sym compare reports "not equal" for two expressions that are the same growth class, whenever one is written with * and the other with ^.
  2. Composed overheads that happen to multiply an antichain by itself render a non-minimal Big-O (extra cross terms) in pred path.

Objective

Make X * X and X^2 produce the same normal form — either by having Mul recognize a self-product, or (more generally) by teaching the antichain to drop a term that is dominated by the sum of other terms (AM–GM pruning), not just by a single term. Decide which; the second is more general but changes the antichain invariant, so it needs care.

Verification

pred-sym compare "(n+m)*(n+m)" "(n+m)^2"equal, and pred-sym big-o "(n+m)*(n+m)"O(m^2 + n^2) (no m*n term). Add a growth unit test pinning Mul(X,X) == Pow(X,2) for a multi-term X.

Negative control: the same test on today's code fails (the two forms differ) — proving the fix changed real behavior.

Related

Found during the old-vs-new correctness audit of PR #1083 (growth domain). Independent of the milestone's shipped correctness — this is a tightness/consistency refinement, not a soundness fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions