Apply the parity identities, so that sin(-x) + sin(x) reaches zero (#929) - #931
Merged
Conversation
) cos(-u) = cos(u), sin(-u) = -sin(u) and the rest of the family were absent, so an expression that cancels exactly did not: "sin(-x) + sin(x)".Simplify() sin(-x) + sin(x) -> 0 "cos(-x) - cos(x)".Simplify() cos(-x) - cos(x) -> 0 Nothing false was asserted, so this is coverage rather than a wrong answer put right -- but an expression that is identically zero was left standing, and anything testing a residual against zero saw a non-zero residual where there was none. The one case that already folded, cos(-2 * x), folded by accident. The multiple-angle expansion fires for a coefficient of magnitude two or more, and `-x` is a coefficient of -1, which it skips -- so cos(-2 * x) worked while cos(-x) did not, and sin(-2 * x), tan(-2 * x) and abs(-2 * x) did not either, cosine being the only function that expansion happens to handle this way. A bare negation and a negative numeric coefficient are the same shape, a product with a negative real, and that is what is matched now. Even: cos, sec, abs. Odd: sin, tan, cotan, cosec, sgn. Each holds on the whole complex plane, and the poles of the odd ones sit symmetrically about zero -- tan(-z) is undefined exactly where tan(z) is -- so the domain neither widens nor narrows and no condition is owed. That has a test of its own rather than being asserted here. A cancellation between reciprocal functions keeps the condition it already had: tan(-x) + tan(x) is `0 provided not cos(x) = 0`, which is right, since it is undefined at the poles rather than zero there. A lone sin(-x) still prints as sin(-x) rather than -sin(x), and that is a tie rather than a failure: `rate::` gives both exactly 14, and a tie goes to whichever candidate was generated first. The identity is applied -- it is what makes the cancellation work -- but it does not win a comparison it was never going to win. So the tests assert the cancellation, which is what the identity is for, rather than pinning the tie-break, which would pin the tie-break. The inverse functions are deliberately absent: arcsin and arctan are odd and arccos is not, and this library's arccotan has range (-pi/2, pi/2] rather than the textbook (0, pi), so each wants measuring before it is written down. Found by `canoncheck`, the canonical-form harness added for #746 tier 1: abs(-x) vs abs(x), sin(-x) vs -sin(x) and cos(-x) vs cos(x) are three of its listed pairs and all three disagreed. Its Simplify agreement count goes 6 disagreements to 4, the two remaining being sin(-x) (the tie above) and x ^ 0, which is `1 provided not x = 0` and correct. Measured: suite 6960 passed / 0 failed; casbench 116/119 with 0 wrong, 0 error, 0 timeout; propcheck 1340 checks / 0 failures; rootcheck 596/596 clean; simpsweep 10463/10463 agree. #929 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #929.
cos(-u) = cos(u),sin(-u) = -sin(u)and the rest of the family were absent, so an expression that cancels exactly did not.Nothing false was being asserted, so this is coverage rather than a wrong answer put right — but an expression that is identically zero was left standing, and anything testing a residual against zero saw a non-zero residual where there was none.
Why only
cos(-2 * x)worked beforeBy accident. The multiple-angle expansion fires for a coefficient of magnitude two or more, and
-xis a coefficient of-1, which it skips — socos(-2 * x)folded whilecos(-x)did not, andsin(-2 * x),tan(-2 * x)andabs(-2 * x)did not either, cosine being the only function that expansion happens to handle this way.A bare negation and a negative numeric coefficient are the same shape — a product with a negative real — and that is what is matched now.
Soundness
Even:
cos,sec,abs. Odd:sin,tan,cotan,cosec,sgn.Each holds on the whole complex plane, and the poles of the odd ones sit symmetrically about zero —
tan(-z)is undefined exactly wheretan(z)is — so the domain neither widens nor narrows and no condition is owed. That has a test of its own rather than being asserted here.A cancellation between reciprocal functions keeps the condition it already had:
tan(-x) + tan(x)is0 provided not cos(x) = 0, which is right, since it is undefined at the poles rather than zero there.The inverse functions are deliberately absent.
arcsinandarctanare odd andarccosis not, and this library'sarccotanhas range(-pi/2, pi/2]rather than the textbook(0, pi), so each wants measuring before anything is written down.One thing that did not move, and why the tests are shaped as they are
A lone
sin(-x)still prints assin(-x)rather than-sin(x). That is a tie, not a failure:rate::gives both exactly 14, and a tie goes to whichever candidate was generated first. The identity is applied — it is what makes the cancellation above work — but it does not win a comparison it was never going to win.So the tests assert the cancellation, which is what the identity is for, rather than pinning the tie-break, which would pin the tie-break.
How it was found
canoncheck, the canonical-form harness added for #746 tier 1 (see #928).abs(-x)vsabs(x),sin(-x)vs-sin(x)andcos(-x)vscos(x)are three of its listed agreement pairs and all three disagreed. ItsSimplifyagreement count goes 6 disagreements to 4 — the two remaining beingsin(-x)(the tie above) andx ^ 0, which answers1 provided not x = 0and is correct.Measured
Suite 6960 passed / 0 failed (22 new); casbench 116/119 with 0 wrong, 0 error, 0 timeout; propcheck 1340 checks / 0 failures; rootcheck 596/596 clean; simpsweep 10463/10463 agree.
BREAKING-CHANGES.mdcarries the entry.🤖 Generated with Claude Code