Skip to content

Commit b0f4733

Browse files
committed
fix: fixed #252
1 parent 0814581 commit b0f4733

2 files changed

Lines changed: 38 additions & 49 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
`\forall x, x>y`). The body of the quantified expression is now correctly
1818
serialized.
1919

20+
- **([#252](https://github.com/cortex-js/compute-engine/issues/252))
21+
Sum/Product**: Fixed `Sum` and `Product` returning `NaN` when the body
22+
contains free variables (variables not bound by the index). For example,
23+
`\sum_{n=1}^{10}(x)` now correctly evaluates to `10x` instead of `NaN`, and
24+
`\prod_{n=1}^{5}(x)` evaluates to `x^5`. Mixed expressions like
25+
`\sum_{n=1}^{10}(n \cdot x)` now return `55x`.
26+
2027
## 0.31.0 _2026-01-27_
2128

2229
### Breaking Changes

src/compute-engine/library/arithmetic.ts

Lines changed: 31 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,41 +1475,30 @@ export const ARITHMETIC_LIBRARY: SymbolDefinitions[] = [
14751475
canonicalBigop('Product', body, bounds, scope),
14761476

14771477
evaluate: (ops, options) => {
1478-
const fn = (acc, x) => {
1479-
x = x.evaluate(options);
1480-
return x.isNumberLiteral ? acc.mul(x.numericValue!) : null;
1481-
};
1482-
14831478
const result = run(
14841479
reduceBigOp(
14851480
ops[0],
14861481
ops.slice(1),
1487-
fn,
1488-
options.engine._numericValue(1)
1482+
(acc: BoxedExpression, x) => acc.mul(x.evaluate(options)),
1483+
options.engine.One
14891484
),
14901485
options.engine._timeRemaining
14911486
);
1492-
return options.engine.number(result ?? NaN);
1487+
return result ?? options.engine.NaN;
14931488
},
14941489

14951490
evaluateAsync: async (ops, options) => {
1496-
const fn = (acc, x) => {
1497-
x = x.evaluate(options);
1498-
if (!x.isNumberLiteral) return null;
1499-
return acc.mul(x.numericValue!);
1500-
};
1501-
15021491
const result = await runAsync(
15031492
reduceBigOp(
15041493
ops[0],
15051494
ops.slice(1),
1506-
fn,
1507-
options.engine._numericValue(1)
1495+
(acc: BoxedExpression, x) => acc.mul(x.evaluate(options)),
1496+
options.engine.One
15081497
),
15091498
options.engine._timeRemaining,
15101499
options.signal
15111500
);
1512-
return options.engine.number(result ?? NaN);
1501+
return result ?? options.engine.NaN;
15131502
},
15141503
},
15151504

@@ -1526,39 +1515,32 @@ export const ARITHMETIC_LIBRARY: SymbolDefinitions[] = [
15261515
canonical: ([body, ...bounds], { scope }) =>
15271516
canonicalBigop('Sum', body, bounds, scope),
15281517

1529-
evaluate: ([fn, ...indexes], { engine }) =>
1530-
engine.number(
1531-
run(
1532-
reduceBigOp(
1533-
fn,
1534-
indexes,
1535-
(acc, x) => {
1536-
x = x.evaluate();
1537-
return x.isNumberLiteral ? acc.add(x.numericValue!) : null;
1538-
},
1539-
engine._numericValue(0)
1540-
),
1541-
engine._timeRemaining
1542-
)
1543-
),
1518+
evaluate: ([body, ...indexes], { engine }) => {
1519+
const result = run(
1520+
reduceBigOp(
1521+
body,
1522+
indexes,
1523+
(acc: BoxedExpression, x) => acc.add(x.evaluate()),
1524+
engine.Zero
1525+
),
1526+
engine._timeRemaining
1527+
);
1528+
return result ?? engine.NaN;
1529+
},
15441530

1545-
evaluateAsync: async (xs, { engine, signal }) =>
1546-
engine.number(
1547-
await runAsync(
1548-
reduceBigOp(
1549-
xs[0],
1550-
xs.slice(1),
1551-
(acc, x) => {
1552-
x = x.evaluate();
1553-
if (!x.isNumberLiteral) return null;
1554-
return acc.add(x.numericValue!);
1555-
},
1556-
engine._numericValue(0)
1557-
),
1558-
engine._timeRemaining,
1559-
signal
1560-
)
1561-
),
1531+
evaluateAsync: async (xs, { engine, signal }) => {
1532+
const result = await runAsync(
1533+
reduceBigOp(
1534+
xs[0],
1535+
xs.slice(1),
1536+
(acc: BoxedExpression, x) => acc.add(x.evaluate()),
1537+
engine.Zero
1538+
),
1539+
engine._timeRemaining,
1540+
signal
1541+
);
1542+
return result ?? engine.NaN;
1543+
},
15621544
},
15631545
},
15641546
];

0 commit comments

Comments
 (0)