Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
266 changes: 266 additions & 0 deletions news/posts/2025-09-01-GSoC-Report-DoodleBUGS/index.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
---
title: "GSoC Report for DoodleBUGS: a Browser-Based Graphical Interface for Drawing Probabilistic Graphical Models"
description: "Shravan Goswami's GSoC 2025 final report: goals, architecture, progress vs proposal, and how to try it."
toc: true
categories:
- GSoC
- Blog
author:
- name: Shravan Goswami
url: https://shravangoswami.com/
date: 2025-09-01
aliases:
- /GSoC-2025-Report-DoodleBUGS # submitted this to GSoC so please remember to update incase moving this page somewhere else
bibliography: references.bib
csl: university-of-york-ieee.csl
link-citations: true
nocite: |
@*
---

## TL;DR

- BUGS (Bayesian Inference Using Gibbs Sampling) is a probabilistic programming language for Bayesian models, and JuliaBUGS is its modern implementation in Julia. DoodleBUGS is a browser-based graphical interface for JuliaBUGS, allowing users to draw probabilistic graphical models and generate BUGS code.
- 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.
- Changed from proposal: frontend implemented in Vue 3 (instead of React); backend simplified (frontend is the single source of truth for standalone scripts).
- Status: Working application. Try it here (static UI): [https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/](https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/). For local inference, run the backend server.

## DoodleBUGS UI

![DoodleBUGS UI](DoodleBUGS.png)

## DoodleBUGS Project Structure

```{.bash}
DoodleBUGS/ # Vite + Vue 3 app (UI editor)
├── README.md # project documentation
├── public/ # static assets served by Vite
│ └── examples/ # example projects
├── experiments/ # prototypes and exploratory work
├── runtime/ # Julia HTTP backend (API endpoints & dependencies)
├── src/ # application source
│ ├── assets/ # styles and static assets
│ ├── components/ # Vue components composing the UI
│ │ ├── canvas/ # graph canvas and toolbars
│ │ ├── common/ # shared UI primitives
│ │ ├── layouts/ # app layout and modals
│ │ │ └── MainLayout.vue # main application layout
│ │ ├── left-sidebar/ # palette, project manager, execution settings
│ │ ├── panels/ # code preview and data input panels
│ │ ├── right-sidebar/ # execution, JSON editor, node properties
│ │ └── ui/ # base UI elements (buttons, inputs, selects)
│ ├── composables/ # reusable logic (codegen, drag & drop, graph, validator, grid)
│ ├── config/ # configuration and node definitions
│ ├── stores/ # Pinia state stores (graph, data, execution, project, UI)
│ └── types/ # TypeScript types and ambient declarations
├── tmp/ # local temporary outputs (ignored in builds)
└── ztest/ # scratch/test artifacts
```

## Motivation

[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 modelling concept with a modern browser-based stack so users can:

- Construct probabilistic graphical models visually (nodes, edges, plates).
- Export readable legacy BUGS code that compiles with JuliaBUGS [@JuliaBUGS; @bugs-rjournal; @bugs-book].
- 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].

## What Was Built

