Skip to content

A monomial body is multiplied in closed form (#717) - #1153

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
closed-form-product
Sep 3, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
closed-form-product

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

The same for product as #1152 did for sum, over the narrower class a product's shape allows.

"product(k, k, 1, n)".Simplify()      was  product(k, k, 1, n)
                                      is   piecewise(n! provided n >= 1, 1)

"product(2, k, 1, 500)".Simplify()    was  product(2, k, 1, 500)
                                      is   2^500, written out in full
input now
product(k ^ 2, k, 1, n) (n!)^2
product(k ^ 3, k, 1, n) (n!)^3
product(2 * k, k, 1, n) n! * 2^n
product(c, k, m, n) c^(n - m + 1) — symbolic lower bound, no factorial needed

This closes the last of the three sum/product rows in #717's survey.

Narrower than the sum, and not by omission

A sum of two terms is the sum of their sums; a product of two terms is not the product of their
products in any way that helps. There is no linearity to take a general polynomial apart with, so
what separates is the body that is one term — c * k^p, over which the product becomes a
power of the constant times a power of the factorial.

product(k + 1, k, 1, n) is therefore still carried, and there is a test saying so.

The condition moved by one, and that is the interesting part

The sum's condition is to >= from - 1. The product's is to >= from, and the single point
between them is the reason.

At the empty range itself the closed form is c^0 — which is 1 for every c except zero,
where it is undefined, while the empty product is 1 for every c including zero. Handing
that one point to the identity branch keeps a value from becoming an undefinedness, which is what
the contract's O4 asks. It costs nothing: both branches say 1 there.

I found this by comparing the closed form against the expansion at bounds either side of the
boundary — it is not visible from the formula, and it is why the tests are written as agreement
with the expansion rather than against a printed form.

A lower bound that cannot be a condition

product(k, k, a, b) is b!/(a-1)! only where a >= 1. Below that the range runs through zero,
so the product is 0, while (a-1)! is undefined at the negative integers.

That cannot go into the piecewise condition, because a < 1 does not mean the range is empty
— a branch reading "identity otherwise" would answer 1 where the product is 0. So it is
decided before anything is built: a lower bound that is not a concrete integer of at least one is
declined outright where the index is in the body. product(k, k, 0, n) and product(k, k, m, n)
stay as written, with tests carrying that reason.

A constant body has no such restriction, there being no factorial in its answer — hence
product(c, k, m, n) working with m symbolic.

Records that changed their verdict

Three tests asserted the product is carried, two of them added by #1152 an hour earlier, with
the reason it could not yet be answered. They now assert what is still carried — a body that is
not one term.

Corrected the same way: Syntax.md, MathS.Product's <returns>, and #1152's own
BREAKING-CHANGES.md entry, which said answering the product "is not done here".

Comparison.md's declines cell is again left as it was measured. That table records one
sympyparity run at one commit, and a hand-corrected cell would be a measurement nobody took; its
note now names two moved rows rather than one, and says the row between them — infinite series —
has not moved, an infinite bound being refused outright by both closed forms.

Verification

Full suite 9487 passed, 0 failed, 14 skipped. 27 new tests.

Every closed form is checked by agreeing with the expansion at bounds on both sides of the
empty-range boundary. The exception is the c = 0 test, which is about the boundary itself and
would pass vacuously if it only compared the two routes.

Part of #717.

The same for `product` as #1152 did for `sum`, over the narrower class a
product's shape allows: a sum of two terms is the sum of their sums, and a
product of two terms is not the product of their products in any way that
helps. What separates is the body that is one term.

    product(k, k, 1, n)      was  carried
                             is   piecewise(n! provided n >= 1, 1)
    product(2, k, 1, 500)    was  carried
                             is   the 151-digit integer

`product(k^2, k, 1, n)` is `(n!)^2`, `product(2 * k, k, 1, n)` is `n! * 2^n`,
and a constant body needs no factorial at all -- `product(c, k, m, n)` is
`c^(n - m + 1)`, symbolic lower bound included. This closes the last of the
three `sum`/`product` rows in #717's survey.

The condition is `to >= from` where the sum's is `to >= from - 1`, and the one
point between them is the reason. At the empty range itself the closed form is
`c^0`, which is 1 for every `c` but zero and undefined there, while the empty
product is 1 for every `c` including zero. Giving that point to the identity
branch keeps a value from becoming an undefinedness. It cost nothing: both
branches say 1 there. Found by comparing the closed form against the expansion
at bounds either side of the boundary, which is why the check is written that
way rather than against a printed form.

A lower bound that is not a concrete integer of at least one is declined where
the index is in the body, rather than conditioned. `b!/(a-1)!` holds only for
`a >= 1`; below that the range runs through zero so the product is 0 while
`(a-1)!` is undefined -- and `a < 1` does not make the range empty, so it
cannot share a branch with the empty-range case. A piecewise reading "identity
otherwise" would be wrong there.

Three tests asserted the product is carried, two of them added by #1152 with
the reason it could not yet be answered. They now assert what is still carried
-- a body that is not one term. Syntax.md, MathS.Product's summary and the
#1152 entry in BREAKING-CHANGES.md are corrected the same way.
Comparison.md's cell is again left as measured, its note now naming two moved
rows rather than one.

Full suite 9487 passed, 0 failed.

Part of #717.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet
Rafael-SOWNet merged commit c19393e into master Sep 3, 2026
31 checks passed
@Rafael-SOWNet
Rafael-SOWNet deleted the closed-form-product branch September 3, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant