-- EML tree: S → 1 | eml(S, S) -- eml(x, y) = exp(x) − ln(y) -- -- Single binary primitive that generates all elementary functions. -- (Odrzywolek 2026, arXiv:2603.21852) -- ── Core inductive ──────────────────────────────────────────────────────────── inductive EMLExpr where | one : EMLExpr | var (name : String) : EMLExpr -- free variable resolved in the rendering env | eml (l r : EMLExpr) : EMLExpr -- ── Derived forms ───────────────────────────────────────────────────────────── -- exp(x) = eml(x, 1) since exp(x) − ln(1) = exp(x) − 0 = exp(x) def EMLExpr.expOf (x : EMLExpr) : EMLExpr := .eml x .one -- ln(x) = eml(1, eml(eml(1, x), 1)) def EMLExpr.lnOf (x : EMLExpr) : EMLExpr := .eml .one (.eml (.eml .one x) .one) -- ── Plot configuration ──────────────────────────────────────────────────────── /-- A path config: an EML expression plus its distinguished path-dimension variable. `dimName` is the name that ranges over `{0, 1}` when the config is interpreted as an `EMLPath` (see `EML/Path.lean`'s `PlotConfig.toEMLPath`). When `dimName` does not occur in `expr`, the path is *constant* (its value is the same at both endpoints); when it does, the path is genuinely parametric. -/ structure PlotConfig where expr : EMLExpr dimName : String := "t" -- ── Named demo expressions (probe test fixtures) ───────────────────────────── -- exp(x): depth-1 EML tree, one node. def plotExp : PlotConfig := { expr := EMLExpr.expOf (.var "px") } -- ln(x): depth-3 EML tree, three nodes. -- -- Historical note: an earlier `plotLn` used the variable name -- `"max(px, 0.001)"` to clamp negative inputs on the GPU side. That -- made the GPU shader evaluate `max(px, 0.001)` as a GLSL expression -- but left Lean's `shaderVar` hitting the fallback `0.0` — the two -- sides disagreed on the semantic of the variable. The probe test -- surfaced the divergence; the fix is to use a real variable `px` -- and accept that `ln` of negative `px` produces `NaN` on both sides -- (the two sides agree, which is what `render_faithful` cares about). def plotLn : PlotConfig := { expr := EMLExpr.lnOf (.var "px") } -- ── Continuous-homotopy demo: a genuinely parametric path ──────────────────── -- `exp(px) − t` translates the exponential curve down by `t`. At `t=0` -- the curve is `y = exp(x)`; at `t=1` it's `y = exp(x) − 1`; in between it -- smoothly slides. EML-expressible because `exp(px) - log(exp(t))` reduces -- (via `log ∘ exp = id`) to `exp(px) - t`; body is -- `eml(var "px", eml(var "t", one))`. def plotTransp : PlotConfig := { expr := .eml (.var "px") (.eml (.var "t") .one) dimName := "t" } -- ── Clean fibers in [0, 1] for greyscale demos ────────────────────────────── -- These bodies are picked specifically so their image lies in `[0, 1]`, -- matching the framebuffer's natural display range. Each is a 1-cell; -- their shape under different `pathParam` values shows the transport -- in action with no clamping artifacts. /-- The 1-cell whose body IS the dim variable: at `pathParam = c` every pixel evaluates to `c`. Different fibers display as solid-color panels with brightness equal to the fiber's parameter. `at0 = solid black`, `at1 = solid white`, `mid = solid 50% grey`. -/ def plotT : PlotConfig := { expr := .var "t" dimName := "t" } /-- The 1-cell whose body is `px`: image is the horizontal coordinate itself, a black-to-white left-to-right gradient. Constant 1-cell (no `t` dependence) — every fiber is the same gradient. Useful as a sanity check: fibers should NOT differ. -/ def plotPx : PlotConfig := { expr := .var "px" dimName := "t" }