[Interactive Graph] Update tangent notes for AI shared context#3333
[Interactive Graph] Update tangent notes for AI shared context#3333
Conversation
🗄️ Schema Change: No Changes ✅ |
🛠️ Item Splitting: No Changes ✅ |
|
Size Change: 0 B Total Size: 486 kB ℹ️ View Unchanged
|
npm Snapshot: PublishedGood news!! We've packaged up the latest commit from this PR (7f54f17) and published it to npm. You Example: pnpm add @khanacademy/perseus@PR3333If you are working in Khan Academy's frontend, you can run the below command. ./dev/tools/bump_perseus_version.ts -t PR3333If you are working in Khan Academy's webapp, you can run the below command. ./dev/tools/bump_perseus_version.js -t PR3333 |
There was a problem hiding this comment.
LGTM — documentation-only update with no code changes.
Extended reasoning...
Overview
This PR modifies only two files: an empty changeset (khaki-terms-brush.md) and the tangent notes markdown file (tangent.md). All changes are documentation corrections that improve accuracy around the canonical form normalization for tangent coefficients — specifically clarifying that only b > 0 can be guaranteed (not both a > 0 and b > 0), adding descriptions of what p1 and p2 represent, and noting that coefficient computation delegates to kmath.
Security risks
None. This PR contains zero code changes — only markdown documentation updates in an AI shared context notes file.
Level of scrutiny
Low scrutiny appropriate. This is a documentation-only change with no runtime impact. The content corrections are mathematically accurate and internally consistent throughout the document (the canonical form discussion at lines 113-116, the scoring section, and the decisions section all agree).
Other factors
The one bug reported is a valid nit: line 50 says the grapher-util.ts copy is "kept in sync with the kmath version", which is slightly misleading since the two implementations use different normalization strategies. This is a minor documentation wording issue that does not block approval — the rest of the PR actually documents this difference correctly in multiple other places. No outstanding reviewer comments exist, and no human reviews have been submitted yet.
| The Grapher widget already has a tangent graph type that served as the mathematical reference: | ||
|
|
||
| - `packages/perseus-core/src/utils/grapher-util.ts` — Tangent coefficient computation (`getTangentCoefficients`) and equation string generation. Uses the same `f(x) = a * tan(b*x - c) + d` model. Also contains the `"sin("` → `"tan("` label bug (see [Related: Grapher Widget Bug](#related-grapher-widget-bug)). | ||
| - `packages/perseus-core/src/utils/grapher-util.ts` — Tangent coefficient computation (`getTangentCoefficients`), equation string generation, and its own copy of `canonicalTangentCoefficients()` (kept in sync with the kmath version). Uses the same `f(x) = a * tan(b*x - c) + d` model. Also contains the `"sin("` → `"tan("` label bug (see [Related: Grapher Widget Bug](#related-grapher-widget-bug)). |
There was a problem hiding this comment.
🟡 The claim that grapher-util.ts's canonicalTangentCoefficients() is "kept in sync with the kmath version" (line 50) is inaccurate. The grapher-util.ts version (lines 58-91) guarantees both a > 0 and b > 0 using a phase += period/2 step, while this same PR's documentation elsewhere correctly states the kmath version can only guarantee b > 0 using the odd function identity. Consider rewording to note that the grapher-util.ts copy uses a different (and mathematically questionable) normalization strategy.
Extended reasoning...
What the bug is
Line 50 of the updated tangent.md introduces the phrase: grapher-util.ts has "its own copy of canonicalTangentCoefficients() (kept in sync with the kmath version)". This claim is internally inconsistent with the rest of the documentation added by this same PR.
The inconsistency in detail
The grapher-util.ts canonicalTangentCoefficients() (lines 58-91) implements a two-step normalization:
- Guarantee
a > 0: flips signs ofamplitude,angularFrequency, andphase - Guarantee
b > 0: flipsangularFrequencyandphase, then doesphase += period/2
Meanwhile, the PR's own documentation at lines 113-116 describes the kmath version as using a fundamentally different approach:
- Guarantee
b > 0using the odd function identity:a * tan(-|b|x - c) = (-a) * tan(|b|x - (-c)), which flips signs ofaandc - Explicitly notes: "unlike sine... tangent has no such half-period identity, so only
b > 0can be guaranteed"
These are different algorithms with different guarantees (a > 0 AND b > 0 vs only b > 0).
Why the grapher-util.ts approach is mathematically questionable
The grapher-util.ts version uses phase += period/2 (i.e., phase += π/2) to guarantee b > 0. This step implicitly relies on tan(x + π/2) = -tan(x), but that identity is incorrect: tan(x + π/2) = -cot(x), not -tan(x). The sine version works because sin(x + π) = -sin(x) is a valid identity. The tangent version appears to be a copy-paste from the sine normalization with only the period changed from 2π to π, without accounting for the different trigonometric identities.
Step-by-step proof of the discrepancy
Consider f(x) = -1 * tan(-2x - 1) + 0 (a = -1, b = -2, c = 1, d = 0).
grapher-util.ts algorithm:
- a < 0, so flip: a = 1, b = 2, c = -1
- b > 0 already, skip step 2
- Normalize c: c = -1, add π → c ≈ 2.14
- Result:
tan(2x - 2.14)with a = 1, b = 2
kmath algorithm (as documented):
- b < 0, so apply odd identity: a = -(-1) = 1, b = 2, c = -(-1) = -1 (wait, let me redo: flips signs of a and c)
Actually: a * tan(-|b|x - c) = (-a) * tan(|b|x + c), so a becomes 1, c becomes -1, b becomes 2 - Normalize c: c = -1, add π → c ≈ 2.14
- Result:
1 * tan(2x - 2.14)with a = 1, b = 2
In this case they agree, but the key difference is that the grapher-util.ts version always guarantees a > 0 as an independent first step, while the kmath version does not have this guarantee. For cases where a < 0 and b > 0, the grapher-util.ts version will flip a positive while also flipping b negative, then use the questionable phase shift to fix b. The kmath version would leave a negative since b is already positive.
Impact
This is a documentation-only issue in an AI shared context notes file. The "kept in sync" claim could mislead developers or AI assistants into thinking the two implementations are interchangeable or identical, when they are not. Since this PR is specifically updating these notes for accuracy, the claim should be corrected.
Suggested fix
Replace "kept in sync with the kmath version" with something like "uses a different normalization algorithm that guarantees both a > 0 and b > 0, unlike the kmath version which only guarantees b > 0".
| The Grapher widget already has a tangent graph type that served as the mathematical reference: | ||
|
|
||
| - `packages/perseus-core/src/utils/grapher-util.ts` — Tangent coefficient computation (`getTangentCoefficients`) and equation string generation. Uses the same `f(x) = a * tan(b*x - c) + d` model. Also contains the `"sin("` → `"tan("` label bug (see [Related: Grapher Widget Bug](#related-grapher-widget-bug)). | ||
| - `packages/perseus-core/src/utils/grapher-util.ts` — Tangent coefficient computation (`getTangentCoefficients`), equation string generation, and its own copy of `canonicalTangentCoefficients()` that uses a different normalization strategy than the kmath version: it guarantees both `a > 0` and `b > 0` by using a `phase += period/2` step, whereas the kmath version only guarantees `b > 0` via the odd function identity (see [Canonical Normalization](#canonical-normalization) for details). Uses the same `f(x) = a * tan(b*x - c) + d` model. Also contains the `"sin("` → `"tan("` label bug (see [Related: Grapher Widget Bug](#related-grapher-widget-bug)). |
There was a problem hiding this comment.
We definitely shouldn't have two different versions of a function called canonicalTangentCoefficients because that would be an oxymoron 😆 Do we need to update the kmath function? I don't understand why the math would be different.
There was a problem hiding this comment.
For this section, References > Grapher Widget (Legacy Tangent)
this part here is just mentioning what logic we currently have in the Grapher widget.
But since we will be deprecating the Grapher widget in the near future, the proper place to have the logic will be in packages/kmath/src/geometry.ts This is following the pattern we have for sin:
- canonicalSineCoefficients in grapher-utils
- canonicalSineCoefficients in geometry
There was a problem hiding this comment.
The other option I can think is to move all the logic in kmath, and update the Grapher logic to use it. What do you think?
Update: Thinking more about this during current implementation, IMO it will be best to not touch the grapher-util.ts since this is currently a private function used within the file and additionally will be deprecated anyways. Moving forward anything related to tangent will use the function in geometry.ts this also follows the pattern for sin.
Summary:
This PR updates the existing tangent notes, to reflect recent fixes in the POC PR.
Issue: LEMS-3937
Test plan: