|
| 1 | +--- |
| 2 | +title: "Introducing DoodleBUGS: a Browser-Based Graphical Interface for JuliaBUGS" |
| 3 | +description: "Shravan Goswami's GSoC 2025 final report: goals, architecture, progress vs proposal, and how to try it." |
| 4 | +categories: |
| 5 | + - GSoC |
| 6 | +author: |
| 7 | + - name: Shravan Goswami |
| 8 | + url: https://shravangoswami.com/ |
| 9 | +date: 2025-08-28 |
| 10 | +aliases: |
| 11 | + - /news/doodlebugs-gsoc-2025-final-report |
| 12 | + - /news/doodlebugs-gsoc-2025 |
| 13 | + - /news/doodlebugs-introduction |
| 14 | + - /news/shravan-gsoc-2025-doodlebugs |
| 15 | + - /doodlebugs-gsoc-2025-final-report |
| 16 | + - /doodlebugs-gsoc-2025 |
| 17 | + - /doodlebugs-introduction |
| 18 | + - /shravan-gsoc-2025-doodlebugs |
| 19 | +bibliography: references.bib |
| 20 | +csl: university-of-york-ieee.csl |
| 21 | +link-citations: true |
| 22 | +nocite: | |
| 23 | + @* |
| 24 | +--- |
| 25 | + |
| 26 | +## TL;DR |
| 27 | + |
| 28 | +- DoodleBUGS is a browser-based graphical editor for Bayesian models that targets JuliaBUGS for BUGS model compilation and connects to Julia inference backends (e.g., AdvancedHMC via AbstractMCMC). |
| 29 | +- Implemented: visual editor (nodes, edges, nested plates), legacy BUGS code generation that compiles with [JuliaBUGS](https://github.com/TuringLang/JuliaBUGS.jl) [@JuliaBUGS; @bugs-book], local execution via a Julia backend, unified standalone script generation (frontend), timeouts, multiple layouts, and extensive cleanup/typing. |
| 30 | +- Changed from proposal: frontend implemented in Vue 3 (instead of React); backend simplified (frontend is the single source of truth for standalone scripts). |
| 31 | +- Status: Working application. Live demo (static UI) available; for running inference locally, use the backend server. |
| 32 | + |
| 33 | +## Project Links |
| 34 | + |
| 35 | +- Repo: [https://github.com/TuringLang/JuliaBUGS.jl](https://github.com/TuringLang/JuliaBUGS.jl) |
| 36 | +- Live demo: [https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/](https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/) |
| 37 | + |
| 38 | +## DoodleBUGS Project Structure |
| 39 | + |
| 40 | +- `DoodleBUGS/` — Vite + Vue 3 app (UI editor) |
| 41 | + - `public/` — static assets served by Vite; example projects under `public/examples/` |
| 42 | + - `experiments/` — prototypes and exploratory work |
| 43 | + - `runtime/` — Julia HTTP backend for local runs (API endpoints and Julia dependencies) |
| 44 | + - `src/` — application source |
| 45 | + - `assets/` — styles and static assets |
| 46 | + - `components/` — Vue components composing the UI |
| 47 | + - `canvas/` — graph canvas and toolbars |
| 48 | + - `common/` — shared UI primitives |
| 49 | + - `layouts/` — app layout and modals |
| 50 | + - `left-sidebar/` — palette, project manager, execution settings |
| 51 | + - `panels/` — code preview and data input panels |
| 52 | + - `right-sidebar/` — execution, JSON editor, node properties |
| 53 | + - `ui/` — base UI elements (buttons, inputs, selects) |
| 54 | + - `composables/` — reusable logic (code generation, drag & drop, graph instance, validator, grid snapping) |
| 55 | + - `config/` — configuration and node definitions |
| 56 | + - `stores/` — Pinia state stores (graph, data, execution, project, UI) |
| 57 | + - `types/` — TypeScript types and ambient declarations |
| 58 | + - `tmp/` — local temporary outputs (ignored in builds) |
| 59 | + - `ztest/` — scratch/test artifacts |
| 60 | + |
| 61 | +## Motivation |
| 62 | + |
| 63 | +[JuliaBUGS](https://github.com/TuringLang/JuliaBUGS.jl) is a modern Julia implementation of the BUGS language [@bugs-rjournal; @bugs-book; @bugs-project]. DoodleBUGS revives the original visual modeling concept with a modern stack so users can: |
| 64 | + |
| 65 | +- Construct probabilistic graphical models visually (nodes, edges, plates). |
| 66 | +- Export readable legacy BUGS code that compiles with JuliaBUGS [@bugs-rjournal; @bugs-book]. |
| 67 | +- Run inference and inspect results from the UI. Common BUGS applications include parallel MCMC [@multibugs], survival analysis [@bugs-survival], and Gibbs-style samplers [@albert-chib-1993; @informs-gibbs]. |
| 68 | + |
| 69 | +## What Was Built |
| 70 | + |
| 71 | +- Visual editor |
| 72 | + - Node types: stochastic, observed, deterministic |
| 73 | + - Plates with arbitrary nesting; robust drag-in/out and creation inside plates |
| 74 | + - Graph layouts: [WebCola](https://ialab.it.monash.edu/webcola/) and [ELK/KLay](https://www.eclipse.org/elk/); stable drag interactions |
| 75 | +- Legacy BUGS code generation [@bugs-rjournal; @bugs-book] |
| 76 | + - Topological ordering and plate-aware traversal |
| 77 | + - Parameter formatting and safe index expansion |
| 78 | + - Implemented in `DoodleBUGS/src/composables/useBugsCodeGenerator.ts` |
| 79 | +- Execution flow |
| 80 | + - Frontend sends `model_code`, `data`, `inits`, `settings` to backend |
| 81 | + - Backend compiles/samples and returns summaries and quantiles |
| 82 | + - Frontend unifies standalone script generation; backend no longer attaches duplicates |
| 83 | +- Timeouts/resilience |
| 84 | + - Configurable timeout (frontend); enforced in backend worker |
| 85 | + - Safe temp directory cleanup on Windows with retries |
| 86 | +- Cleanup/typing |
| 87 | + - TypeScript fixes in `DoodleBUGS/src/components/right-sidebar/ExecutionPanel.vue` |
| 88 | + - Removal of unused backend code; consistent naming and logs |
| 89 | + |
| 90 | +## Architecture Overview |
| 91 | + |
| 92 | +- Frontend: [Vue 3](https://vuejs.org/), [Pinia](https://pinia.vuejs.org/), [Cytoscape.js](https://js.cytoscape.org/) [@cytoscapejs], [CodeMirror](https://codemirror.net/) |
| 93 | + - Code generation: `DoodleBUGS/src/composables/useBugsCodeGenerator.ts` |
| 94 | + - Execution panel: `DoodleBUGS/src/components/right-sidebar/ExecutionPanel.vue` |
| 95 | +- Backend (Julia) HTTP server |
| 96 | + - Server: `DoodleBUGS/runtime/server.jl` |
| 97 | + - Project deps: `DoodleBUGS/runtime/Project.toml` (HTTP, JSON3, JuliaBUGS, AbstractMCMC, AdvancedHMC, ReverseDiff, MCMCChains, DataFrames, StatsBase, Statistics) |
| 98 | + - Endpoints: GET `/api/health`; POST `/api/run` and `/api/run_model` |
| 99 | + - Execution: creates temp dir, writes `model.bugs` and `payload.json`, generates `run_script.jl`, enforces optional timeout |
| 100 | + |
| 101 | +## Design Principles and Architecture |
| 102 | + |
| 103 | +**Design principles** |
| 104 | + |
| 105 | +- Visual-first modeling with deterministic export to legacy BUGS [@bugs-rjournal; @bugs-book]. |
| 106 | +- Separation of concerns: editing (graph), generation (BUGS), execution (backend), and results (summary/quantiles) are modular. |
| 107 | +- Deterministic ordering: topological sort + plate-aware traversal ensures readable, stable code output. |
| 108 | +- Robustness: cancellable frontend fetch, backend-enforced timeout, and resilient temp cleanup on Windows (`safe_rmdir()`). |
| 109 | + |
| 110 | +**Frontend architecture (Vue 3 + Cytoscape.js)** |
| 111 | + |
| 112 | +- Core graph state is managed in Vue; Cytoscape.js handles layout, hit-testing, and interaction semantics (including compound nodes for plates) [@cytoscapejs]. |
| 113 | +- Code generation lives in `DoodleBUGS/src/composables/useBugsCodeGenerator.ts` and maps `GraphNode`/`GraphEdge` to BUGS: |
| 114 | + - Kahn topological sort for definition order |
| 115 | + - Plate-aware recursion for `for (...) { ... }` blocks |
| 116 | + - Parameter canonicalization (indices, numeric/expr passthrough) |
| 117 | +- Standalone Julia script generation uses `generateStandaloneScript()` in the same composable, mirroring backend execution. |
| 118 | + |
| 119 | +**Backend architecture (Julia)** |
| 120 | + |
| 121 | +- `run_model_handler()` in `DoodleBUGS/runtime/server.jl` materializes `model.bugs`, `payload.json`, and a transient `run_script.jl` that: |
| 122 | + - Builds `NamedTuple`s from JSON or string-literal data/inits |
| 123 | + - Compiles via `JuliaBUGS.@bugs`, wraps with `ADgradient(:ReverseDiff)` [@ReverseDiff] |
| 124 | + - Samples with `AdvancedHMC.NUTS` through `AbstractMCMC` (Threads or Serial) [@AdvancedHMC; @AbstractMCMC; @HoffmanGelman2014] |
| 125 | + - Emits summaries (`MCMCChains`, `DataFrames`) and quantiles to JSON |
| 126 | + [@MCMCChains; @DataFrames] |
| 127 | +- Timeout: worker process is killed if exceeding `timeout_s`. |
| 128 | +- Cleanup: `safe_rmdir()` retries with GC to avoid EBUSY on Windows. |
| 129 | + |
| 130 | +## Why Vue (not React) |
| 131 | + |
| 132 | +The proposal planned React; we chose Vue 3 after evaluating the graph layer and developer velocity for this app. |
| 133 | + |
| 134 | +- Tried Konva (canvas) for custom graph editing: powerful drawing primitives, but required bespoke graph semantics (hit testing, edge routing, compound nodes) that Cytoscape.js provides out of the box. |
| 135 | +- Tried D3 force/layouts: flexible, but compound nodes (plates), nesting, and drag constraints became a significant amount of custom code to maintain. |
| 136 | +- Cytoscape.js offered: |
| 137 | + - Native graph model with compound nodes (great for plates) |
| 138 | + - Integrated layouts (WebCola, KLay) and rich interaction APIs [@webcola; @elk] |
| 139 | + - Mature ecosystem and performance characteristics for medium-sized graphs |
| 140 | +- Vue 3 (vs React) for this project: |
| 141 | + - Composition API made integrating an imperative graph library (Cytoscape) straightforward via composables and lifecycle hooks |
| 142 | + - SFC ergonomics and Pinia stores enabled quick iteration with strong TypeScript support |
| 143 | + - Template reactivity + refs reduced reconciliation overhead when bridging to Cytoscape’s imperative API |
| 144 | + - Minimal glue code for state management (Pinia) vs setting up reducers/selectors; enabled rapid iteration |
| 145 | + - Vite + Vue tooling yielded fast HMR for UI-heavy iterations |
| 146 | +- Design inspirations: draw.io for interaction affordances; Stan Playground for model/run UX [@drawio; @stan-playground]. |
| 147 | + |
| 148 | +## Comparison to Legacy DoodleBUGS |
| 149 | + |
| 150 | +The legacy tool was a desktop application driving WinBUGS [@winbugs]; the new DoodleBUGS is a browser-based editor targeting JuliaBUGS [@JuliaBUGS]. Key differences: |
| 151 | + |
| 152 | +- Platform and backend |
| 153 | + - Legacy: Desktop UI, WinBUGS execution pipeline |
| 154 | + - New: Web UI, Julia backend via `JuliaBUGS.@bugs`, sampling with `AdvancedHMC.NUTS` through `AbstractMCMC` |
| 155 | +- Graph engine and plates |
| 156 | + - Legacy: Bespoke graph handling with limited nesting semantics |
| 157 | + - New: Cytoscape.js with compound nodes for robust nested plates; custom DnD for drag-in/out and creating inside plates |
| 158 | +- Layouts and interactions |
| 159 | + - Legacy: Limited auto-layout support |
| 160 | + - New: Multiple layout engines (Cola, Klay) and stable interactions; positions updated after `layoutstop` [@webcola; @elk] |
| 161 | +- Code generation |
| 162 | + - Legacy: Export to BUGS without strong ordering guarantees |
| 163 | + - New: Deterministic topological + plate-aware traversal; parameter canonicalization and safe index expansion |
| 164 | +- Execution and tooling |
| 165 | + - Legacy: WinBUGS-managed runs |
| 166 | + - New: Lightweight Julia HTTP backend, configurable timeouts, resilient temp cleanup, JSON summaries via `MCMCChains` |
| 167 | +- DevX and maintainability |
| 168 | + - New: Vue 3 + TypeScript + Pinia; unified standalone script generation on the frontend; leaner backend responses |
| 169 | + |
| 170 | +## Progress vs Proposal |
| 171 | + |
| 172 | +- Implemented |
| 173 | + - Visual editor with nested plates and robust DnD |
| 174 | + - BUGS code generator (topological + plate-aware) |
| 175 | + - Local execution + summaries/quantiles |
| 176 | + - Unified standalone script generation (frontend) |
| 177 | + - Timeouts/resilience |
| 178 | + - Multiple layouts and interactions |
| 179 | + - Extensive cleanup/typing |
| 180 | + - Execution timeout (end-to-end) |
| 181 | + - Layout options (Cola, Klay) and interactions |
| 182 | + - Cleanup and stronger typing |
| 183 | +- Changed |
| 184 | + - Vue 3 instead of React |
| 185 | + - Backend responses smaller; no standalone script attachment |
| 186 | +- Deferred/Partial |
| 187 | + - Rich diagnostics (R-hat, ESS, PPC, trace/density plots) |
| 188 | + - WebKit/Safari support |
| 189 | + - UX polish for large graphs |
| 190 | + |
| 191 | +## How to Run Locally |
| 192 | + |
| 193 | +Frontend (Vite): |
| 194 | + |
| 195 | +```bash |
| 196 | +# from repo root |
| 197 | +cd DoodleBUGS |
| 198 | +npm install |
| 199 | +npm run dev |
| 200 | +``` |
| 201 | + |
| 202 | +Backend (Julia): |
| 203 | + |
| 204 | +```bash |
| 205 | +# from repo root |
| 206 | +julia --project=DoodleBUGS/runtime DoodleBUGS/runtime/server.jl |
| 207 | +# server listens on http://localhost:8081 |
| 208 | +``` |
| 209 | + |
| 210 | +Notes: |
| 211 | + |
| 212 | +- CORS is enabled in the backend so the dev UI can call `http://localhost:8081`. |
| 213 | +- Live demo (static UI): https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/ |
| 214 | + |
| 215 | +## API Summary |
| 216 | + |
| 217 | +- GET `/api/health` → `{ "status": "ok" }` |
| 218 | +- POST `/api/run` (alias: `/api/run_model`) |
| 219 | + - Body: `model_code`, `data`/`data_string`, `inits`/`inits_string`, `settings` `{ n_samples, n_adapts, n_chains, seed, timeout_s }` |
| 220 | + - Response: `{ success, summary, quantiles, logs, files[] }` |
| 221 | + |
| 222 | +See `DoodleBUGS/runtime/server.jl`. |
| 223 | + |
| 224 | +## Current Limitations |
| 225 | + |
| 226 | +- WebKit/Safari/iOS: unsupported at this time (see `DoodleBUGS/README.md`). |
| 227 | +- Limited visualization beyond summary/quantiles. |
| 228 | +- No persisted projects; session-based. |
| 229 | + |
| 230 | +## Future Work |
| 231 | + |
| 232 | +- Diagnostics/visualization: R-hat, ESS, trace plots, PPC, posterior densities |
| 233 | +- UX: richer node templates, validation, distribution hints |
| 234 | +- Persistence/sharing: save/load and shareable links |
| 235 | +- Browser compatibility: WebKit/Safari and iOS/iPadOS |
| 236 | +- Performance: virtualization for large graphs |
| 237 | + |
| 238 | +## Acknowledgements |
| 239 | + |
| 240 | +Much appreciation goes to my mentors Xianda Sun and Hong Ge. The work is impossible without your help and support. |
| 241 | + |
| 242 | +- Mentor: Xianda Sun ([\@sunxd3](https://github.com/sunxd3)) |
| 243 | +- Advisor: Hong Ge ([\@yebai](https://github.com/yebai)) |
| 244 | +- TuringLang/JuliaBUGS community and contributors |
0 commit comments