{ "cells": [ { "cell_type": "raw", "id": "0e4ef35e-e7c0-4123-b58e-018fb6b7554d", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "source": [ "---\n", "title: Load and save graphical models in pyAgrum >2.3.2\n", "date: 2026-04-29\n", "categories: ['notebooks']\n", "authors: Pierre-Henri Wuillemin\n", "tags: snippets\n", "description: \"Comparisons of different input/output formats using pyAgrum\"\n", "---" ] }, { "cell_type": "markdown", "id": "17eba4a8", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "# Loading and saving graphical models" ] }, { "cell_type": "markdown", "id": "b307de73", "metadata": {}, "source": [ "pyAgrum can read and write graphical models in many file formats. This notebook gives an overview of which formats are available for each model type, what they preserve, and why the native **bgum** (binary) and **jgum** (JSON) formats are the best choice for pyAgrum workflows." ] }, { "cell_type": "code", "execution_count": 1, "id": "5f086777", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.724059Z", "iopub.status.busy": "2026-04-29T11:50:19.723852Z", "iopub.status.idle": "2026-04-29T11:50:19.728685Z", "shell.execute_reply": "2026-04-29T11:50:19.727616Z", "shell.execute_reply.started": "2026-04-29T11:50:19.724027Z" }, "tags": [] }, "outputs": [], "source": [ "import os\n", "import tempfile\n", "import time\n", "\n", "import pyagrum as gum\n", "import pyagrum.lib.notebook as gnb" ] }, { "cell_type": "markdown", "id": "e346e51f", "metadata": {}, "source": [ "## Available formats\n", "\n", "The three main model types each have their own set of supported formats." ] }, { "cell_type": "code", "execution_count": 2, "id": "2a3d5c16", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.729355Z", "iopub.status.busy": "2026-04-29T11:50:19.729217Z", "iopub.status.idle": "2026-04-29T11:50:19.733211Z", "shell.execute_reply": "2026-04-29T11:50:19.732443Z", "shell.execute_reply.started": "2026-04-29T11:50:19.729339Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "BayesNet formats : bif|dsl|net|bifxml|o3prm|uai|xdsl|pkl|jgum|bgum\n", "InfluenceDiagram : xmlbif|bifxml|xml|jgum|bgum|pkl\n", "MarkovRandomField : uai|jgum|bgum|pkl\n" ] } ], "source": [ "print(f\"BayesNet formats : {gum.availableBNExts()}\")\n", "print(f\"InfluenceDiagram : {gum.availableIDExts()}\")\n", "print(f\"MarkovRandomField : {gum.availableMRFExts()}\")" ] }, { "cell_type": "markdown", "id": "041921bb", "metadata": {}, "source": [ "The load/save API is uniform across model types:\n", "\n", "| Model | Load | Save |\n", "|-------|------|------|\n", "| `BayesNet` | `gum.loadBN(filename)` | `gum.saveBN(bn, filename)` |\n", "| `InfluenceDiagram` | `gum.loadID(filename)` | `gum.saveID(diag, filename)` |\n", "| `MarkovRandomField` | `gum.loadMRF(filename)` | `gum.saveMRF(mrf, filename)` |\n", "\n", "The format is selected automatically from the file extension." ] }, { "cell_type": "markdown", "id": "0a563688", "metadata": {}, "source": [ "## Bayesian networks\n", "\n", "### A tour of BN formats\n", "\n", "Let's load the classic Asia network and save/reload it in every available format, comparing file size and round-trip speed." ] }, { "cell_type": "code", "execution_count": 3, "id": "1157629f", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.733862Z", "iopub.status.busy": "2026-04-29T11:50:19.733735Z", "iopub.status.idle": "2026-04-29T11:50:19.852592Z", "shell.execute_reply": "2026-04-29T11:50:19.852046Z", "shell.execute_reply.started": "2026-04-29T11:50:19.733849Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", " \n", "
\n", "\n", "G\n", "\n", "\n", "Smoking\n", "\n", "\n", "Smoking\n", "\n", "\n", "\n", "\n", "\n", "Lung cancer\n", "\n", "\n", "Lung cancer\n", "\n", "\n", "\n", "\n", "\n", "Smoking->Lung cancer\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Bronchitis\n", "\n", "\n", "Bronchitis\n", "\n", "\n", "\n", "\n", "\n", "Smoking->Bronchitis\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Dyspnoea\n", "\n", "\n", "Dyspnoea\n", "\n", "\n", "\n", "\n", "\n", "TorC\n", "\n", "\n", "TorC\n", "\n", "\n", "\n", "\n", "\n", "Lung cancer->TorC\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Asia\n", "\n", "\n", "Asia\n", "\n", "\n", "\n", "\n", "\n", "Tuberculosis\n", "\n", "\n", "Tuberculosis\n", "\n", "\n", "\n", "\n", "\n", "Asia->Tuberculosis\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "chest XRay\n", "\n", "\n", "chest XRay\n", "\n", "\n", "\n", "\n", "\n", "Bronchitis->Dyspnoea\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "Tuberculosis->TorC\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "TorC->Dyspnoea\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "TorC->chest XRay\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
Asia BN — 8 nodes, 8 arcs
" ], "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bn_asia = gum.fastBN(\"Asia->Tuberculosis->TorC->chest XRay;TorC<-Lung cancer<-Smoking->Bronchitis->Dyspnoea<-TorC\")\n", "gnb.flow.row(bn_asia, captions=[f\"Asia BN — {bn_asia.size()} nodes, {bn_asia.sizeArcs()} arcs\"])" ] }, { "cell_type": "code", "execution_count": 4, "id": "cf94ed14", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.853074Z", "iopub.status.busy": "2026-04-29T11:50:19.852983Z", "iopub.status.idle": "2026-04-29T11:50:19.863630Z", "shell.execute_reply": "2026-04-29T11:50:19.863185Z", "shell.execute_reply.started": "2026-04-29T11:50:19.853064Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ext size (B) save (ms) load (ms) names labels types\n", "-----------------------------------------------------------------\n", "bif (unsupported for this model)\n", "dsl (unsupported for this model)\n", "net (unsupported for this model)\n", "bifxml 3593 0.189 0.237 True True True\n", "o3prm (unsupported for this model)\n", "uai 587 0.133 0.107 False False False\n", "xdsl 2342 0.481 0.119 True True False\n", "pkl 1940 0.178 0.104 True True True\n", "jgum 1887 0.115 0.066 True True True\n", "bgum 836 0.098 0.059 True True True\n" ] } ], "source": [ "def benchmark_bn(bn, exts):\n", " \"\"\"Save and reload a BN in every given format, return a comparison table.\"\"\"\n", " rows = []\n", " with tempfile.TemporaryDirectory() as d:\n", " for ext in exts.split(\"|\"):\n", " fname = os.path.join(d, f\"model.{ext}\")\n", " try:\n", " t0 = time.perf_counter()\n", " gum.saveBN(bn, fname)\n", " t_save = time.perf_counter() - t0\n", " size = os.path.getsize(fname)\n", " t0 = time.perf_counter()\n", " bn2 = gum.loadBN(fname)\n", " t_load = time.perf_counter() - t0\n", " names_ok = sorted(bn.names()) == sorted(bn2.names())\n", " labels_ok = names_ok and all(\n", " list(bn.variable(n).labels()) == list(bn2.variable(n).labels())\n", " for n in bn.names()\n", " )\n", " types_ok = names_ok and all(\n", " bn.variable(n).varType() == bn2.variable(n).varType()\n", " for n in bn.names()\n", " )\n", " rows.append((ext, size, t_save * 1000, t_load * 1000, names_ok, labels_ok, types_ok))\n", " except Exception as e:\n", " rows.append((ext, None, None, None, False, False, str(e)[:60]))\n", " return rows\n", "\n", "\n", "rows = benchmark_bn(bn_asia, gum.availableBNExts())\n", "\n", "header = f\"{'ext':8s} {'size (B)':>9s} {'save (ms)':>10s} {'load (ms)':>10s} {'names':>5s} {'labels':>6s} {'types':>5s}\"\n", "print(header)\n", "print(\"-\" * len(header))\n", "for ext, size, ts, tl, n_ok, l_ok, t_ok in rows:\n", " if size is not None:\n", " print(f\"{ext:8s} {size:9d} {ts:10.3f} {tl:10.3f} {str(n_ok):>5s} {str(l_ok):>6s} {str(t_ok):>5s}\")\n", " else:\n", " print(f\"{ext:8s} (unsupported for this model)\")" ] }, { "cell_type": "markdown", "id": "0269c3d4", "metadata": {}, "source": [ "### Variable type fidelity\n", "\n", "Many real-world networks contain variables of heterogeneous types: labelled (`LabelizedVariable`), integer ranges (`RangeVariable`) or discretized continuous domains (`DiscretizedVariable`). Not all formats can represent these faithfully." ] }, { "cell_type": "code", "execution_count": 5, "id": "bb52c103", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.864143Z", "iopub.status.busy": "2026-04-29T11:50:19.864052Z", "iopub.status.idle": "2026-04-29T11:50:19.867629Z", "shell.execute_reply": "2026-04-29T11:50:19.867304Z", "shell.execute_reply.started": "2026-04-29T11:50:19.864134Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variable types in bn_mixed:\n", " Smoker : Labelized\n", " Cancer : Labelized\n", " Temp : Discretized\n", " Age : Range\n" ] } ], "source": [ "# Build a BN that mixes all main variable types\n", "bn_mixed = gum.BayesNet(\"mixed_types\")\n", "bn_mixed.add(gum.LabelizedVariable(\"Smoker\", \"Smoker\", [\"yes\", \"no\"]))\n", "bn_mixed.add(gum.RangeVariable(\"Age\", \"Age\", 20, 60))\n", "bn_mixed.add(gum.DiscretizedVariable(\"Temp\", \"Temp\", [36.0, 37.0, 38.5, 42.0]))\n", "bn_mixed.add(gum.LabelizedVariable(\"Cancer\", \"Cancer\", [\"yes\", \"no\"]))\n", "bn_mixed.addArc(\"Smoker\", \"Cancer\")\n", "bn_mixed.addArc(\"Age\", \"Cancer\")\n", "bn_mixed.addArc(\"Temp\", \"Cancer\")\n", "bn_mixed.cpt(\"Smoker\").fillWith([0.3, 0.7])\n", "bn_mixed.cpt(\"Age\").fillWith(1).normalize()\n", "bn_mixed.cpt(\"Temp\").fillWith(1).normalize()\n", "bn_mixed.cpt(\"Cancer\").fillWith(1).normalize()\n", "\n", "print(\"Variable types in bn_mixed:\")\n", "type_names = {\n", " gum.VarType_LABELIZED: \"Labelized\",\n", " gum.VarType_DISCRETIZED: \"Discretized\",\n", " gum.VarType_RANGE: \"Range\",\n", " gum.VarType_INTEGER: \"Integer\",\n", " gum.VarType_NUMERICAL: \"Numerical\",\n", "}\n", "for n in bn_mixed.names():\n", " v = bn_mixed.variable(n)\n", " print(f\" {n:10s}: {type_names.get(v.varType(), f'type={v.varType()}')}\")" ] }, { "cell_type": "code", "execution_count": 6, "id": "3f6553b7", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.868056Z", "iopub.status.busy": "2026-04-29T11:50:19.867932Z", "iopub.status.idle": "2026-04-29T11:50:19.889264Z", "shell.execute_reply": "2026-04-29T11:50:19.888805Z", "shell.execute_reply.started": "2026-04-29T11:50:19.868041Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ext size (B) save (ms) load (ms) names labels types\n", "-----------------------------------------------------------------\n", "bif (unsupported for this model)\n", "dsl (unsupported for this model)\n", "net (unsupported for this model)\n", "bifxml 8951 0.267 0.483 True True True\n", "o3prm (unsupported for this model)\n", "uai 6703 0.307 0.625 False False False\n", "xdsl 7886 0.554 0.519 True True False\n", "pkl 16127 0.255 0.176 True True True\n", "jgum 16074 0.153 0.140 True True True\n", "bgum 5160 0.157 0.074 True True True\n" ] } ], "source": [ "rows = benchmark_bn(bn_mixed, gum.availableBNExts())\n", "\n", "header = f\"{'ext':8s} {'size (B)':>9s} {'save (ms)':>10s} {'load (ms)':>10s} {'names':>5s} {'labels':>6s} {'types':>5s}\"\n", "print(header)\n", "print(\"-\" * len(header))\n", "for ext, size, ts, tl, n_ok, l_ok, t_ok in rows:\n", " if size is not None:\n", " print(f\"{ext:8s} {size:9d} {ts:10.3f} {tl:10.3f} {str(n_ok):>5s} {str(l_ok):>6s} {str(t_ok):>5s}\")\n", " else:\n", " print(f\"{ext:8s} (unsupported for this model)\")" ] }, { "cell_type": "markdown", "id": "6e24042b", "metadata": {}, "source": [ "**Observations for BN:**\n", "\n", "| Format | Notes |\n", "|--------|-------|\n", "| `bif`, `dsl`, `net` | Classic, widely used, but do **not** support `DiscretizedVariable` |\n", "| `bifxml` / `xdsl` | XML-based; `bifxml` preserves types, `xdsl` does not |\n", "| `uai` | Compact but loses variable names |\n", "| `o3prm` | Verbose; requires a full class hierarchy |\n", "| `pkl` | Python pickle; preserves everything but not portable across pyAgrum versions |\n", "| **`jgum`** | Native JSON format; preserves all types, human-readable |\n", "| **`bgum`** | Native binary format; smallest files, fastest I/O, preserves all types |" ] }, { "cell_type": "markdown", "id": "1ccefcc3", "metadata": {}, "source": [ "## Influence diagrams\n", "\n", "Influence diagrams have fewer supported formats than BNs." ] }, { "cell_type": "code", "execution_count": 7, "id": "0e363a51", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:19.889735Z", "iopub.status.busy": "2026-04-29T11:50:19.889650Z", "iopub.status.idle": "2026-04-29T11:50:20.124219Z", "shell.execute_reply": "2026-04-29T11:50:20.123684Z", "shell.execute_reply.started": "2026-04-29T11:50:19.889727Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", " \n", "
\n", "\n", "\n", "\n", "TestResult\n", "\n", "TestResult\n", "\n", "\n", "\n", "Drilling\n", "\n", "Drilling\n", "\n", "\n", "\n", "TestResult->Drilling\n", "\n", "\n", "\n", "\n", "\n", "OilContents\n", "\n", "OilContents\n", "\n", "\n", "\n", "OilContents->TestResult\n", "\n", "\n", "\n", "\n", "\n", "Reward\n", "\n", "Reward\n", "\n", "\n", "\n", "OilContents->Reward\n", "\n", "\n", "\n", "\n", "\n", "Testing\n", "\n", "Testing\n", "\n", "\n", "\n", "Testing->TestResult\n", "\n", "\n", "\n", "\n", "\n", "Testing->Drilling\n", "\n", "\n", "\n", "\n", "\n", "Cost\n", "\n", "Cost\n", "\n", "\n", "\n", "Testing->Cost\n", "\n", "\n", "\n", "\n", "\n", "Drilling->Reward\n", "\n", "\n", "\n", "\n", "
Oil Wildcatter — 6 nodes
" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Classic Oil Wildcatter influence diagram\n", "diag = gum.fastID(\"$Cost<-*Testing->TestResult<-OilContents->Reward<-*Drilling<-TestResult;Drilling<-Testing\")\n", "gnb.flow.row(diag, captions=[f\"Oil Wildcatter — {diag.size()} nodes\"])" ] }, { "cell_type": "code", "execution_count": 8, "id": "d6d36ee4", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.125022Z", "iopub.status.busy": "2026-04-29T11:50:20.124801Z", "iopub.status.idle": "2026-04-29T11:50:20.131117Z", "shell.execute_reply": "2026-04-29T11:50:20.130686Z", "shell.execute_reply.started": "2026-04-29T11:50:20.125005Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ext size (B) save (ms) load (ms) names\n", "--------------------------------------------------\n", "xmlbif 2664 0.249 0.222 True\n", "bifxml 2664 0.125 0.111 True\n", "xml 2664 0.117 0.097 True\n", "jgum 1299 0.136 0.079 True\n", "bgum 609 0.097 0.058 True\n", "pkl 1360 0.148 0.073 True\n" ] } ], "source": [ "def benchmark_id(diag, exts):\n", " rows = []\n", " with tempfile.TemporaryDirectory() as d:\n", " for ext in exts.split(\"|\"):\n", " fname = os.path.join(d, f\"model.{ext}\")\n", " try:\n", " t0 = time.perf_counter()\n", " gum.saveID(diag, fname)\n", " t_save = time.perf_counter() - t0\n", " size = os.path.getsize(fname)\n", " t0 = time.perf_counter()\n", " diag2 = gum.loadID(fname)\n", " t_load = time.perf_counter() - t0\n", " names_ok = sorted(diag.names()) == sorted(diag2.names())\n", " rows.append((ext, size, t_save * 1000, t_load * 1000, names_ok))\n", " except Exception as e:\n", " rows.append((ext, None, None, None, str(e)[:60]))\n", " return rows\n", "\n", "\n", "rows = benchmark_id(diag, gum.availableIDExts())\n", "\n", "header = f\"{'ext':8s} {'size (B)':>9s} {'save (ms)':>10s} {'load (ms)':>10s} {'names':>5s}\"\n", "print(header)\n", "print(\"-\" * len(header))\n", "for ext, size, ts, tl, n_ok in rows:\n", " if size is not None:\n", " print(f\"{ext:8s} {size:9d} {ts:10.3f} {tl:10.3f} {str(n_ok):>5s}\")\n", " else:\n", " print(f\"{ext:8s} (unsupported for this model)\")" ] }, { "cell_type": "markdown", "id": "291de8a8", "metadata": {}, "source": [ "**Observations for InfluenceDiagram:**\n", "\n", "| Format | Notes |\n", "|--------|-------|\n", "| `bifxml` / `xmlbif` / `xml` | Three aliases for the same XML format |\n", "| `pkl` | Portable only within the same pyAgrum version |\n", "| **`jgum`** | Compact JSON, fully faithful, human-readable |\n", "| **`bgum`** | Smallest files, fastest I/O |" ] }, { "cell_type": "markdown", "id": "8e164c83", "metadata": {}, "source": [ "## Markov random fields\n", "\n", "MRFs have the smallest set of supported formats." ] }, { "cell_type": "code", "execution_count": 9, "id": "5450f23b", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.131474Z", "iopub.status.busy": "2026-04-29T11:50:20.131388Z", "iopub.status.idle": "2026-04-29T11:50:20.236164Z", "shell.execute_reply": "2026-04-29T11:50:20.235504Z", "shell.execute_reply.started": "2026-04-29T11:50:20.131466Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", " \n", "
\n", "\n", "G\n", "\n", "\n", "A\n", "\n", "\n", "A\n", "\n", "\n", "\n", "\n", "\n", "C\n", "\n", "\n", "C\n", "\n", "\n", "\n", "\n", "\n", "D\n", "\n", "\n", "D\n", "\n", "\n", "\n", "\n", "\n", "B\n", "\n", "\n", "B\n", "\n", "\n", "\n", "\n", "\n", "f0#1#2\n", "\n", "\n", "\n", "\n", "f0#1#2--A\n", "\n", "\n", "\n", "\n", "f0#1#2--C\n", "\n", "\n", "\n", "\n", "f0#1#2--B\n", "\n", "\n", "\n", "\n", "f1#3\n", "\n", "\n", "\n", "\n", "f1#3--D\n", "\n", "\n", "\n", "\n", "f1#3--B\n", "\n", "\n", "\n", "
MRF — 4 nodes, 4 edges
" ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mrf = gum.fastMRF(\"A{yes|no}--B{low|mid|high}--C{yes|no}--A;B--D{yes|no}\")\n", "gnb.flow.row(mrf, captions=[f\"MRF — {mrf.size()} nodes, {mrf.sizeEdges()} edges\"])" ] }, { "cell_type": "code", "execution_count": 10, "id": "91b6921a", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.236946Z", "iopub.status.busy": "2026-04-29T11:50:20.236754Z", "iopub.status.idle": "2026-04-29T11:50:20.243636Z", "shell.execute_reply": "2026-04-29T11:50:20.243059Z", "shell.execute_reply.started": "2026-04-29T11:50:20.236931Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ext size (B) save (ms) load (ms) names labels\n", "----------------------------------------------------------\n", "uai 280 0.202 0.194 False False\n", "jgum 1029 0.131 0.080 True True\n", "bgum 428 0.103 0.058 True True\n", "pkl 1091 0.157 0.124 True True\n" ] } ], "source": [ "def benchmark_mrf(mrf, exts):\n", " rows = []\n", " with tempfile.TemporaryDirectory() as d:\n", " for ext in exts.split(\"|\"):\n", " fname = os.path.join(d, f\"model.{ext}\")\n", " try:\n", " t0 = time.perf_counter()\n", " gum.saveMRF(mrf, fname)\n", " t_save = time.perf_counter() - t0\n", " size = os.path.getsize(fname)\n", " t0 = time.perf_counter()\n", " mrf2 = gum.loadMRF(fname)\n", " t_load = time.perf_counter() - t0\n", " names_ok = sorted(mrf.names()) == sorted(mrf2.names())\n", " labels_ok = names_ok and all(\n", " list(mrf.variable(n).labels()) == list(mrf2.variable(n).labels())\n", " for n in mrf.names()\n", " )\n", " rows.append((ext, size, t_save * 1000, t_load * 1000, names_ok, labels_ok))\n", " except Exception as e:\n", " rows.append((ext, None, None, None, False, str(e)[:60]))\n", " return rows\n", "\n", "\n", "rows = benchmark_mrf(mrf, gum.availableMRFExts())\n", "\n", "header = f\"{'ext':8s} {'size (B)':>9s} {'save (ms)':>10s} {'load (ms)':>10s} {'names':>5s} {'labels':>6s}\"\n", "print(header)\n", "print(\"-\" * len(header))\n", "for ext, size, ts, tl, n_ok, l_ok in rows:\n", " if size is not None:\n", " print(f\"{ext:8s} {size:9d} {ts:10.3f} {tl:10.3f} {str(n_ok):>5s} {str(l_ok):>6s}\")\n", " else:\n", " print(f\"{ext:8s} (unsupported for this model)\")" ] }, { "cell_type": "markdown", "id": "5f591c54", "metadata": {}, "source": [ "**Observations for MRF:**\n", "\n", "| Format | Notes |\n", "|--------|-------|\n", "| `uai` | Only standard MRF format, but loses variable names and labels |\n", "| `pkl` | Portable only within the same pyAgrum version |\n", "| **`jgum`** | Full fidelity, JSON, readable |\n", "| **`bgum`** | Smallest, fastest, full fidelity |" ] }, { "cell_type": "markdown", "id": "fb8c1260", "metadata": {}, "source": [ "## Why bgum and jgum are the best choice for pyAgrum\n", "\n", "The `bgum` and `jgum` formats are the **native aGrUM formats**, designed specifically for all model types supported by pyAgrum. They share the same advantages:\n", "\n", "1. **Universal** — same format works for `BayesNet`, `InfluenceDiagram` and `MarkovRandomField`.\n", "2. **Full fidelity** — all variable types (`LabelizedVariable`, `RangeVariable`, `DiscretizedVariable`, `IntegerVariable`) are preserved exactly.\n", "3. **Fast** — both I/O are among the fastest of all formats.\n", "4. **Compact** — `bgum` typically produces the smallest files; `jgum` is still compact while remaining human-readable.\n", "5. **No external dependencies** — no need for third-party parsers.\n", "\n", "The only difference between the two is readability:\n", "- **`bgum`** (binary) — optimal for production workflows, automated pipelines, storing large models.\n", "- **`jgum`** (JSON) — easier to inspect, diff, or version-control.\n", "\n", "### Quick demo: round-trip with bgum and jgum" ] }, { "cell_type": "code", "execution_count": 11, "id": "7b6b9d6b", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.244187Z", "iopub.status.busy": "2026-04-29T11:50:20.244091Z", "iopub.status.idle": "2026-04-29T11:50:20.247827Z", "shell.execute_reply": "2026-04-29T11:50:20.247357Z", "shell.execute_reply.started": "2026-04-29T11:50:20.244179Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variable types before save:\n", " Smoker : Labelized labels=['yes', 'no']\n", " Cancer : Labelized labels=['yes', 'no']\n", " Temp : Discretized labels=['[36;37[', '[37;38.5[', '[38.5;42]']\n", " Age : Range labels=['20', '21', '22', '23']...\n" ] } ], "source": [ "# Build a BN with mixed variable types to stress-test fidelity\n", "bn_demo = gum.BayesNet(\"demo\")\n", "bn_demo.add(gum.LabelizedVariable(\"Smoker\", \"Smoker\", [\"yes\", \"no\"]))\n", "bn_demo.add(gum.RangeVariable(\"Age\", \"Age\", 20, 60))\n", "bn_demo.add(gum.DiscretizedVariable(\"Temp\", \"Temp\", [36.0, 37.0, 38.5, 42.0]))\n", "bn_demo.add(gum.LabelizedVariable(\"Cancer\", \"Cancer\", [\"yes\", \"no\"]))\n", "bn_demo.addArc(\"Smoker\", \"Cancer\")\n", "bn_demo.addArc(\"Age\", \"Cancer\")\n", "bn_demo.addArc(\"Temp\", \"Cancer\")\n", "bn_demo.cpt(\"Smoker\").fillWith([0.3, 0.7])\n", "bn_demo.cpt(\"Age\").fillWith(1).normalize()\n", "bn_demo.cpt(\"Temp\").fillWith(1).normalize()\n", "bn_demo.cpt(\"Cancer\").fillWith(1).normalize()\n", "\n", "type_names = {\n", " gum.VarType_LABELIZED: \"Labelized\",\n", " gum.VarType_DISCRETIZED: \"Discretized\",\n", " gum.VarType_RANGE: \"Range\",\n", " gum.VarType_INTEGER: \"Integer\",\n", " gum.VarType_NUMERICAL: \"Numerical\",\n", "}\n", "\n", "print(\"Variable types before save:\")\n", "for n in bn_demo.names():\n", " v = bn_demo.variable(n)\n", " print(f\" {n:8s}: {type_names.get(v.varType(), f'type={v.varType()}'):12s} labels={list(v.labels()[:4])}{'...' if v.domainSize() > 4 else ''}\")" ] }, { "cell_type": "code", "execution_count": 12, "id": "a911e17d", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.248159Z", "iopub.status.busy": "2026-04-29T11:50:20.248084Z", "iopub.status.idle": "2026-04-29T11:50:20.254305Z", "shell.execute_reply": "2026-04-29T11:50:20.253682Z", "shell.execute_reply.started": "2026-04-29T11:50:20.248151Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--- bgum (5153 bytes) ---\n", " Smoker : Labelized labels=['yes', 'no']\n", " Cancer : Labelized labels=['yes', 'no']\n", " Temp : Discretized labels=['[36;37[', '[37;38.5[', '[38.5;42]']\n", " Age : Range labels=['20', '21', '22', '23']...\n", "\n", "--- jgum (16067 bytes) ---\n", " Smoker : Labelized labels=['yes', 'no']\n", " Cancer : Labelized labels=['yes', 'no']\n", " Temp : Discretized labels=['[36;37[', '[37;38.5[', '[38.5;42]']\n", " Age : Range labels=['20', '21', '22', '23']...\n" ] } ], "source": [ "with tempfile.TemporaryDirectory() as d:\n", " for ext in (\"bgum\", \"jgum\"):\n", " fname = os.path.join(d, f\"demo.{ext}\")\n", " gum.saveBN(bn_demo, fname)\n", " bn2 = gum.loadBN(fname)\n", " print(f\"\\n--- {ext} ({os.path.getsize(fname)} bytes) ---\")\n", " for n in bn2.names():\n", " v = bn2.variable(n)\n", " print(f\" {n:8s}: {type_names.get(v.varType(), f'type={v.varType()}'):12s} labels={list(v.labels()[:4])}{'...' if v.domainSize() > 4 else ''}\")" ] }, { "cell_type": "code", "execution_count": 13, "id": "4696dd7a", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.255034Z", "iopub.status.busy": "2026-04-29T11:50:20.254913Z", "iopub.status.idle": "2026-04-29T11:50:20.258680Z", "shell.execute_reply": "2026-04-29T11:50:20.258213Z", "shell.execute_reply.started": "2026-04-29T11:50:20.255024Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\n", " \"Smoker{yes|no}\",\n", " \"Age[20,60]\",\n", " \"Temp[36,37,38.5,42]\",\n", " \"Cancer{yes|no}\"\n", "]\n" ] } ], "source": [ "# jgum is plain JSON — easy to inspect\n", "import json\n", "\n", "with tempfile.TemporaryDirectory() as d:\n", " fname = os.path.join(d, \"demo.jgum\")\n", " gum.saveBN(bn_demo, fname)\n", " with open(fname) as f:\n", " data = json.load(f)\n", " # Show just the variable descriptions\n", " print(json.dumps(data.get(\"variables\", data.get(\"nodes\", {})), indent=2))" ] }, { "cell_type": "markdown", "id": "8c1d2f5d", "metadata": {}, "source": [ "### bgum and jgum work identically for all model types" ] }, { "cell_type": "code", "execution_count": 14, "id": "c7386f77", "metadata": { "execution": { "iopub.execute_input": "2026-04-29T11:50:20.259057Z", "iopub.status.busy": "2026-04-29T11:50:20.258985Z", "iopub.status.idle": "2026-04-29T11:50:20.262914Z", "shell.execute_reply": "2026-04-29T11:50:20.262541Z", "shell.execute_reply.started": "2026-04-29T11:50:20.259048Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "BN round-trip via bgum: names match = True\n", "ID round-trip via bgum: names match = True\n", "MRF round-trip via bgum: names match = True\n" ] } ], "source": [ "with tempfile.TemporaryDirectory() as d:\n", " # BayesNet\n", " gum.saveBN(bn_asia, os.path.join(d, \"asia.bgum\"))\n", " bn_rt = gum.loadBN(os.path.join(d, \"asia.bgum\"))\n", " print(f\"BN round-trip via bgum: names match = {sorted(bn_asia.names()) == sorted(bn_rt.names())}\")\n", "\n", " # InfluenceDiagram\n", " gum.saveID(diag, os.path.join(d, \"oil.bgum\"))\n", " diag_rt = gum.loadID(os.path.join(d, \"oil.bgum\"))\n", " print(f\"ID round-trip via bgum: names match = {sorted(diag.names()) == sorted(diag_rt.names())}\")\n", "\n", " # MarkovRandomField\n", " gum.saveMRF(mrf, os.path.join(d, \"mrf.bgum\"))\n", " mrf_rt = gum.loadMRF(os.path.join(d, \"mrf.bgum\"))\n", " print(f\"MRF round-trip via bgum: names match = {sorted(mrf.names()) == sorted(mrf_rt.names())}\")" ] }, { "cell_type": "markdown", "id": "800169c6", "metadata": {}, "source": [ "## Summary\n", "\n", "| | `bif`/`dsl`/`net` | `bifxml`/`xdsl` | `uai` | `pkl` | **`jgum`** | **`bgum`** |\n", "|---|---|---|---|---|---|---|\n", "| BayesNet | ✓ | ✓ | partial | ✓ | ✓ | ✓ |\n", "| InfluenceDiagram | ✗ | ✓ | ✗ | ✓ | ✓ | ✓ |\n", "| MarkovRandomField | ✗ | ✗ | partial | ✓ | ✓ | ✓ |\n", "| Preserves all variable types | ✗ | partial | ✗ | ✓ | ✓ | ✓ |\n", "| Preserves variable names | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ |\n", "| Human-readable | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |\n", "| Compact size | medium | large | small | medium | small | **smallest** |\n", "| I/O speed | medium | medium | fast | fast | fast | **fastest** |\n", "| Version-stable | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ |\n", "\n", "**Recommendation:**\n", "- Use **`bgum`** whenever storage size or I/O speed matters, or when working with non-BN models.\n", "- Use **`jgum`** when the file needs to be inspected, diffed, or stored in version control.\n", "- Use **`bif`/`bifxml`** only when interoperability with other tools (GeNIe, Netica, …) is required." ] }, { "cell_type": "code", "execution_count": null, "id": "af22df73-ee75-42b7-9802-a78d555a90e7", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "hide_input": false, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.4" } }, "nbformat": 4, "nbformat_minor": 5 }