Skip to content

Commit e187bb5

Browse files
committed
Diff Grok: LaTeX: Update examples
1 parent a28877f commit e187bb5

30 files changed

Lines changed: 1331 additions & 128 deletions

‎.claude/settings.local.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
"Bash(ls c:/Users/vmaka/Datagrok/diff-grok/src/latex-export/examples/*.ivp)",
1414
"Bash(node -e \"const {tokenize} = require\\(''./src/latex-export/parser/tokenizer''\\); const t = tokenize\\(''-0.04 * A + 1e4 * B * C''\\); t.forEach\\(\\(tok,i\\) => console.log\\(i, tok.type, tok.value\\)\\);\")",
1515
"Bash(npx ts-node:*)",
16-
"Bash(xargs wc:*)"
16+
"Bash(xargs wc:*)",
17+
"Bash(ls -la /c/Users/vmaka/Datagrok/diff-grok/src/latex-export/examples/*.ts)",
18+
"WebFetch(domain:datagrok.ai)",
19+
"Bash(npx tsx:*)"
1720
]
1821
}
1922
}

‎README.MD‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,19 @@ Learn more
521521
* [Parameters optimization](https://datagrok.ai/help/compute/function-analysis#parameter-optimization)
522522
* [Sensitivity analysis](https://datagrok.ai/help/compute/function-analysis#sensitivity-analysis)
523523

524+
## LaTeX export
525+
526+
The [latex-export](src/latex-export/) module converts IVP model files into publication-ready **LaTeX** and **Markdown** with LaTeX math:
527+
528+
- ODE systems rendered as `\frac{dy}{dt}` with `align` environments
529+
- Automatic Greek letter recognition, subscripts, and chemical formula formatting
530+
- Scientific notation, `\frac`, `\cdot`, `\sqrt`, `\begin{cases}` for ternary operators
531+
- Configurable output: toggle metadata, initial conditions, parameters, constants tables
532+
- Compact mode for small models: inline initial conditions, text labels instead of section headings
533+
- Two formats: standalone `.tex` documents and Markdown with `$...$` / `$$...$$` math
534+
535+
See the full documentation in [src/latex-export/README.md](src/latex-export/README.md).
536+
524537
## Environment Requirements
525538

526539
### Development & Build

‎src/latex-export/README.md‎

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
# latex-export
2+
3+
Converts [Diff Studio](https://datagrok.ai/help/compute/diff-studio) IVP model files into **LaTeX** and **Markdown** with LaTeX math. Part of the [diff-grok](../../README.md) library.
4+
5+
## Quick start
6+
7+
```typescript
8+
import {convertIvpToLatex} from 'diff-grok';
9+
10+
const ivp = `#name: Robertson
11+
#equations:
12+
dA/dt = -0.04 * A + 1e4 * B * C
13+
dB/dt = 0.04 * A - 1e4 * B * C - 3e7 * B**2
14+
dC/dt = 3e7 * B**2
15+
16+
#inits:
17+
A = 1 {units: mol/L}
18+
B = 0 {units: mol/L}
19+
C = 0 {units: mol/L}
20+
21+
#argument: t
22+
start = 0
23+
finish = 40
24+
step = 0.01`;
25+
26+
// LaTeX output (default)
27+
const latex = convertIvpToLatex(ivp);
28+
29+
// Markdown output
30+
const markdown = convertIvpToLatex(ivp, {format: 'markdown'});
31+
```
32+
33+
## API
34+
35+
### `convertIvpToLatex(ivpText, options?)`
36+
37+
Converts the full text of an `.ivp` file to LaTeX or Markdown.
38+
39+
**Parameters:**
40+
41+
| Parameter | Type | Description |
42+
|---|---|---|
43+
| `ivpText` | `string` | Raw text content of an `.ivp` file |
44+
| `options` | `Partial<ConvertOptions>` | Conversion options (all optional) |
45+
46+
**Returns:** `string` — LaTeX or Markdown markup.
47+
48+
### `ConvertOptions`
49+
50+
| Option | Type | Default | Description |
51+
|---|---|---|---|
52+
| `format` | `'latex' \| 'markdown'` | `'latex'` | Output format |
53+
| `includeMetadata` | `boolean` | `true` | Include name, description, and comment |
54+
| `includeInits` | `boolean` | `true` | Include initial conditions table |
55+
| `includeParameters` | `boolean` | `true` | Include parameters table |
56+
| `includeConstants` | `boolean` | `true` | Include constants table |
57+
| `useCdot` | `boolean` | `true` | Use `\cdot` for multiplication; if `false`, use juxtaposition |
58+
| `compact` | `boolean` | `false` | Compact mode: no section headings, inline initial conditions |
59+
60+
### Compact mode
61+
62+
When `compact: true`, the output uses text labels instead of section headings — suitable for small models:
63+
64+
- **"Equation:"** / **"Equations:"** followed by the align block and argument range
65+
- **"initial condition:"** / **"initial conditions:"** with inline `$x(0) = 2$, $y(0) = 0$`
66+
- **"where"** for expressions, listed inline as `$E_1 = ...$, $E_2 = ...$`
67+
- Parameters and constants remain as tables
68+
69+
```typescript
70+
const compact = convertIvpToLatex(ivp, {compact: true});
71+
```
72+
73+
## Examples
74+
75+
The [examples/](examples/) directory contains 13 `.ivp` model files and 13 `.ts` scripts demonstrating `convertIvpToLatex` with different option combinations. Models are based on the [Datagrok model library](https://datagrok.ai/help/compute/models).
76+
77+
Models with up to 3 equations use `compact: true` for concise output; larger models use section headings.
78+
79+
### Running examples
80+
81+
```bash
82+
# Run a single example
83+
npx tsx src/latex-export/examples/basic-to-latex.ts
84+
85+
# Run all examples
86+
for f in src/latex-export/examples/*.ts; do echo "=== $f ==="; npx tsx "$f"; done
87+
```
88+
89+
### Example catalog
90+
91+
| Example | Model | Format | Options |
92+
|---|---|---|---|
93+
| [basic-to-latex.ts](examples/basic-to-latex.ts) | Simple 1D ODE template | LaTeX | `compact` |
94+
| [robertson-to-latex-no-meta.ts](examples/robertson-to-latex-no-meta.ts) | Robertson's stiff chemical kinetics (3 species) | LaTeX | `compact`, `includeMetadata: false` |
95+
| [advanced-to-latex.ts](examples/advanced-to-latex.ts) | 2D ODE template with expressions, constants, and parameters | LaTeX | `compact`, `includeMetadata: false`, `useCdot: false` |
96+
| [fermentation-to-latex.ts](examples/fermentation-to-latex.ts) | Ethanol fermentation process kinetics | LaTeX | `compact`, `includeParameters: false` |
97+
| [nimotuzumab-to-latex.ts](examples/nimotuzumab-to-latex.ts) | Population PK of the monoclonal antibody nimotuzumab | LaTeX | `compact`, `includeInits: false`, `useCdot: false` |
98+
| [chem-react-to-latex-no-cdot.ts](examples/chem-react-to-latex-no-cdot.ts) | Deterministic mass-action kinetics (4 species) | LaTeX | `useCdot: false` |
99+
| [pollution-to-latex.ts](examples/pollution-to-latex.ts) | Air pollution chemistry (20 species, 25 reactions) | LaTeX | `includeMetadata: false`, `includeInits: false` |
100+
| [bioreactor-to-latex-selective.ts](examples/bioreactor-to-latex-selective.ts) | Fab-arm exchange bioreactor simulation (13 species) | LaTeX | `includeConstants: false`, `includeParameters: false` |
101+
| [extended-to-markdown.ts](examples/extended-to-markdown.ts) | 2D ODE system with expressions and annotations | Markdown | `compact` |
102+
| [pk-to-markdown.ts](examples/pk-to-markdown.ts) | One-compartment pharmacokinetic model with dosing loop | Markdown | `compact` |
103+
| [energy-n-control-to-markdown.ts](examples/energy-n-control-to-markdown.ts) | Custom JS functions, ternary operators, arrow functions | Markdown | `compact`, `includeConstants: false` |
104+
| [ga-production-to-markdown.ts](examples/ga-production-to-markdown.ts) | Gluconic acid production by Aspergillus niger (multi-stage) | Markdown | `useCdot: false` |
105+
| [pk-pd-to-markdown-minimal.ts](examples/pk-pd-to-markdown-minimal.ts) | Two-compartment PK-PD model with dosing loop | Markdown | `includeInits: false`, `includeParameters: false`, `includeConstants: false` |
106+
107+
## What gets converted
108+
109+
### Equations
110+
111+
ODE system from `#equations` block. Derivatives become fractions:
112+
113+
| IVP | LaTeX |
114+
|---|---|
115+
| `dy/dt = -k * y` | `\frac{dy}{dt} = -k \cdot y` |
116+
| `d(FFox)/dt = -E11 + E12` | `\frac{d\,\mathrm{FFox}}{dt} = -\mathrm{E11} + \mathrm{E12}` |
117+
| `dy1/dt = r1 + r2` | `\frac{dy_{1}}{dt} = r_{1} + r_{2}` |
118+
119+
Equations are rendered inside `align` (LaTeX) or `aligned` (Markdown) environments.
120+
121+
### Expressions
122+
123+
Auxiliary formulas from `#expressions` block, including:
124+
125+
- Regular expressions: `E1 = C1 * exp(-t) + P1`
126+
- Arrow functions: `func1 = (p, t) => (p < 8) ? ceil(t) : ceil(t**2 / 20)`
127+
- Ternary operators: `control = (P1 < 3) ? cos(t) : sin(t)`
128+
- Aliases: `ceil = Math.ceil`
129+
130+
### Identifiers
131+
132+
Automatic recognition and formatting:
133+
134+
| Pattern | Example | LaTeX |
135+
|---|---|---|
136+
| Greek letter | `alpha` | `\alpha` |
137+
| Greek + subscript | `mu2` | `\mu_{2}` |
138+
| Letter + digits | `x1`, `k25` | `x_{1}`, `k_{25}` |
139+
| Chemical formula | `CO2`, `N2O5` | `\mathrm{CO}_{2}`, `\mathrm{N_{2}O_{5}}` |
140+
| Multi-letter name | `FFox`, `MEAthiol` | `\mathrm{FFox}`, `\mathrm{MEAthiol}` |
141+
| Special constants | `PI`, `Inf` | `\pi`, `\infty` |
142+
143+
### Operators
144+
145+
| IVP | LaTeX | Notes |
146+
|---|---|---|
147+
| `a * b` | `a \cdot b` | Configurable via `useCdot` |
148+
| `a / b` | `\frac{a}{b}` | Inline `/` inside exponents |
149+
| `x**2` | `x^{2}` | Parentheses added for compound bases |
150+
| `>=` | `\geq` | |
151+
| `<=` | `\leq` | |
152+
| `!=` | `\neq` | |
153+
| `==` | `=` | |
154+
155+
### Functions
156+
157+
| IVP | LaTeX |
158+
|---|---|
159+
| `sin(x)`, `cos(x)`, `tan(x)` | `\sin\!\left(x\right)`, `\cos\!\left(x\right)`, `\tan\!\left(x\right)` |
160+
| `exp(x)` | `e^{x}` |
161+
| `sqrt(x)` | `\sqrt{x}` |
162+
| `pow(a, b)` | `a^{b}` (parentheses around compound base) |
163+
| `log(x)` | `\ln\!\left(x\right)` |
164+
| `log10(x)` | `\log_{10}\!\left(x\right)` |
165+
| `abs(x)` | `\left\lvert x \right\rvert` |
166+
| `ceil(x)` / `Math.ceil(x)` | `\left\lceil x \right\rceil` |
167+
| `Math.floor(x)` | `\left\lfloor x \right\rfloor` |
168+
169+
### Scientific notation
170+
171+
| IVP | LaTeX |
172+
|---|---|
173+
| `1e4` | `10^{4}` |
174+
| `3e7` | `3 \times 10^{7}` |
175+
| `9.2E-2` | `9.2 \times 10^{-2}` |
176+
177+
### Ternary operator
178+
179+
```
180+
Fin = t < switchTime ? 0 : 0.025
181+
```
182+
183+
Rendered as a `cases` environment:
184+
185+
```latex
186+
\mathrm{Fin} = \begin{cases} 0, & \text{if } t < \mathrm{switchTime} \\ 0.025, & \text{otherwise} \end{cases}
187+
```
188+
189+
### Tables
190+
191+
Initial conditions, parameters, and constants from `#inits`, `#parameters`, and `#constants` blocks are rendered as tables:
192+
193+
- **LaTeX:** `tabular` with `booktabs` rules (`\toprule`, `\midrule`, `\bottomrule`)
194+
- **Markdown:** pipe-delimited tables with inline math `$...$`
195+
196+
Tables include name, value, and units (when available).
197+
198+
## Output formats
199+
200+
### LaTeX
201+
202+
Produces a standalone `.tex`-ready document with:
203+
204+
- `\documentclass{article}` preamble
205+
- `amsmath`, `amssymb`, `booktabs` packages
206+
- `\section` / `\subsection` structure
207+
- `align` environments for equations
208+
- `tabular` environments for data tables
209+
210+
### Markdown
211+
212+
Produces Markdown with:
213+
214+
- `##` / `###` heading structure
215+
- `$$\begin{aligned}...\end{aligned}$$` display math blocks
216+
- `$...$` inline math in tables
217+
- Standard Markdown pipe tables
218+
219+
## Supported IVP blocks
220+
221+
| Block | Converted | Description |
222+
|---|---|---|
223+
| `#name` | Yes | Model title |
224+
| `#description` | Yes | Short description |
225+
| `#comment` | Yes | Free-form text |
226+
| `#equations` | Yes | ODE system (primary content) |
227+
| `#expressions` | Yes | Auxiliary formulas |
228+
| `#inits` | Yes | Initial conditions |
229+
| `#parameters` | Yes | Model parameters |
230+
| `#constants` | Yes | Fixed constants |
231+
| `#argument` | Yes | Independent variable and range |
232+
| `#loop` | Yes | Cyclic process (dosing) |
233+
| `#update` | Yes | Multi-stage process |
234+
| `#output` | Parsed | Output columns |
235+
| `#tolerance` | Parsed | Solver tolerance |
236+
| `#meta.solver` | Parsed | Solver settings |
237+
238+
## Architecture
239+
240+
```
241+
index.ts Public API: convertIvpToLatex()
242+
│
243+
├── parser/
244+
│ ├── line-joiner.ts Strip comments, join multi-line formulas
245+
│ ├── ivp-parser.ts Parse .ivp text → ParsedModel
246+
│ ├── tokenizer.ts Expression string → token stream
247+
│ └── ast-parser.ts Tokens → Abstract Syntax Tree
248+
│
249+
├── transformer/
250+
│ ├── identifier.ts Identifiers → Greek, subscripts, \mathrm
251+
│ ├── functions.ts Function calls → LaTeX
252+
│ └── operators.ts Operators → LaTeX
253+
│
254+
├── generator/
255+
│ ├── latex-generator.ts AST → LaTeX string
256+
│ ├── bracket-manager.ts Precedence-based parenthesis logic
257+
│ └── document-builder.ts Assemble complete document
258+
│
259+
├── output/
260+
│ ├── tex-formatter.ts Wrap in \documentclass preamble
261+
│ └── md-formatter.ts Wrap math in $...$ / $$...$$
262+
│
263+
└── types.ts All TypeScript interfaces
264+
```
265+
266+
**Data flow:**
267+
268+
```
269+
.ivp text → parseIvp() → ParsedModel → buildLatexDocument() / buildMarkdownDocument() → string
270+
│
271+
For each formula:
272+
expressionToLatex(rhs)
273+
│
274+
tokenize → parse → AST → nodeToLatex
275+
├── identifierToLatex
276+
├── functionToLatex
277+
├── operatorToLatex
278+
└── needsParentheses
279+
```
280+
281+
## Testing
282+
283+
```bash
284+
npx jest src/tests/latex-export/ # all latex-export tests
285+
npx jest src/tests/latex-export/parser/ # parser tests only
286+
npx jest src/tests/latex-export/transformer/ # transformer tests only
287+
npx jest src/tests/latex-export/generator/ # generator tests only
288+
npx jest src/tests/latex-export/integration/ # end-to-end with real .ivp files
289+
```

‎src/latex-export/SPEC.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ interface ConvertOptions {
8686

8787
/** Use \cdot for multiplication. Default: true. If false, use juxtaposition. */
8888
useCdot: boolean;
89+
90+
/** Compact mode: no section headings, inline initial conditions. Default: false */
91+
compact: boolean;
8992
}
9093

9194
function convertIvpToLatex(ivpText: string, options?: Partial<ConvertOptions>): string;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/** Example: advanced model → LaTeX, no metadata, no cdot (juxtaposition). */
2+
3+
import {convertIvpToLatex} from '../index';
4+
5+
const MODEL = `#name: Advanced
6+
#comment:
7+
This is an advanced template. Modify it. Use multi-line formulas if needed.
8+
Add new equations, expressions, constants & parameters. Edit these comment lines if required.
9+
#equations:
10+
dx/dt = E1 * y + sin(t)
11+
12+
dy/dt = E2 * x - pow(t, 5)
13+
14+
#expressions:
15+
E1 = C1 * exp(-t) + P1
16+
E2 = C2 * cos(2 * t) + P2
17+
18+
#argument: t
19+
start = 0
20+
finish = 10 {min: 10; max: 100}
21+
step = 0.01
22+
23+
#inits:
24+
x = 2
25+
y = 0
26+
27+
#constants:
28+
C1 = 1
29+
C2 = 3
30+
31+
#parameters:
32+
P1 = 1
33+
P2 = -1
34+
35+
#tolerance: 5e-5`;
36+
37+
// Compact, no metadata, juxtaposition
38+
const latex = convertIvpToLatex(MODEL, {
39+
compact: true,
40+
includeMetadata: false,
41+
useCdot: false,
42+
});
43+
console.log(latex);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/** Example: basic model → compact LaTeX with default options. */
2+
3+
import {convertIvpToLatex} from '../index';
4+
5+
const MODEL = `#name: Template
6+
#equations:
7+
dy/dt = -y + sin(t) / t
8+
9+
#argument: t
10+
initial = 0.01 {min: 0.01; max: 10}
11+
final = 15 {min: 15; max: 150}
12+
step = 0.01 {min: 0.001; max: 0.1}
13+
14+
#inits:
15+
y = 0 {min: 0; max: 9}`;
16+
17+
// Compact mode: text labels instead of section headings, inline initial conditions
18+
const latex = convertIvpToLatex(MODEL, {compact: true});
19+
console.log(latex);

0 commit comments

Comments
 (0)