Skip to content

Commit c1aea53

Browse files
committed
feat: WGSL compilation target
1 parent ff9b357 commit c1aea53

10 files changed

Lines changed: 973 additions & 409 deletions

File tree

CHANGELOG.md

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ the introduction of type-guarded role interfaces, which improves type safety and
99
API ergonomics but requires updates to code that accessed role-specific
1010
properties directly on expression instances.
1111

12+
See
13+
[`MIGRATION_GUIDE_0.50.0.md`](https://github.com/cortex-js/compute-engine/blob/main/MIGRATION_GUIDE_0.50.0.md)
14+
for details.
15+
1216
#### Naming Alignment: `Expression`, `MathJsonExpression`, and `ExpressionInput`
1317

1418
- The compute-engine runtime type is now `Expression` (preferred name).
@@ -66,19 +70,19 @@ ce.box(['Add', 1, 'x'], { form: ['Number', 'Order'] }); // selective passes
6670
Top-level free functions are now available for common operations and use a
6771
shared `ComputeEngine` instance created on first call.
6872

69-
| Function | Purpose |
70-
| :-- | :-- |
71-
| `getDefaultEngine()` | Return the shared default `ComputeEngine` instance. |
72-
| `parse(latex)` | Parse a LaTeX string into an `Expression`. |
73-
| `simplify(exprOrLatex)` | Simplify an expression or LaTeX input. |
74-
| `evaluate(exprOrLatex)` | Evaluate an expression or LaTeX input symbolically. |
75-
| `N(exprOrLatex)` | Numerically evaluate an expression or LaTeX input. |
76-
| `assign(id, value)` / `assign(record)` | Assign one symbol value or many at once. |
77-
| `expand(exprOrLatex)` | Expand distributively at the top level (`Expression \| null`). |
78-
| `expandAll(exprOrLatex)` | Expand distributively recursively (`Expression \| null`). |
79-
| `solve(exprOrLatex, vars?)` | Solve equations/systems (returns solve result variants). |
80-
| `factor(exprOrLatex)` | Factor an expression. |
81-
| `compile(exprOrLatex, options?)` | Compile to a target language with `CompilationResult`. |
73+
| Function | Purpose |
74+
| :------------------------------------- | :------------------------------------------------------------- |
75+
| `getDefaultEngine()` | Return the shared default `ComputeEngine` instance. |
76+
| `parse(latex)` | Parse a LaTeX string into an `Expression`. |
77+
| `simplify(exprOrLatex)` | Simplify an expression or LaTeX input. |
78+
| `evaluate(exprOrLatex)` | Evaluate an expression or LaTeX input symbolically. |
79+
| `N(exprOrLatex)` | Numerically evaluate an expression or LaTeX input. |
80+
| `assign(id, value)` / `assign(record)` | Assign one symbol value or many at once. |
81+
| `expand(exprOrLatex)` | Expand distributively at the top level (`Expression \| null`). |
82+
| `expandAll(exprOrLatex)` | Expand distributively recursively (`Expression \| null`). |
83+
| `solve(exprOrLatex, vars?)` | Solve equations/systems (returns solve result variants). |
84+
| `factor(exprOrLatex)` | Factor an expression. |
85+
| `compile(exprOrLatex, options?)` | Compile to a target language with `CompilationResult`. |
8286

8387
```ts
8488
import {
@@ -103,19 +107,19 @@ factor('(2x)(4y)'); // 8xy
103107
compile('x^2 + 1').run({ x: 3 }); // 10
104108
```
105109

106-
Except for `parse()`, `assign()`, and `getDefaultEngine()`, these free
107-
functions accept either a LaTeX string or an existing `Expression`.
110+
Except for `parse()`, `assign()`, and `getDefaultEngine()`, these free functions
111+
accept either a LaTeX string or an existing `Expression`.
108112

109113
#### Free Function Notes
110114

111115
- `compile()` is now a top-level entry point returning `CompilationResult`.
112-
Custom compilation targets are managed with
113-
`ce.registerCompilationTarget()` and `ce.unregisterCompilationTarget()`.
116+
Custom compilation targets are managed with `ce.registerCompilationTarget()`
117+
and `ce.unregisterCompilationTarget()`.
114118
- `expand()` and `expandAll()` return `null` when an expression is not
115119
expandable.
116120
- `solve()` is available as a top-level wrapper over equation/system solving.
117-
- `factor()` is the top-level factoring entry point. Specialized helpers such
118-
as `factorPolynomial()` and `factorQuadratic()` remain expression-only APIs.
121+
- `factor()` is the top-level factoring entry point. Specialized helpers such as
122+
`factorPolynomial()` and `factorQuadratic()` remain expression-only APIs.
119123

120124
#### `trigSimplify()` Method Removed
121125

@@ -391,6 +395,25 @@ ce.simplificationRules.push({
391395
`sinh/cosh → exp`, `arsinh/arcosh/artanh → ln`, and `arcsin → arctan2` that
392396
prevented abs/odd-function rules from firing.
393397

398+
### Compilation
399+
400+
- **WGSL (WebGPU Shading Language) Compilation Target**: New built-in WGSL
401+
target for compiling mathematical expressions to WebGPU shaders.
402+
403+
```ts
404+
// Via the registry
405+
const result = compile(expr, { to: 'wgsl' });
406+
```
407+
408+
WGSL-specific differences from GLSL:
409+
- `inverseSqrt` (camelCase) instead of `inversesqrt`
410+
- `%` operator for mod instead of `mod()` function
411+
- `vec2f`/`vec3f`/`vec4f` constructors instead of `vec2`/`vec3`/`vec4`
412+
- `array<f32, n>()` instead of `float[n]()`
413+
- `fn name(x: f32) -> f32` instead of `float name(float x)`
414+
- `@vertex`/`@fragment`/`@compute` entry points with struct-based I/O
415+
- `@group`/`@binding` uniform declarations and `@workgroup_size` for compute
416+
394417
### Bug Fixes
395418

396419
- **`Sequence` type inference now returns a proper tuple type**: Multi-argument
@@ -475,12 +498,13 @@ ce.simplificationRules.push({
475498
validation to fail.
476499

477500
- **Assign to compound symbol names no longer misinterpreted as sequence
478-
definitions** (fixes [#286](https://github.com/cortex-js/compute-engine/issues/286)):
501+
definitions** (fixes
502+
[#286](https://github.com/cortex-js/compute-engine/issues/286)):
479503
`ce.box(["Assign", "t_half", 10])` previously failed because the Assign
480504
evaluate handler split any symbol containing `_` and treated it as a
481-
subscripted sequence definition. User-provided compound symbols like
482-
`t_half` or `half_life` are now assigned correctly. Sequence definitions
483-
via parsed LaTeX (e.g., `L_0 := 1`) continue to work as before.
505+
subscripted sequence definition. User-provided compound symbols like `t_half`
506+
or `half_life` are now assigned correctly. Sequence definitions via parsed
507+
LaTeX (e.g., `L_0 := 1`) continue to work as before.
484508

485509
## 0.35.6 _2026-02-07_
486510

Lines changed: 69 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# API Transition Guide
1+
# Migration Guide to Compute Engine 0.50.0
22

33
This guide covers the breaking changes introduced in the latest architecture
4-
revision of `@cortex-js/compute-engine`. Each section shows the old API, the
5-
new API, and a brief rationale.
4+
revision of `@cortex-js/compute-engine`. Each section shows the old API, the new
5+
API, and a brief rationale.
66

77
---
88

@@ -67,23 +67,22 @@ type FormOption =
6767
## 2. Role-Specific Properties Moved to Role Interfaces
6868

6969
Properties that were previously available on all `Expression` instances
70-
(returning `null` or `undefined` when not applicable) have been removed from
71-
the base interface. They are now only accessible after narrowing with a type
72-
guard.
70+
(returning `null` or `undefined` when not applicable) have been removed from the
71+
base interface. They are now only accessible after narrowing with a type guard.
7372

7473
### Removed from `Expression`
7574

76-
| Property | Access via |
77-
|:-------------------|:-------------------------------------------------|
78-
| `.symbol` | `isSymbol(expr)` then `expr.symbol` |
79-
| `.string` | `isString(expr)` then `expr.string` |
80-
| `.ops` | `isFunction(expr)` then `expr.ops` |
81-
| `.nops` | `isFunction(expr)` then `expr.nops` |
82-
| `.op1`/`.op2`/`.op3` | `isFunction(expr)` then `expr.op1` etc. |
75+
| Property | Access via |
76+
| :---------------------- | :-------------------------------------------------- |
77+
| `.symbol` | `isSymbol(expr)` then `expr.symbol` |
78+
| `.string` | `isString(expr)` then `expr.string` |
79+
| `.ops` | `isFunction(expr)` then `expr.ops` |
80+
| `.nops` | `isFunction(expr)` then `expr.nops` |
81+
| `.op1`/`.op2`/`.op3` | `isFunction(expr)` then `expr.op1` etc. |
8382
| `.isFunctionExpression` | `isFunction(expr)` then `expr.isFunctionExpression` |
84-
| `.numericValue` | `isNumber(expr)` then `expr.numericValue` |
85-
| `.isNumberLiteral` | `isNumber(expr)` then `expr.isNumberLiteral` |
86-
| `.tensor` | `isTensor(expr)` then `expr.tensor` |
83+
| `.numericValue` | `isNumber(expr)` then `expr.numericValue` |
84+
| `.isNumberLiteral` | `isNumber(expr)` then `expr.isNumberLiteral` |
85+
| `.tensor` | `isTensor(expr)` then `expr.tensor` |
8786

8887
### Before
8988

@@ -120,16 +119,18 @@ if (sym(expr) === 'Pi') {
120119

121120
See Section 6 for the full list of type guards and role interfaces.
122121

123-
**Note:** The `sym()` helper combines `isSymbol()` check with symbol name access,
124-
making simple symbol comparisons more concise.
122+
**Note:** The `sym()` helper combines `isSymbol()` check with symbol name
123+
access, making simple symbol comparisons more concise.
125124

126125
### Still on `Expression`
127126

128127
- `.re` / `.im` — typed `number`, return `NaN` when not applicable
129128
- `.shape` — typed `number[]`, returns `[]` for scalars
130129
- `.operator` — returns the operator name for all expression types
131-
- All arithmetic methods (`.add()`, `.mul()`, etc.) — work symbolically on all expressions
132-
- All numeric predicates (`.isPositive`, `.isInteger`, etc.) — meaningful with assumptions
130+
- All arithmetic methods (`.add()`, `.mul()`, etc.) — work symbolically on all
131+
expressions
132+
- All numeric predicates (`.isPositive`, `.isInteger`, etc.) — meaningful with
133+
assumptions
133134

134135
---
135136

@@ -213,16 +214,16 @@ console.log(expanded.latex); // "x^2+3x+2"
213214
```
214215

215216
> **Note**: `expand()` returns `null` if the expression cannot be expanded.
216-
> Handle this with `expand(expr) ?? expr` if you want the original expression
217-
> as a fallback.
217+
> Handle this with `expand(expr) ?? expr` if you want the original expression as
218+
> a fallback.
218219
219220
---
220221

221222
## 5. Library System
222223

223-
The constructor now accepts a `libraries` option for controlling which
224-
libraries are loaded. Libraries declare their dependencies explicitly and are
225-
loaded in topological order.
224+
The constructor now accepts a `libraries` option for controlling which libraries
225+
are loaded. Libraries declare their dependencies explicitly and are loaded in
226+
topological order.
226227

227228
### Before
228229

@@ -362,25 +363,25 @@ const name = sym(expr); // string | undefined
362363

363364
### Role Interfaces
364365

365-
| Guard | Narrows to |
366-
|:-------------------|:-------------------------------------------|
367-
| `isNumber` | `Expression & NumberLiteralInterface` |
368-
| `isSymbol` | `Expression & SymbolInterface` |
369-
| `isFunction` | `Expression & FunctionInterface` |
370-
| `isString` | `Expression & StringInterface` |
371-
| `isTensor` | `Expression & TensorInterface` |
372-
| `isDictionary` | `Expression & DictionaryInterface` |
373-
| `isCollection` | `Expression & CollectionInterface` |
366+
| Guard | Narrows to |
367+
| :-------------------- | :---------------------------------------- |
368+
| `isNumber` | `Expression & NumberLiteralInterface` |
369+
| `isSymbol` | `Expression & SymbolInterface` |
370+
| `isFunction` | `Expression & FunctionInterface` |
371+
| `isString` | `Expression & StringInterface` |
372+
| `isTensor` | `Expression & TensorInterface` |
373+
| `isDictionary` | `Expression & DictionaryInterface` |
374+
| `isCollection` | `Expression & CollectionInterface` |
374375
| `isIndexedCollection` | `Expression & IndexedCollectionInterface` |
375-
| `isExpression` | `Expression` (from `unknown`) |
376+
| `isExpression` | `Expression` (from `unknown`) |
376377

377378
---
378379

379380
## 7. Compilation Targets
380381

381382
Custom compilation targets can now be registered and unregistered dynamically.
382-
Built-in targets (`'javascript'`, `'glsl'`, `'python'`, `'interval-javascript'`,
383-
`'interval-glsl'`) are pre-registered.
383+
Built-in targets (`'javascript'`, `'glsl'`, 'wgsl', `'python'`,
384+
`'interval-javascript'`, `'interval-glsl'`) are pre-registered.
384385

385386
### Before
386387

@@ -470,8 +471,8 @@ expr.simplify({ rules: otherRules });
470471

471472
## 9. Subpath Exports
472473

473-
`Expression` now refers to the compute-engine runtime expression type.
474-
The MathJSON type has been renamed to `MathJsonExpression`.
474+
`Expression` now refers to the compute-engine runtime expression type. The
475+
MathJSON type has been renamed to `MathJsonExpression`.
475476

476477
### Before
477478

@@ -495,21 +496,21 @@ import { MathJsonExpression } from '@cortex-js/compute-engine/math-json';
495496

496497
## 10. Removed Properties
497498

498-
The following properties have been removed from the `Expression` base
499-
interface. They are now only available on the corresponding role interfaces,
500-
accessed via type guards.
501-
502-
| Removed Property | Type Guard → Interface |
503-
|:----------------------------|:--------------------------------------------------|
504-
| `expr.numericValue` | `isNumber()``NumberLiteralInterface` |
505-
| `expr.isNumberLiteral` | `isNumber()``NumberLiteralInterface` |
506-
| `expr.symbol` | `isSymbol()``SymbolInterface` |
507-
| `expr.string` | `isString()``StringInterface` |
508-
| `expr.isFunctionExpression` | `isFunction()``FunctionInterface` |
509-
| `expr.ops` | `isFunction()``FunctionInterface` |
510-
| `expr.nops` | `isFunction()``FunctionInterface` |
511-
| `expr.op1` / `op2` / `op3` | `isFunction()``FunctionInterface` |
512-
| `expr.tensor` | `isTensor()``TensorInterface` |
499+
The following properties have been removed from the `Expression` base interface.
500+
They are now only available on the corresponding role interfaces, accessed via
501+
type guards.
502+
503+
| Removed Property | Type Guard → Interface |
504+
| :-------------------------- | :-------------------------------------- |
505+
| `expr.numericValue` | `isNumber()``NumberLiteralInterface` |
506+
| `expr.isNumberLiteral` | `isNumber()``NumberLiteralInterface` |
507+
| `expr.symbol` | `isSymbol()``SymbolInterface` |
508+
| `expr.string` | `isString()``StringInterface` |
509+
| `expr.isFunctionExpression` | `isFunction()``FunctionInterface` |
510+
| `expr.ops` | `isFunction()``FunctionInterface` |
511+
| `expr.nops` | `isFunction()``FunctionInterface` |
512+
| `expr.op1` / `op2` / `op3` | `isFunction()``FunctionInterface` |
513+
| `expr.tensor` | `isTensor()``TensorInterface` |
513514

514515
Accessing these properties without first narrowing with a type guard is now a
515516
TypeScript compile error.
@@ -531,6 +532,7 @@ if (isSymbol(expr)) {
531532
### Pattern: Checking Multiple Expression Types
532533

533534
**Before:**
535+
534536
```ts
535537
if (expr.symbol !== null) {
536538
return expr.symbol;
@@ -542,6 +544,7 @@ if (expr.symbol !== null) {
542544
```
543545

544546
**After:**
547+
545548
```ts
546549
import { isSymbol, isNumber, isFunction } from '@cortex-js/compute-engine';
547550

@@ -557,6 +560,7 @@ if (isSymbol(expr)) {
557560
### Pattern: Processing Function Arguments
558561

559562
**Before:**
563+
560564
```ts
561565
if (expr.ops) {
562566
for (const arg of expr.ops) {
@@ -566,6 +570,7 @@ if (expr.ops) {
566570
```
567571

568572
**After:**
573+
569574
```ts
570575
import { isFunction } from '@cortex-js/compute-engine';
571576

@@ -579,11 +584,13 @@ if (isFunction(expr)) {
579584
### Pattern: Safe Numeric Value Access
580585

581586
**Before:**
587+
582588
```ts
583589
const value = expr.numericValue ?? 0; // Default to 0 if not a number
584590
```
585591

586592
**After:**
593+
587594
```ts
588595
import { isNumber } from '@cortex-js/compute-engine';
589596

@@ -593,11 +600,13 @@ const value = isNumber(expr) ? expr.numericValue : 0;
593600
### Pattern: Symbol Name Extraction
594601

595602
**Before:**
603+
596604
```ts
597605
const name = expr.symbol || 'unknown';
598606
```
599607

600608
**After:**
609+
601610
```ts
602611
import { sym } from '@cortex-js/compute-engine';
603612

@@ -607,11 +616,13 @@ const name = sym(expr) ?? 'unknown';
607616
### Pattern: Working with Decomposition Results
608617

609618
**Before:**
619+
610620
```ts
611621
const [P, L, U] = luDecomposition.ops; // Unsafe - ops might be null
612622
```
613623

614624
**After:**
625+
615626
```ts
616627
import { isFunction } from '@cortex-js/compute-engine';
617628

@@ -639,17 +650,14 @@ ce.box(json, { canonical: false });
639650

640651
// New
641652
import {
642-
ComputeEngine,
653+
getDefaultEngine,
643654
compile,
644655
expand,
645656
isFunction,
646657
} from '@cortex-js/compute-engine';
647658

648-
const ce = new ComputeEngine();
649-
const expr = ce.parse('x^2 + 1');
650-
651-
// New free function calls
652-
expand(expr);
659+
const expr = parse('x^2 + 1');
660+
expand(expr); // or expand("x^2 + 1")
653661
compile(expr);
654-
ce.box(json, { form: 'raw' });
662+
getDefaultEngine().box(json, { form: 'raw' });
655663
```

src/compute-engine.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export type {
2323
} from './compute-engine/compilation/types';
2424

2525
export { JavaScriptTarget } from './compute-engine/compilation/javascript-target';
26+
export { GPUShaderTarget } from './compute-engine/compilation/gpu-target';
2627
export { GLSLTarget } from './compute-engine/compilation/glsl-target';
28+
export { WGSLTarget } from './compute-engine/compilation/wgsl-target';
2729
export { PythonTarget } from './compute-engine/compilation/python-target';
2830
export { IntervalJavaScriptTarget } from './compute-engine/compilation/interval-javascript-target';
2931
export { IntervalGLSLTarget } from './compute-engine/compilation/interval-glsl-target';

0 commit comments

Comments
 (0)