- Visual editor
- Node types: stochastic, observed, deterministic
- Plates with arbitrary nesting; robust drag-in/out and creation inside plates
- Graph layouts: Dagre (default), fCoSE (Force-Directed), Cola (Physics Simulation), KLay (Layered); stable interactions
- Legacy BUGS code generation [@bugs-rjournal; @bugs-book]
- Topological ordering and plate-aware traversal
- Parameter formatting and safe index expansion
- Implemented in `DoodleBUGS/src/composables/useBugsCodeGenerator.ts`
- Execution flow
- Frontend POSTs to `/api/run` with body: `model_code` (BUGS), `data` and `inits` (JSON), `data_string` and `inits_string` (Julia NamedTuple literals), and `settings` `{ n_samples, n_adapts, n_chains, seed, timeout_s }`. If `/api/run` returns 404, it falls back to `/api/run_model`.
- Backend creates a temp dir, writes `model.bugs` and `payload.json`, generates an ephemeral `run_script.jl`, compiles with `JuliaBUGS.@bugs`, wraps with `ADgradient(:ReverseDiff)`, and samples via `AdvancedHMC.NUTS` through `AbstractMCMC` (Threads or Serial). It writes summaries (incl. ESS, R-hat) and quantiles to JSON and returns `{ success, summary, quantiles, logs, files[] }`, where `files` includes `model.bugs`, `payload.json`, `run_script.jl`, and `results.json`.
- Frontend also generates a `standalone.jl` script locally (mirrors backend execution) and shows it alongside the backend files; the backend does not attach a standalone script.
- Timeouts/resilience
- Configurable timeout (frontend); enforced in backend worker
- Safe temp directory cleanup on Windows with retries
- Cleanup/typing
- Strong, project-wide TypeScript typing across stores, components, and composables
- Removal of unused backend code; consistent naming and logs

## Architecture Overview

