Skip to content

Commit 3e661eb

Browse files
committed
feat(core): add symbolic equation isolation engine for algebraic loop reduction
1 parent 2e0eb57 commit 3e661eb

3 files changed

Lines changed: 445 additions & 4 deletions

File tree

packages/core/src/compiler/modelica/blt.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ModelicaRealVariable,
1010
ModelicaSimpleEquation,
1111
} from "./dae.js";
12+
import { isolateSymbolically } from "./symbolic.js";
1213

1314
/**
1415
* Visitor that collects all variable names referenced in an expression/equation.
@@ -65,6 +66,21 @@ function isExplicitlySolvableFor(eq: ModelicaEquation, v: string): boolean {
6566
return false;
6667
}
6768

69+
/**
70+
* Attempt to symbolically isolate `v` from a `ModelicaSimpleEquation`.
71+
* Returns a new explicit equation `v = expr` if successful, or null.
72+
*/
73+
function trySymbolicIsolation(eq: ModelicaEquation, v: string): ModelicaSimpleEquation | null {
74+
if (!(eq instanceof ModelicaSimpleEquation)) return null;
75+
if (!eq.expression1 || !eq.expression2) return null;
76+
77+
const result = isolateSymbolically(eq.expression1, eq.expression2, v);
78+
if (!result) return null;
79+
80+
const nameExpr = new ModelicaNameExpression(v);
81+
return new ModelicaSimpleEquation(nameExpr, result);
82+
}
83+
6884
/**
6985
* Perform a full Block Lower Triangular (BLT) Transformation on the DAE equations.
7086
*/
@@ -241,11 +257,20 @@ export function performBltTransformation(dae: ModelicaDAE): {
241257
const deps = eqDeps.get(eqIdx);
242258
if (deps?.has(v)) {
243259
// It's a single variable assigned to this equation. But is it a true loop?
244-
// If it's a structural explicit assignment (v = ...), it's not an algebraic loop unless it's v = v + 1.
260+
// If it's a structural explicit assignment (v = ...), it's not an algebraic loop.
245261
const matchingEq = equations[eqIdx];
246262
if (matchingEq && !isExplicitlySolvableFor(matchingEq, v)) {
247-
// implicit single variable loop
248-
algebraicLoops.push({ variables: scc, equations: sccEqs });
263+
// Try symbolic isolation before giving up
264+
const isolated = trySymbolicIsolation(matchingEq, v);
265+
if (isolated) {
266+
// Replace the implicit equation with the explicit one
267+
const idx = sccEqs.indexOf(matchingEq);
268+
if (idx >= 0) sccEqs[idx] = isolated;
269+
equations[eqIdx] = isolated;
270+
} else {
271+
// implicit single variable loop — cannot isolate
272+
algebraicLoops.push({ variables: scc, equations: sccEqs });
273+
}
249274
}
250275
}
251276
}

packages/core/src/compiler/modelica/symbolic-diff.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,5 +476,8 @@ export function simplifyExpr(expr: ModelicaExpression): ModelicaExpression {
476476
return expr;
477477
}
478478

479-
// Re-export for convenience
479+
// Re-export constants and builder utilities for use by symbolic.ts
480+
export { add, call, div, HALF, isOne, isZero, mul, NEG_ONE, ONE, pow, sub, TWO, ZERO };
481+
482+
// Legacy re-exports
480483
export { HALF as DIFF_HALF, NEG_ONE as DIFF_NEG_ONE, ONE as DIFF_ONE, TWO as DIFF_TWO, ZERO as DIFF_ZERO };

0 commit comments

Comments
 (0)