Skip to content

Commit 9fb5c56

Browse files
committed
feat: add support for solving systems of linear equations and enhance solve method. fix #189
1 parent 870fefb commit 9fb5c56

7 files changed

Lines changed: 535 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22

33
### Improvements
44

5+
- **Systems of Linear Equations**: The `solve()` method now handles systems of
6+
linear equations parsed from LaTeX `\begin{cases}...\end{cases}` environments.
7+
Returns an object mapping variable names to their solutions.
8+
9+
```javascript
10+
const e = ce.parse('\\begin{cases}x+y=70\\\\2x-4y=80\\end{cases}');
11+
const result = e.solve(['x', 'y']);
12+
console.log(result.x.json); // 60
13+
console.log(result.y.json); // 10
14+
15+
// 3x3 systems work too
16+
const e2 = ce.parse('\\begin{cases}x+y+z=6\\\\2x+y-z=1\\\\x-y+2z=5\\end{cases}');
17+
const result2 = e2.solve(['x', 'y', 'z']);
18+
// → { x: 1, y: 2, z: 3 }
19+
```
20+
21+
Non-linear systems (containing `xy`, ``, etc.) and inconsistent systems
22+
return `null`.
23+
524
- **Extended Sqrt Equation Solving**: The equation solver now handles sqrt
625
equations of the form `√(f(x)) = g(x)` by squaring both sides and solving
726
the resulting polynomial. Extraneous roots are automatically filtered.

src/compute-engine/boxed-expression/abstract-boxed-expression.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ export abstract class _BoxedExpression implements BoxedExpression {
622622
| string
623623
| BoxedExpression
624624
| Iterable<BoxedExpression>
625-
): null | ReadonlyArray<BoxedExpression> {
625+
): null | ReadonlyArray<BoxedExpression> | Record<string, BoxedExpression> {
626626
return null;
627627
}
628628

src/compute-engine/boxed-expression/boxed-function.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
import { NumericValue } from '../numeric-value/types';
3737

3838
import { findUnivariateRoots } from './solve';
39+
import { solveLinearSystem } from './solve-linear-system';
3940
import { replace } from './rules';
4041
import { negate } from './negate';
4142
import { Product } from './product';
@@ -904,8 +905,18 @@ export class BoxedFunction extends _BoxedExpression {
904905
| string
905906
| BoxedExpression
906907
| Iterable<BoxedExpression>
907-
): null | ReadonlyArray<BoxedExpression> {
908+
): null | ReadonlyArray<BoxedExpression> | Record<string, BoxedExpression> {
908909
const varNames = normalizedUnknownsForSolve(vars ?? this.unknowns);
910+
911+
// Handle List of equations (system of equations)
912+
if (this.operator === 'List') {
913+
const equations = this.ops;
914+
if (equations && equations.every((eq) => eq.operator === 'Equal')) {
915+
return solveLinearSystem([...equations], varNames);
916+
}
917+
}
918+
919+
// Existing univariate solving
909920
if (varNames.length !== 1) return null;
910921
return findUnivariateRoots(this, varNames[0]);
911922
}

src/compute-engine/boxed-expression/boxed-tensor.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,18 @@ export class BoxedTensor<T extends TensorDataType> extends _BoxedExpression {
359359
if (this._tensor && this._tensor.dtype !== 'expression') return this;
360360
return this.structural.N();
361361
}
362+
363+
solve(
364+
vars?:
365+
| Iterable<string>
366+
| string
367+
| BoxedExpression
368+
| Iterable<BoxedExpression>
369+
): null | ReadonlyArray<BoxedExpression> | Record<string, BoxedExpression> {
370+
// Delegate to the structural expression which may be a BoxedFunction
371+
// that can handle system of equations
372+
return this.structural.solve(vars);
373+
}
362374
}
363375

364376
export function isBoxedTensor(val: unknown): val is BoxedTensor<any> {

0 commit comments

Comments
 (0)