Skip to content

Infinite series (Sum) comparison is unreliable in three distinct ways — fine for a converging number, silently wrong for a diverging number, hangs for any function of a free variable #326

Description

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions