Environment: @cortex-js/compute-engine 0.98.0, Node 20.11.1
Summary: Working on an auto-grader for AP Calculus BC, where infinite series are a core topic — Taylor/Maclaurin series (power series, a function of x), and convergence/divergence tests (numeric series). Comparing two Sum expressions with isEqual() behaves correctly in exactly one of three common cases, and fails in two different ways in the others:
1. Numeric, convergent series — works correctly:
import { ComputeEngine } from '@cortex-js/compute-engine';
const ce = new ComputeEngine();
const geo = ce.parse('\\sum_{n=0}^{\\infty} \\left(\\frac{1}{2}\\right)^n'); // no free variables
geo.isEqual(ce.parse('2')); // true, 8ms -- correct, series sums to 2
geo.isEqual(ce.parse('3')); // false, 20ms -- correctly rejects a wrong value
2. Numeric, divergent series — silently wrong, not even flagged:
const harmonic = ce.parse('\\sum_{n=1}^{\\infty} \\frac{1}{n}'); // diverges to +infinity
harmonic.N(); // 9.787706026045382... (301ms) -- a finite number!
harmonic.isEqual(ce.parse('\\infty')); // false -- WRONG, this series genuinely diverges
harmonic.isEqual(ce.parse('9.787706026045382')); // true -- confidently "confirms" its own truncation
It's silently truncating at some fixed iteration count and treating the partial sum as the true value — no error, no "diverges" result, no indication anything is unreliable. This is arguably worse than an outright failure: a caller has no signal to distrust the answer.
3. Any series with a free variable — hangs indefinitely, even with withTimeLimit:
const a = ce.parse('\\sum_{n=0}^{\\infty} x^n');
const b = ce.parse('\\sum_{n=1}^{\\infty} x^{n-1}'); // the SAME series, index shifted by 1
a.isEqual(a); // true, <1ms -- fast path for structural identity
a.isEqual(b); // never returns -- yet these are term-for-term identical
a.isEqual(ce.parse('2')); // never returns, even against an unrelated constant
ce.withTimeLimit({ ms: 5000 }, () => a.isEqual(b)); // still hangs past the deadline
The two series above are term-for-term identical (n=0 term is 1 either way, n=1 term is x either way, ...) — not a case requiring deep symbolic reasoning, just a one-step reindex. Only literal structural identity resolves fast; anything else involving a free variable hangs, confirmed past 5 minutes wall-clock.
(For reference, withTimeLimit's ms really is milliseconds and does work for other slow operations — ce.withTimeLimit({ ms: 300 }, () => ce.box(['Factorial', ['Factorial', 700]]).N()) throws Timeout exceeded (299ms) right on schedule. So this isn't "timeouts don't work here" generally — the Sum/isEqual case specifically doesn't respect it.)
Why this matters as one report, not three: these are all facets of the same underlying gap — Sum isn't actually evaluated with real convergence/divergence analysis or genuine symbolic reasoning about the general term; it's numerically approximated with no way to detect when that approximation is meaningless (case 2), and falls back to something that never terminates the moment a closed numeric answer isn't available (case 3). For a domain like AP Calculus BC where infinite series are a core topic, this makes Sum comparison unreliable for most of the actual use cases that come up.
Expected: a true/false/undefined verdict in every case (or CancellationError under a time limit for genuinely expensive cases) — the same reliability already seen in case 1.
Context: worked around case 3 by extracting each side's summand and comparing that directly instead of ever calling isEqual on the Sum node. Haven't found a safe workaround for case 2 yet — happy to share more of our test corpus if useful. Possibly related to #293 (timeLimit not interrupting heavy operations).
Environment:
@cortex-js/compute-engine0.98.0, Node 20.11.1Summary: Working on an auto-grader for AP Calculus BC, where infinite series are a core topic — Taylor/Maclaurin series (power series, a function of
x), and convergence/divergence tests (numeric series). Comparing twoSumexpressions withisEqual()behaves correctly in exactly one of three common cases, and fails in two different ways in the others:1. Numeric, convergent series — works correctly:
2. Numeric, divergent series — silently wrong, not even flagged:
It's silently truncating at some fixed iteration count and treating the partial sum as the true value — no error, no "diverges" result, no indication anything is unreliable. This is arguably worse than an outright failure: a caller has no signal to distrust the answer.
3. Any series with a free variable — hangs indefinitely, even with
withTimeLimit:The two series above are term-for-term identical (n=0 term is 1 either way, n=1 term is x either way, ...) — not a case requiring deep symbolic reasoning, just a one-step reindex. Only literal structural identity resolves fast; anything else involving a free variable hangs, confirmed past 5 minutes wall-clock.
(For reference,
withTimeLimit'smsreally is milliseconds and does work for other slow operations —ce.withTimeLimit({ ms: 300 }, () => ce.box(['Factorial', ['Factorial', 700]]).N())throwsTimeout exceeded (299ms)right on schedule. So this isn't "timeouts don't work here" generally — theSum/isEqualcase specifically doesn't respect it.)Why this matters as one report, not three: these are all facets of the same underlying gap —
Sumisn't actually evaluated with real convergence/divergence analysis or genuine symbolic reasoning about the general term; it's numerically approximated with no way to detect when that approximation is meaningless (case 2), and falls back to something that never terminates the moment a closed numeric answer isn't available (case 3). For a domain like AP Calculus BC where infinite series are a core topic, this makesSumcomparison unreliable for most of the actual use cases that come up.Expected: a
true/false/undefinedverdict in every case (orCancellationErrorunder a time limit for genuinely expensive cases) — the same reliability already seen in case 1.Context: worked around case 3 by extracting each side's summand and comparing that directly instead of ever calling
isEqualon theSumnode. Haven't found a safe workaround for case 2 yet — happy to share more of our test corpus if useful. Possibly related to #293 (timeLimitnot interrupting heavy operations).