autodiff-visualizer / text_trace_implementation_plan.md
Simon Clematide
Implement stepwise text trace, semantic diagram model, and quiet cluster styling
228e074
|
Raw History Blame Contribute Delete
20.3 kB
Yes. I would implement the textual view as a second renderer of the same semantic computation model, not as an explanation independently reconstructed from the expression. That is the key architectural decision: graph and text must be two projections of exactly the same forward/backward events.
Implementation plan: Stepwise Text Trace
1. Define the purpose precisely
The graph and trace answer different questions:
Graph: Where does information flow?
Trace: What happens, step by step?
The textual trace should linearize the computation while preserving the visual grammarโ€™s concepts:
FORWARD
input values โ†’ local operation โ†’ resulting quantity
BACKWARD
incoming adjoint โ†’ local derivative/rule
โ†’ numerical application
โ†’ contribution
โ†’ accumulation if necessary
โ†’ resulting adjoint
It must not introduce a second autodiff explanation system.
โธป
2. Make DiagramModel the shared semantic source
Target architecture:
Tensor/autodiff engine
โ”‚
โ–ผ
DiagramModel
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ โ”‚
โ–ผ โ–ผ
Mermaid renderer Trace renderer
โ”‚ โ”‚
โ–ผ โ–ผ
computation graph stepwise text trace
The trace renderer must consume the same:
- quantity IDs: x, y, n1, n2, โ€ฆ, out;
- operation formulas;
- operand occurrences;
- primal values;
- adjoints;
- local derivative rules;
- evaluated local derivatives;
- gradient contributions;
- accumulation relationships;
- output seed.
Do not recompute derivatives inside the trace renderer.
โธป
3. Add an explicit trace renderer
Create something such as:
trace_renderer.py
with a public entry point conceptually:
render_trace(model, notation, mode) -> str
where:
notation = mathbook | code
mode = forward | forward_backward
This should parallel the existing diagram renderer.
โธป
4. Preserve execution order explicitly
The graph uses spatial topology. The trace needs deterministic sequential order.
Forward
Operations appear in topological execution order:
n1
n2
n3
...
out
Backward
Operations appear in reverse topological order:
out
...
n3
n2
n1
Within an operation, operand occurrences retain their original argument order.
For:
n1 = x \* x
there are still two operand occurrences. Do not collapse them merely because they refer to the same variable.
โธป
5. Forward-pass trace grammar
Every forward operation gets the same structure.
For example:
FORWARD PASS
Inputs
โ”€โ”€โ”€โ”€โ”€โ”€
x = 3
y = 4
1. Multiply
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
n1 = x ยท x
Inputs:
x = 3
x = 3
Apply:
n1 = 3 ยท 3
Result:
n1 = 9
2. Multiply
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
n2 = n1 ยท y
Inputs:
n1 = 9
y = 4
Apply:
n2 = 9 ยท 4
Result:
n2 = 36
3. Add
โ”€โ”€โ”€โ”€โ”€โ”€
out = 2 + y + n2
Inputs:
2 = 2
y = 4
n2 = 36
Apply:
out = 2 + 4 + 36
Result:
out = 42
The exact amount of repetition can later be tuned, but the schema should initially remain explicit.
That is consistent with the pedagogical goal of this application.
โธป
6. Treat the Output boundary separately
As in the graph, do not conflate the final operation with the Output presentation quantity.
After the final forward operation:
OUTPUT
โ”€โ”€โ”€โ”€โ”€โ”€
out = 42
In Forward+Backward mode this then becomes the starting point for reverse mode:
BACKWARD PASS
Seed
โ”€โ”€โ”€โ”€
outฬ„ = โˆ‚out/โˆ‚out = 1
This mirrors the established graph semantics exactly.
โธป
7. Backward-pass trace grammar
Every local backward computation should follow one rigid rhetorical template:
Incoming adjoint
โ†“
Local derivative / backward rule
โ†“
Numerical application
โ†“
Contribution
For a scalar multiplication:
1. Backpropagate through n2 = n1 ยท y
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Incoming adjoint:
nฬ„2 = โˆ‚out/โˆ‚n2 = 1
With respect to n1
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Local derivative:
โˆ‚n2/โˆ‚n1 = y
Apply:
nฬ„2 ยท y = 1 ยท 4 = 4
Contribution to n1:
4
With respect to y
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Local derivative:
โˆ‚n2/โˆ‚y = n1
Apply:
nฬ„2 ยท n1 = 1 ยท 9 = 9
Contribution to y:
9
This is the textual equivalent of the two derivative trapezoids.
โธป
8. Make the adjoint interpretation explicit
Whenever a new accumulated adjoint becomes available, state it explicitly:
Adjoint:
nฬ„1 = โˆ‚out/โˆ‚n1 = 4
This is important because the adjoint is our notation for:
the chain rule accumulated so far from the output to this quantity.
The trace should therefore use exactly the same adjoint notation as the graph.
โธป
9. Make accumulation an explicit textual step
Do not silently sum contributions.
For x\*x, write:
Backpropagate through n1 = x ยท x
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Incoming adjoint:
nฬ„1 = โˆ‚out/โˆ‚n1 = 4
Argument 1: x
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Local derivative:
โˆ‚n1/โˆ‚x = x
Apply:
nฬ„1 ยท x = 4 ยท 3 = 12
Contribution to x:
12
Argument 2: x
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Local derivative:
โˆ‚n1/โˆ‚x = x
Apply:
nฬ„1 ยท x = 4 ยท 3 = 12
Contribution to x:
12
Accumulate contributions to x
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
xฬ„ = 12 + 12
= 24
Therefore:
xฬ„ = โˆ‚out/โˆ‚x = 24
This is the exact textual equivalent of:
12 โ”€โ”€โ”
ฮฃ โ”€โ”€โ†’ xฬ„ = 24
12 โ”€โ”€โ”˜
The trace therefore preserves the core invariant:
Along one backward dependency: apply/multiply.
Across multiple contributions: add.
โธป
10. Distinguish contributions from adjoints
This distinction must be just as strict in text as in the graph.
Before accumulation:
Contribution to x:
12
not:
xฬ„ = 12
if another contribution to x still exists.
Only after all contributions have been collected may the trace say:
xฬ„ = 24
This prevents exactly the conceptual ambiguity the explicit ฮฃ nodes solved in the graph.
โธป
11. Tensor operations use the same structure
Do not create a separate tensor trace grammar.
For:
n1 = W @ x
write:
Backpropagate through n1 = W @ x
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Incoming adjoint:
nฬ„1 = [-0.10, 0.24, -0.14]
With respect to W
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Local backward rule:
Wฬ„ contribution = nฬ„1 xแต€
Apply:
[-0.10, 0.24, -0.14] [1, 2]แต€
Result:
[[-0.10, -0.20],
[ 0.24, 0.48],
[-0.14, -0.28]]
Contribution to W:
[[-0.10, -0.20],
[ 0.24, 0.48],
[-0.14, -0.28]]
With respect to x
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Local backward rule:
xฬ„ contribution = Wแต€ nฬ„1
Apply:
...
Result:
[-0.12, 0.20]
Contribution to x:
[-0.12, 0.20]
If each target receives only one contribution, those contributions immediately establish the respective adjoints.
โธป
12. Handle VJPs honestly
For tensor operations, forcing everything into scalar notation such as
\bar v\,\frac{\partial v}{\partial x}
can obscure what is actually happening.
The trace should use the operation-specific local backward rule already represented by the model.
For softmax:
Backpropagate through n3 = softmax(n2)
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Incoming adjoint:
nฬ„3 = [0, 1, 0]
Local backward rule:
nฬ„2 = n3 โŠ™ (nฬ„3 - sum(nฬ„3 โŠ™ n3))
Values:
n3 = [0.18, 0.59, 0.24]
nฬ„3 = [0, 1, 0]
Apply:
...
Result:
nฬ„2 = [-0.10, 0.24, -0.14]
This keeps the same conceptual ruleโ€”incoming adjoint transformed by the local backward operationโ€”without pretending every tensor derivative should be displayed as a huge Jacobian.
โธป
13. Reuse the canonical tensor formatter
The trace must use the tensor-display policy we just defined.
Initially:
element budget = 16
or, if you deliberately want more detail in text:
graph budget = 16
trace budget = 32
But both must use the same formatter and semantics.
Thus a 3\times2 gradient must appear as its six actual values:
[[-0.10, -0.20],
[ 0.24, 0.48],
[-0.14, -0.28]]
Never:
shape (3, 2)
For a large tensor:
[[0.12, -0.31, โ€ฆ, 0.08],
[ โ‹ฎ โ‹ฎ โ‹ฑ โ‹ฎ ],
[-0.42, 0.06, โ€ฆ, 0.29]]
shape (128, 64)
โธป
14. Respect Forward-only mode strictly
If the user selects Forward only, the textual trace must end after the forward result.
For example:
FORWARD PASS
Inputs
โ”€โ”€โ”€โ”€โ”€โ”€
x = 3
y = 4
...
OUTPUT
โ”€โ”€โ”€โ”€โ”€โ”€
out = 42
There must be no:
BACKWARD PASS
outฬ„
gradient
derivative
contribution
adjoint
โ€”
This is the textual counterpart of the Forward-only purity invariant.
โธป
15. Support Mathbook and Code notation
The trace should obey the existing notation toggle.
Mathbook
Local derivative:
โˆ‚n2/โˆ‚y = n1
Apply:
nฬ„2 ยท n1 = 1 ยท 9 = 9
Code
Local derivative:
diff(n1 _ y, y) = n1
Apply:
n2_bar _ n1 = 1 \* 9 = 9
Do not implement separate mathematical semantics. Only notation changes.
Use the existing centralized formatting helpers wherever possible.
โธป
16. Introduce structured trace events if DiagramModel is insufficient
This is the one architectural extension I would seriously consider.
Rendering directly by reverse-engineering edges can become fragile. If DiagramModel does not already make execution steps explicit enough, add semantic objects such as:
@dataclass
class ForwardStep:
result: QuantityState
operation: OperationState
operands: list[QuantityState]
@dataclass
class BackwardOperandStep:
operation: OperationState
operand_occurrence: int
target: QuantityState
incoming_adjoint: Any
local_rule: ...
local_value: Any
contribution: Any
@dataclass
class AccumulationStep:
target: QuantityState
contributions: list[Any]
resulting_adjoint: Any
Then both renderers can consume these.
The important architecture is:
autodiff computation
โ”‚
โ–ผ
semantic steps
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”
โ–ผ โ–ผ
graph text
not:
autodiff โ†’ graph
โ”‚
โ–ผ
parse graph back
into explanation
โธป
17. Keep operation identity separate from pedagogical quantity names
The trace should use the established:
x, y, W, b
n1, n2, n3, ...
out
Do not reintroduce O1, O2, etc.
If an internal operation ID is required, keep it internal.
The learner should see:
3. n2 = n1 + b
not:
Operation O3
unless there is eventually a specific debugging mode that exposes internal IDs.
โธป
18. Do not overuse operation names
A heading like:
2. Multiply
can be useful, but the actual formula should remain authoritative:
n2 = n1 ยท y
For less familiar operations:
3. Softmax
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
n3 = softmax(n2)
This is clearer than generating verbose prose such as:
โ€œThe third operation applies the softmax function to the second intermediate node.โ€
The trace should remain computational rather than narrative.
โธป
19. Suggested complete scalar fixture
Use the existing canonical expression:
out=y(x x)+(y+2),\quad x=3,\;y=4.
A shortened target trace is:
# FORWARD PASS
Inputs
x = 3
y = 4
1. n1 = x ยท x
= 3 ยท 3
= 9
2. n2 = n1 ยท y
= 9 ยท 4
= 36
3. out = n2 + y + 2
= 36 + 4 + 2
= 42
OUTPUT
out = 42
BACKWARD PASS
=============
Seed
outฬ„ = โˆ‚out/โˆ‚out = 1
4. Backpropagate through out = n2 + y + 2
To n2:
โˆ‚out/โˆ‚n2 = 1
outฬ„ ยท 1 = 1 ยท 1 = 1
contribution = 1
To y:
โˆ‚out/โˆ‚y = 1
outฬ„ ยท 1 = 1 ยท 1 = 1
contribution = 1
Therefore:
nฬ„2 = โˆ‚out/โˆ‚n2 = 1
5. Backpropagate through n2 = n1 ยท y
Incoming:
nฬ„2 = 1
To n1:
โˆ‚n2/โˆ‚n1 = y
nฬ„2 ยท y = 1 ยท 4 = 4
contribution = 4
To y:
โˆ‚n2/โˆ‚y = n1
nฬ„2 ยท n1 = 1 ยท 9 = 9
contribution = 9
Therefore:
nฬ„1 = โˆ‚out/โˆ‚n1 = 4
6. Backpropagate through n1 = x ยท x
Incoming:
nฬ„1 = 4
Argument 1:
โˆ‚n1/โˆ‚x = x
nฬ„1 ยท x = 4 ยท 3 = 12
contribution = 12
Argument 2:
โˆ‚n1/โˆ‚x = x
nฬ„1 ยท x = 4 ยท 3 = 12
contribution = 12
ACCUMULATION
============
For x:
xฬ„ = 12 + 12 = 24
For y:
yฬ„ = 1 + 9 = 10
FINAL GRADIENTS
===============
โˆ‚out/โˆ‚x = xฬ„ = 24
โˆ‚out/โˆ‚y = yฬ„ = 10
There is one refinement I would make in the real implementation: perform each accumulation at the point where the resulting adjoint becomes available, rather than collecting all accumulation into a final section. That better reflects reverse-mode execution:
contributions to y: 1 + 9
โ†“
yฬ„ = 10
and then continue.
โธป
20. Make accumulation timing semantically correct
This is important for shared intermediates.
The trace must not backpropagate through quantity v until all downstream contributions to v have been accumulated.
For example:
contribution to n1 from path A = ...
contribution to n1 from path B = ...
Accumulate:
nฬ„1 = contribution A + contribution B
Only then:
Backpropagate through n1 = ...
Incoming adjoint:
nฬ„1 = ...
That reproduces the semantics of the graphโ€™s ฮฃ node.
โธป
21. Add dedicated tests at the semantic level
Tests should verify more than strings.
For x\*x:
two operand backward steps
two contributions to x
one accumulation step
final xฬ„ = 2x
For x\*\*2 + x:
two independent contributions to x
one accumulation
For (x+y)\*(x+y):
multiple contributions to shared intermediate
accumulation occurs before backpropagating through that intermediate
For tensor cases:
actual tensor values present
no shape substituted for value
VJP dimensions correct
โธป
22. Add rendering snapshot tests
Then test representative complete trace strings for:
1. x\*y
2. x\*x
3. x\*\*2 + x
4. (x+y)\*(x+y)
5. (x*x)*y + (y+2)
6. W @ x
7. softmax(W @ x + b)[1]
8. the larger loss example.
Test both Mathbook and Code modes where notation differs.
โธป
23. Add mode-purity tests
For forward:
assert no_adjoint_symbols
assert no_local_derivatives
assert no_contributions
assert no_accumulations
assert no_backward_heading
For forward_backward:
assert seed_present
assert incoming_adjoints_present
assert local_rules_present
assert numerical_applications_present
assert accumulations_present_when_required
This should parallel the diagram mode-purity tests.
โธป
24. UI integration
I would expose the trace as a view choice, not as another mathematical mode.
For example:
View:
โ—‹ Graph
โ—‹ Step-by-step
while keeping:
Pass:
โ—‹ Forward
โ—‹ Forward + Backward
Notation:
โ—‹ Mathbook
โ—‹ Code
These dimensions should remain orthogonal:
Graph Step-by-step
Forward โœ“ โœ“
Forward+Back โœ“ โœ“
Mathbook โœ“ โœ“
Code โœ“ โœ“
A future third option could show both side by side, but I would not start there.
โธป
25. Help text should explicitly connect the two views
A short explanation can say:
Graph view shows where values and gradients flow through the computation graph.
Step-by-step view shows the same computation in execution order. During the backward pass, each step starts with the current adjoint, applies one local backward rule, and produces a contribution. Contributions to the same quantity are added to form its adjoint.
That explicitly teaches that these are two representations of one process.
โธป
26. Final implementation sequence
I would implement in this order:
1. Confirm DiagramModel exposes all required semantic data.
2. Add structured ForwardStep, BackwardOperandStep, and AccumulationStep objects only where needed.
3. Generate these steps deterministically from the computation model.
4. Implement trace_renderer.py.
5. Implement Forward-only trace first.
6. Add output boundary and backward seed.
7. Add scalar backward steps.
8. Add explicit accumulation.
9. Add tensor/VJP backward steps.
10. Route all numerical values through the canonical tensor formatter.
11. Add Mathbook/Code formatting.
12. Add semantic tests.
13. Add trace snapshot tests.
14. Add mode-purity tests.
15. Integrate the view selector into the app.
16. Update VISUAL_LANGUAGE.md.
17. Visually compare graph and trace for all canonical fixtures.
The final acceptance invariant should be:
For every piece of information visible in the stepwise trace, there is a corresponding semantic object in the computation model; and for every forward/backward computation represented in the graph, the trace can present the same computation sequentially without recomputing or reinterpreting it.
That will keep the textual view from becoming a parallel explanation system that gradually diverges from the graphโ€”the main implementation risk I would avoid.
Yes. I think two explicit action buttons are better than introducing another โ€œViewโ€ radio selector. The current UI already has two conceptual settingsโ€”computation mode and notationโ€”so output type can naturally be the action the user takes.
I would change the bottom row to:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Generate Diagram โ”‚ Generate Trace โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Both buttons consume exactly the same settings above them:
Expression
Input Values
Graph/Computation Mode
Derivative Notation
I would probably rename โ€œGraph Modeโ€ โ†’ โ€œComputation Modeโ€ at the same time. Once the application produces text traces, โ€œGraph Modeโ€ is misleading because Forward Only versus Forward & Backward controls the semantics of both representations.
So the conceptual UI becomes:
Expression Input Values Computation Mode Derivative Notation
โ—‹ Forward & Backward โ—‹ Mathbook
โ—‹ Forward Only โ—‹ Code
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Generate Diagram โ”‚ โ”‚ Generate Trace โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
And the behavior is particularly clean:
Computation mode Generate Diagram Generate Trace
Forward Only pure forward graph pure forward stepwise trace
Forward & Backward complete autodiff graph forward + backward stepwise trace
The notation selection applies identically to both.
I would also keep the buttons visually equal. โ€œGenerate Diagramโ€ should not look primary and โ€œGenerate Traceโ€ secondary, because these are two equivalent representations of the same computation, not a main action plus an auxiliary feature.
One small naming point: I prefer Generate Trace over โ€œGenerate Textโ€ or โ€œText View.โ€ โ€œTraceโ€ accurately communicates that this is the computation presented in execution order. The output itself can then be headed Step-by-step trace to make the term immediately understandable.
Architecturally, this fits the plan nicely:
current settings
โ”‚
expression + inputs
computation mode
notation
โ”‚
build semantic model
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ–ผ โ–ผ
Generate Diagram Generate Trace
โ”‚ โ”‚
โ–ผ โ–ผ
Mermaid renderer Trace renderer
Most importantly, the buttons select the representation; they must not select different computation logic. That keeps the UI aligned with the architecture we have been defining.