Skip to content

Commit ce861c2

Browse files
committed
feat: implement support for solving linear inequality systems and add corresponding tests
1 parent f8976d5 commit ce861c2

6 files changed

Lines changed: 452 additions & 36 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,26 @@
7676
// → { x: 36/7, y: -10/7 }
7777
```
7878

79+
- **Linear Inequality Systems**: The `solve()` method now handles systems of
80+
linear inequalities in 2 variables, returning the vertices of the feasible
81+
region (convex polygon). Supports all inequality operators: `<`, `<=`, `>`,
82+
`>=`.
83+
84+
```javascript
85+
// Triangle: x >= 0, y >= 0, x + y <= 10
86+
const e = ce.parse('\\begin{cases}x\\geq 0\\\\y\\geq 0\\\\x+y\\leq 10\\end{cases}');
87+
const result = e.solve(['x', 'y']);
88+
// → [{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 0, y: 10 }]
89+
90+
// Square: 0 <= x <= 5, 0 <= y <= 5
91+
const square = ce.parse('\\begin{cases}x\\geq 0\\\\x\\leq 5\\\\y\\geq 0\\\\y\\leq 5\\end{cases}');
92+
square.solve(['x', 'y']);
93+
// → [{ x: 0, y: 0 }, { x: 5, y: 0 }, { x: 5, y: 5 }, { x: 0, y: 5 }]
94+
```
95+
96+
Vertices are returned in counterclockwise convex hull order. Returns `null` for
97+
infeasible systems or non-linear constraints.
98+
7999
- **Extended Sqrt Equation Solving**: The equation solver now handles sqrt
80100
equations of the form `√(f(x)) = g(x)` by squaring both sides and solving
81101
the resulting polynomial. Extraneous roots are automatically filtered.

requirements/DONE.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,3 +1693,57 @@ e3.solve(['x', 'y']); // → null (discriminant < 0)
16931693
- `test/compute-engine/solve.test.ts` - Added 8 new tests for non-linear systems
16941694
- Updated documentation in CHANGELOG.md, doc/changelog.md, and
16951695
doc/17-guide-linear-algebra.md
1696+
1697+
---
1698+
1699+
### 30. Linear Inequality Systems ✅
1700+
1701+
**IMPLEMENTED:** The `solve()` method now handles systems of linear inequalities
1702+
in 2 variables, returning the vertices of the feasible region (convex polygon).
1703+
1704+
**Supported inequality operators:**
1705+
1706+
- `<` (Less), `<=` (LessEqual), `>` (Greater), `>=` (GreaterEqual)
1707+
1708+
**Examples that now work:**
1709+
1710+
```typescript
1711+
// Triangle: x >= 0, y >= 0, x + y <= 10
1712+
const e = ce.parse('\\begin{cases}x\\geq 0\\\\y\\geq 0\\\\x+y\\leq 10\\end{cases}');
1713+
e.solve(['x', 'y']);
1714+
// → [{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 0, y: 10 }]
1715+
1716+
// Square: 0 <= x <= 5, 0 <= y <= 5
1717+
const square = ce.parse('\\begin{cases}x\\geq 0\\\\x\\leq 5\\\\y\\geq 0\\\\y\\leq 5\\end{cases}');
1718+
square.solve(['x', 'y']);
1719+
// → [{ x: 0, y: 0 }, { x: 5, y: 0 }, { x: 5, y: 5 }, { x: 0, y: 5 }]
1720+
1721+
// Pentagon with multiple constraints
1722+
const pentagon = ce.parse('\\begin{cases}x\\geq 0\\\\y\\geq 0\\\\x\\leq 4\\\\y\\leq 4\\\\x+y\\leq 6\\end{cases}');
1723+
pentagon.solve(['x', 'y']);
1724+
// → 5 vertices in counterclockwise order
1725+
```
1726+
1727+
**Algorithm:**
1728+
1729+
1. Extract linear constraints from each inequality (normalize to `ax + by + c <= 0`)
1730+
2. Find all pairwise intersections of constraint boundary lines
1731+
3. Filter to vertices that satisfy all original constraints
1732+
4. Order vertices in counterclockwise convex hull order
1733+
5. Return as array of coordinate objects
1734+
1735+
**Limitations:**
1736+
1737+
- Only supports 2-variable systems
1738+
- Returns `null` for infeasible regions (no valid vertices)
1739+
- Returns `null` for non-linear constraints
1740+
1741+
**Files modified:**
1742+
1743+
- `src/compute-engine/boxed-expression/solve-linear-system.ts` - Added
1744+
`solveLinearInequalitySystem()` and supporting functions (`extractLinearConstraint()`,
1745+
`findLineIntersection()`, `satisfiesConstraint()`, `orderConvexHull()`)
1746+
- `src/compute-engine/boxed-expression/boxed-function.ts` - Updated `solve()` to
1747+
detect and dispatch to inequality solver
1748+
- `test/compute-engine/solve.test.ts` - Added 8 new tests for inequality systems
1749+
- `doc/17-guide-linear-algebra.md` - Added documentation for linear inequality systems

requirements/TODO.md

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -485,43 +485,9 @@ ce.diagnoseSystem(equations, variables);
485485

486486
---
487487

488-
### 30. Linear Inequality Systems
488+
### ~~30. Linear Inequality Systems~~ ✅ COMPLETED
489489

490-
**Problem:** No support for systems of linear inequalities, which define feasible
491-
regions rather than point solutions.
492-
493-
**Example:**
494-
495-
```typescript
496-
const e = ce.parse('\\begin{cases}x+y\\leq 10\\\\x\\geq 0\\\\y\\geq 0\\end{cases}');
497-
e.solve(['x', 'y']); // → currently null or error
498-
```
499-
500-
**Possible return formats:**
501-
502-
1. **Vertices of feasible region:** `[(0,0), (10,0), (0,10)]`
503-
2. **Parametric description:** `{ x: [0, 10], y: [0, 10-x] }`
504-
3. **Constraint set:** Keep as symbolic representation
505-
506-
**Use cases:**
507-
508-
- Linear programming feasibility
509-
- Constraint satisfaction
510-
- Geometric computations (polygon vertices)
511-
512-
**Algorithm (2D vertex enumeration):**
513-
514-
1. Convert inequalities to equalities (boundary lines)
515-
2. Find all pairwise intersections
516-
3. Filter to points satisfying all inequalities
517-
4. Order vertices (convex hull)
518-
519-
**Complexity:** This is significantly more complex than equality systems. May
520-
want to start with 2-variable case only.
521-
522-
**Files:**
523-
524-
- `src/compute-engine/boxed-expression/solve-linear-system.ts` (or new file)
490+
See `requirements/DONE.md` for implementation details.
525491

526492
---
527493

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { findUnivariateRoots } from './solve';
3939
import {
4040
solveLinearSystem,
4141
solvePolynomialSystem,
42+
solveLinearInequalitySystem,
4243
} from './solve-linear-system';
4344
import { replace } from './rules';
4445
import { negate } from './negate';
@@ -927,6 +928,19 @@ export class BoxedFunction extends _BoxedExpression {
927928
const polyResult = solvePolynomialSystem([...equations], varNames);
928929
if (polyResult) return polyResult;
929930
}
931+
932+
// Check for inequality systems (Less, LessEqual, Greater, GreaterEqual)
933+
const inequalityOps = ['Less', 'LessEqual', 'Greater', 'GreaterEqual'];
934+
if (
935+
equations &&
936+
equations.every((eq) => inequalityOps.includes(eq.operator ?? ''))
937+
) {
938+
const inequalityResult = solveLinearInequalitySystem(
939+
[...equations],
940+
varNames
941+
);
942+
if (inequalityResult) return inequalityResult;
943+
}
930944
}
931945

932946
// Existing univariate solving

0 commit comments

Comments
 (0)