- Frontend: [Vue 3](https://vuejs.org/), [Pinia](https://pinia.vuejs.org/), [Cytoscape.js](https://js.cytoscape.org/) [@cytoscapejs], [CodeMirror](https://codemirror.net/)
- Code generation: `DoodleBUGS/src/composables/useBugsCodeGenerator.ts`
- Execution panel: `DoodleBUGS/src/components/right-sidebar/ExecutionPanel.vue`
- Backend (Julia) HTTP server
- Server: `DoodleBUGS/runtime/server.jl`
- Project deps: `DoodleBUGS/runtime/Project.toml` (HTTP, JSON3, JuliaBUGS, AbstractMCMC, AdvancedHMC, ReverseDiff, MCMCChains, DataFrames, StatsBase, Statistics)
- Endpoints: GET `/api/health`; POST `/api/run` and `/api/run_model`
- Execution: creates temp dir, writes `model.bugs` and `payload.json`, generates `run_script.jl`, enforces optional timeout

## Design Principles and Architecture

**Design principles**

- Visual-first modeling with deterministic export to legacy BUGS [@bugs-rjournal; @bugs-book].
- Separation of concerns: editing (graph), generation (BUGS), execution (backend), and results (summary/quantiles) are modular.
- Deterministic ordering: topological sort + plate-aware traversal ensures readable, stable code output.
- Robustness: cancellable frontend fetch, backend-enforced timeout, and resilient temp cleanup on Windows (`safe_rmdir()`).

**Frontend architecture (Vue 3 + Cytoscape.js)**

- Core graph state is managed in Vue; [Cytoscape.js](https://js.cytoscape.org/) handles layout, hit-testing, and interaction semantics (including compound nodes for plates) [@cytoscapejs].
- Code generation lives in `DoodleBUGS/src/composables/useBugsCodeGenerator.ts` and maps `GraphNode`/`GraphEdge` to BUGS:
- Kahn topological sort for definition order
- Plate-aware recursion for `for (...) { ... }` blocks
- Parameter canonicalization (indices, numeric/expr passthrough)
- Standalone Julia script generation uses `generateStandaloneScript()` in the same composable, mirroring backend execution.

**Backend architecture (Julia)**

- `run_model_handler()` in `DoodleBUGS/runtime/server.jl` materializes `model.bugs`, `payload.json`, and a transient `run_script.jl` that:
- Builds `NamedTuple`s from JSON or string-literal data/inits
- Compiles via `JuliaBUGS.@bugs`, wraps with `ADgradient(:ReverseDiff)` [@ReverseDiff]
- Samples with `AdvancedHMC.NUTS` through `AbstractMCMC` (Threads or Serial) [@AdvancedHMC; @AbstractMCMC; @HoffmanGelman2014]
- Emits summaries (incl. ESS and R-hat) via `MCMCChains`/`DataFrames` and quantiles to JSON
[@MCMCChains; @DataFrames]
- Timeout: worker process is killed if exceeding `timeout_s`.
- Cleanup: `safe_rmdir()` retries with GC to avoid EBUSY on Windows.

## Why Vue (not React)?

The proposal planned React; we chose Vue 3 after evaluating the graph layer and developer velocity for this app.

- Tried Konva (canvas) for custom graph editing: powerful drawing primitives, but required bespoke graph semantics (hit testing, edge routing, compound nodes) that [Cytoscape.js](https://js.cytoscape.org/) provides out of the box.
- Tried D3 force/layouts: flexible, but compound nodes (plates), nesting, and drag constraints became a significant amount of custom code to maintain.
- [Cytoscape.js](https://js.cytoscape.org/) offered:
- Native graph model with compound nodes (great for plates)
- Integrated layouts (Dagre, fCoSE, Cola, KLay) and rich interaction APIs [@webcola; @elk]
- Mature ecosystem and performance characteristics for medium-sized graphs
- [Vue 3](https://vuejs.org/) (vs React) for this project:
- Composition API made integrating an imperative graph library (Cytoscape) straightforward via composables and lifecycle hooks
- SFC ergonomics and Pinia stores enabled quick iteration with strong TypeScript support
- Template reactivity + refs reduced reconciliation overhead when bridging to Cytoscape’s imperative API
- Minimal glue code for state management (Pinia) vs setting up reducers/selectors; enabled rapid iteration
- Vite + Vue tooling yielded fast HMR for UI-heavy iterations
- Design inspirations: draw.io for interaction affordances; Stan Playground for model/run UX [@drawio; @stan-playground].

## Comparison to Legacy DoodleBUGS

The legacy tool was a windows desktop application driving WinBUGS [@winbugs]; the new DoodleBUGS is a browser-based editor targeting JuliaBUGS [@JuliaBUGS].

![Legacy DoodleBUGS](Legacy-DoodleBUGS.png)

![New DoodleBUGS](New-DoodleBUGS.png)

Key differences:

- Platform and backend
- Legacy: Desktop UI, WinBUGS execution pipeline
- New: Web UI, Julia backend via `JuliaBUGS.@bugs`, sampling with `AdvancedHMC.NUTS` through `AbstractMCMC`
- Graph engine and plates
- Legacy: Bespoke graph handling with limited nesting semantics
- New: [Cytoscape.js](https://js.cytoscape.org/) with compound nodes for robust nested plates; custom drag-and-drop for drag-in/out and creating inside plates
- Layouts and interactions
- Legacy: Limited auto-layout support
- New: Multiple layout engines (Dagre, fCoSE, Cola, KLay) and stable interactions; positions updated after `layoutstop` [@webcola; @elk]
- Code generation
- Legacy: Export to BUGS without strong ordering guarantees
- New: Deterministic topological + plate-aware traversal; parameter canonicalization and safe index expansion
- Execution and tooling
- Legacy: WinBUGS-managed runs
- New: Lightweight Julia HTTP backend, configurable timeouts, resilient temp cleanup, JSON summaries via `MCMCChains`
- DevX and maintainability
- New: Vue 3 + TypeScript + Pinia; unified standalone script generation on the frontend; leaner backend responses

## Progress vs Proposal

- Implemented
- Visual editor with nested plates and robust drag-and-drop
- BUGS code generator (topological + plate-aware)
- Local execution + summaries/quantiles
- Unified standalone script generation (frontend)
- Timeouts/resilience
- Multiple layouts and interactions
- Extensive cleanup/typing
- Execution timeout (end-to-end)
- Layout options (Dagre (default, layered), fCoSE (force-directed), Cola (physics simulation), KLay (layered)) and interactions
- Cleanup and stronger typing
- Changed
- Vue 3 instead of React
- Backend responses smaller; no standalone script attachment
- Deferred/Partial
- Visualizations: integrate with MCMCChains.jl for plots (trace, density, PPC, diagnostics). ESS and R-hat already included in summary statistics.
- WebKit/Safari support
- UX polish for large graphs

## How to Run Locally

Frontend (Vite):

```bash
# from repo root
cd DoodleBUGS
npm install
npm run dev
```

Backend (Julia):

```bash
# from repo root
julia --project=DoodleBUGS/runtime DoodleBUGS/runtime/server.jl
# server listens on http://localhost:8081
```

Notes:

- CORS is enabled in the backend so the dev UI can call `http://localhost:8081`.
- Try it here (static UI): [https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/](https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/)

## API Summary for Backend Server

- GET `/api/health` → `{ "status": "ok" }`
- POST `/api/run` (alias: `/api/run_model`)
- Body: `model_code`, `data` (JSON), `inits` (JSON), `data_string` (Julia literal), `inits_string` (Julia literal), `settings` `{ n_samples, n_adapts, n_chains, seed, timeout_s }`
- Response: `{ success, summary, quantiles, logs, files[] }` where `files[]` contains `model.bugs`, `payload.json`, `run_script.jl`, `results.json`
- Note: Frontend falls back to `/api/run_model` if `/api/run` is unavailable (404)

See `DoodleBUGS/runtime/server.jl`.

## Current Limitations

- WebKit/Safari/iOS: unsupported at this time (see `DoodleBUGS/README.md`).
- Limited visualization beyond summary/quantiles.
- Overlapped plates (nodes with multiple parent plates) are currently not supported; see [#362](https://github.com/TuringLang/JuliaBUGS.jl/issues/362).

## Future Work

- Backend: Add Pluto.jl as a backend for supporting compound documents and QuartoNotebookRunner.jl for running notebooks.
- Diagnostics/visualization: integrate with MCMCChains.jl for plots (trace, density, PPC, diagnostics). ESS and R-hat already available in summary stats.
- UX: richer node templates, validation, distribution hints
- Sharing: shareable links/cloud sync (projects already persisted locally)
- Browser compatibility: WebKit/Safari and iOS/iPadOS
- Performance: virtualization for large graphs

## Acknowledgements

Much appreciation goes to my mentors Xianda Sun and Hong Ge. The work is impossible without your help and support.

- Mentors: Xianda Sun ([\@sunxd3](https://github.com/sunxd3)) and Hong Ge ([\@yebai](https://github.com/yebai))
- TuringLang/JuliaBUGS community and contributors

## Appendix: Project Links

- Repo: [https://github.com/TuringLang/JuliaBUGS.jl](https://github.com/TuringLang/JuliaBUGS.jl)
- Try it here: [https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/](https://turinglang.org/JuliaBUGS.jl/DoodleBUGS/)

## PRs during GSoC:

- [#321 - Add ISSUE template for DoodleBUGS](https://github.com/TuringLang/JuliaBUGS.jl/pull/321)
- [#339 - DoodleBUGS Project: Phase 1](https://github.com/TuringLang/JuliaBUGS.jl/pull/339)
- [#340 - DoodleBUGS: update all workflows to run on relevent project only](https://github.com/TuringLang/JuliaBUGS.jl/pull/340)
- [#341 - Exclude navigation bar from DoodleBUGS project](https://github.com/TuringLang/JuliaBUGS.jl/pull/341)
- [#347 - DoodleBUGS: Basic Code Generation, Advanced Exports, and State Persistence](https://github.com/TuringLang/JuliaBUGS.jl/pull/347)
- [#357 - DoodleBUGS: Allow Nested Plates, add new layouts and fix lot of linting issues](https://github.com/TuringLang/JuliaBUGS.jl/pull/357)
- [#368 - New Folder Structure](https://github.com/TuringLang/JuliaBUGS.jl/pull/368)
- [#388 - DoodleBUGS Project: Phase 2 (Backend) ](https://github.com/TuringLang/JuliaBUGS.jl/pull/388)
104 changes: 104 additions & 0 deletions news/posts/2025-09-01-GSoC-Report-DoodleBUGS/references.bib
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
@misc{bugs-rjournal,
title = {The BUGS Language},
url = {https://journal.r-project.org/articles/RN-2006-005/RN-2006-005.pdf},
note = {R Journal/News article}
}

@misc{bugs-book,
title = {The BUGS Book: A Practical Introduction to Bayesian Analysis},
url = {https://onlinelibrary.wiley.com/doi/10.1111/anzs.12058},
publisher = {Wiley}
}

@misc{bugs-project,
title = {The BUGS Project},
url = {https://www.mrc-bsu.cam.ac.uk/software/bugs/}
}

@misc{multibugs,
title = {MultiBUGS: Parallel BUGS Modeling},
url = {https://pmc.ncbi.nlm.nih.gov/articles/PMC7116196/}
}

@misc{bugs-survival,
title = {Bayesian survival analysis with BUGS},
url = {https://onlinelibrary.wiley.com/doi/10.1002/sim.8933}
}

@misc{albert-chib-1993,
title = {Inference via Gibbs (Albert \& Chib)},
url = {https://apps.olin.wustl.edu/faculty/chib/papers/albertchibjb93.pdf}
}

@misc{informs-gibbs,
title = {Bayesian Inference Using Gibbs Sampling},
url = {https://pubsonline.informs.org/doi/10.1287/ited.2013.0120}
}

@article{HoffmanGelman2014,
title = {The No-U-Turn Sampler: Adaptively Setting Path Lengths in Hamiltonian Monte Carlo},
author = {Hoffman, Matthew D. and Gelman, Andrew},
year = {2014},
url = {https://arxiv.org/abs/1111.4246},
journal = {arXiv preprint arXiv:1111.4246}
}

@misc{AbstractMCMC,
title = {AbstractMCMC.jl},
url = {https://github.com/TuringLang/AbstractMCMC.jl}
}

@misc{AdvancedHMC,
title = {AdvancedHMC.jl},
url = {https://github.com/TuringLang/AdvancedHMC.jl}
}

@misc{ReverseDiff,
title = {ReverseDiff.jl},
url = {https://github.com/JuliaDiff/ReverseDiff.jl}
}

@misc{MCMCChains,
title = {MCMCChains.jl},
url = {https://github.com/TuringLang/MCMCChains.jl}
}

@misc{DataFrames,
title = {DataFrames.jl},
url = {https://dataframes.juliadata.org/}
}

@misc{cytoscapejs,
title = {Cytoscape.js},
url = {https://js.cytoscape.org/}
}

@misc{webcola,
title = {WebCola},
url = {https://ialab.it.monash.edu/webcola/}
}

@misc{elk,
title = {Eclipse Layout Kernel (ELK / KLay)},
url = {https://www.eclipse.org/elk/}
}

@misc{drawio,
title = {draw.io (diagrams.net)},
url = {https://www.diagrams.net/}
}

@misc{stan-playground,
title = {Stan Playground},
url = {https://stan-playground.flatironinstitute.org/}
}

@misc{winbugs,
title = {WinBUGS},
url = {http://www.openbugs.net/w/FrontPage}
}

@misc{JuliaBUGS,
title = {JuliaBUGS.jl},
url = {https://github.com/TuringLang/JuliaBUGS.jl}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<style xmlns="http://purl.org/net/xbiblio/csl" version="1.0" default-locale="en-GB">
<!-- This style was edited with the Visual CSL Editor (http://editor.citationstyles.org/visualEditor/) -->
<info>
<title>University of York - IEEE</title>
<id>http://www.zotero.org/styles/university-of-york-ieee</id>
<link href="http://www.zotero.org/styles/university-of-york-ieee" rel="self"/>
<link href="http://www.zotero.org/styles/ieee" rel="independent-parent"/>
<link href="https://subjectguides.york.ac.uk/referencing-style-guides/ieee" rel="documentation"/>
<category citation-format="numeric"/>
<category field="engineering"/>
<updated>2025-07-11T09:55:45+00:00</updated>
<rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
</info>
</style>
Loading