From 3c26d1f6137ffaee607cf3f4beaf8cc652c740d5 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 19 Apr 2025 23:39:56 -0400 Subject: [PATCH] Symbolic docs --- gtsam/symbolic/doc/SymbolicBayesNet.ipynb | 231 +++++++++ gtsam/symbolic/doc/SymbolicBayesTree.ipynb | 325 ++++++++++++ .../doc/SymbolicBayesTreeClique.ipynb | 262 ++++++++++ gtsam/symbolic/doc/SymbolicConditional.ipynb | 142 ++++++ .../doc/SymbolicEliminationTree.ipynb | 138 +++++ gtsam/symbolic/doc/SymbolicFactor.ipynb | 131 +++++ gtsam/symbolic/doc/SymbolicFactorGraph.ipynb | 479 ++++++++++++++++++ gtsam/symbolic/doc/SymbolicJunctionTree.ipynb | 133 +++++ gtsam/symbolic/symbolic.md | 40 ++ 9 files changed, 1881 insertions(+) create mode 100644 gtsam/symbolic/doc/SymbolicBayesNet.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicBayesTree.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicBayesTreeClique.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicConditional.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicEliminationTree.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicFactor.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicFactorGraph.ipynb create mode 100644 gtsam/symbolic/doc/SymbolicJunctionTree.ipynb create mode 100644 gtsam/symbolic/symbolic.md diff --git a/gtsam/symbolic/doc/SymbolicBayesNet.ipynb b/gtsam/symbolic/doc/SymbolicBayesNet.ipynb new file mode 100644 index 000000000..49d6d4b43 --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicBayesNet.ipynb @@ -0,0 +1,231 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicBayesNet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicBayesNet` is a directed acyclic graph (DAG) composed of `SymbolicConditional` objects. It represents the structure of a factorized probability distribution P(X) = Π P(Xi | Parents(Xi)) purely in terms of variable connectivity.\n", + "\n", + "It is typically the result of running sequential variable elimination on a `SymbolicFactorGraph`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicConditional, SymbolicFactorGraph, Ordering\n", + "from gtsam.symbol_shorthand import X, L\n", + "import graphviz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a SymbolicBayesNet\n", + "\n", + "SymbolicBayesNets are usually created by eliminating a [SymbolicFactorGraph](SymbolicFactorGraph.ipynb). But you can also build them directly:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Directly Built Symbolic Bayes Net:\n", + " \n", + "size: 5\n", + "conditional 0: P( l1 | x0)\n", + "conditional 1: P( x0 | x1)\n", + "conditional 2: P( l2 | x1)\n", + "conditional 3: P( x1 | x2)\n", + "conditional 4: P( x2)\n" + ] + } + ], + "source": [ + "from gtsam import SymbolicBayesNet\n", + "\n", + "# Create a new Bayes Net\n", + "symbolic_bayes_net = SymbolicBayesNet()\n", + "\n", + "# Add conditionals directly\n", + "symbolic_bayes_net.push_back(SymbolicConditional(L(1), X(0))) # P(l1 | x0)\n", + "symbolic_bayes_net.push_back(SymbolicConditional(X(0), X(1))) # P(x0 | x1)\n", + "symbolic_bayes_net.push_back(SymbolicConditional(L(2), X(1))) # P(l2 | x1)\n", + "symbolic_bayes_net.push_back(SymbolicConditional(X(1), X(2))) # P(x1 | x2)\n", + "symbolic_bayes_net.push_back(SymbolicConditional(X(2))) # P(x2)\n", + "\n", + "symbolic_bayes_net.print(\"Directly Built Symbolic Bayes Net:\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Accessing Conditionals and Visualization" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Conditional at index 1: P( x0 | x1)\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var7782220156096217089\n", + "\n", + "l1\n", + "\n", + "\n", + "\n", + "var7782220156096217090\n", + "\n", + "l2\n", + "\n", + "\n", + "\n", + "var8646911284551352320\n", + "\n", + "x0\n", + "\n", + "\n", + "\n", + "var8646911284551352320->var7782220156096217089\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321\n", + "\n", + "x1\n", + "\n", + "\n", + "\n", + "var8646911284551352321->var7782220156096217090\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321->var8646911284551352320\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352322\n", + "\n", + "x2\n", + "\n", + "\n", + "\n", + "var8646911284551352322->var8646911284551352321\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Access a conditional by index\n", + "conditional_1 = bayes_net.at(1) # P(x0 | l1)\n", + "conditional_1.print(\"Conditional at index 1: \")\n", + "\n", + "# Visualize the Bayes Net structure\n", + "display(graphviz.Source(bayes_net.dot()))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicBayesTree.ipynb b/gtsam/symbolic/doc/SymbolicBayesTree.ipynb new file mode 100644 index 000000000..c6c7aea55 --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicBayesTree.ipynb @@ -0,0 +1,325 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicBayesTree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicBayesTree` is a tree structure where each node (`SymbolicBayesTreeClique`) represents a clique of variables that were eliminated together during multifrontal elimination. Unlike a `SymbolicJunctionTree` which stores factors, a `SymbolicBayesTree` stores the resulting `SymbolicConditional` for each clique.\n", + "\n", + "It represents the factored structure $P(X) = Π P(C_j | S_j)$, where $C_j$ are the frontal variables of clique $j$ and $S_j$ are its separator variables (parents in the Bayes Tree). This structure is computationally advantageous for inference, especially for calculating marginals or performing incremental updates (like in iSAM)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicFactorGraph, Ordering\n", + "from gtsam.symbol_shorthand import X, L\n", + "import graphviz # For visualization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a SymbolicBayesTree\n", + "\n", + "A Bayes tree is typically created by eliminating a `SymbolicFactorGraph` using the multifrontal method." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a factor graph from a GTSFM problem\n", + "graph = SymbolicFactorGraph()\n", + "\n", + "edges = [(2, 4), (2, 5), (2, 27), (4, 6), (5, 6), (5, 7), (5, 8), (5, 9), (6, 7), (6, 8), (6, 9), \n", + " (7, 8), (7, 9), (8, 9), (9, 12), (9, 24), (9, 28), (10, 12), (10, 29), (20, 21), (20, 22), \n", + " (20, 23), (20, 24), (21, 22), (21, 23), (21, 24), (22, 23), (22, 24), (23, 24), (25, 26), \n", + " (25, 27), (25, 28), (25, 29), (26, 27), (26, 28), (26, 29), (27, 28), (27, 29), (28, 29)]\n", + "\n", + "for i,j in edges:\n", + " graph.push_factor(X(i), X(j))\n", + "\n", + "# Define an elimination ordering\n", + "ordering = Ordering.MetisSymbolicFactorGraph(graph) # Use METIS for this example\n", + "\n", + "# Eliminate using Multifrontal method\n", + "bayes_tree = graph.eliminateMultifrontal(ordering)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualization and Properties\n", + "\n", + "The Bayes tree structure can be visualized, and we can query its properties." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Bayes Tree size (number of cliques): 8\n", + "Is the Bayes Tree empty? False\n" + ] + } + ], + "source": [ + "print(f\"\\nBayes Tree size (number of cliques): {bayes_tree.size()}\")\n", + "print(f\"Is the Bayes Tree empty? {bayes_tree.empty()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "\n", + "\n", + "20\n", + "\n", + "x28, x25, x26, x9, x29, x27\n", + "\n", + "\n", + "\n", + "21\n", + "\n", + "x5, x4 : x9, x27\n", + "\n", + "\n", + "\n", + "20->21\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "25\n", + "\n", + "x12 : x9, x29\n", + "\n", + "\n", + "\n", + "20->25\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "27\n", + "\n", + "x24, x23, x22, x21, x20 : x9\n", + "\n", + "\n", + "\n", + "20->27\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "22\n", + "\n", + "x2 : x4, x5, x27\n", + "\n", + "\n", + "\n", + "21->22\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "23\n", + "\n", + "x6 : x4, x5, x9\n", + "\n", + "\n", + "\n", + "21->23\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "24\n", + "\n", + "x8, x7 : x5, x6, x9\n", + "\n", + "\n", + "\n", + "23->24\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "26\n", + "\n", + "x10 : x12, x29\n", + "\n", + "\n", + "\n", + "25->26\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Visualize the Bayes tree using graphviz\n", + "display(graphviz.Source(bayes_tree.dot()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Traversing a Bayes Tree" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of roots: 1\n", + "\n", + "Clique:\n", + " P( x28 x25 x26 x9 x29 x27)\n", + "\n", + "Clique:\n", + " P( x5 x4 | x9 x27)\n", + "\n", + "Clique:\n", + " P( x2 | x4 x5 x27)\n", + "\n", + "Clique:\n", + " P( x6 | x4 x5 x9)\n", + "\n", + "Clique:\n", + " P( x8 x7 | x5 x6 x9)\n", + "\n", + "Clique:\n", + " P( x12 | x9 x29)\n", + "\n", + "Clique:\n", + " P( x10 | x12 x29)\n", + "\n", + "Clique:\n", + " P( x24 x23 x22 x21 x20 | x9)\n" + ] + } + ], + "source": [ + "roots = bayes_tree.roots()\n", + "print(f\"Number of roots: {len(roots)}\")\n", + "\n", + "def traverse_clique(clique):\n", + " if clique:\n", + " clique.print(\"\\nClique:\\n\")\n", + " for j in range(clique.nrChildren()):\n", + " traverse_clique(clique[j])\n", + "\n", + "for root in roots:\n", + " traverse_clique(root)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicBayesTreeClique.ipynb b/gtsam/symbolic/doc/SymbolicBayesTreeClique.ipynb new file mode 100644 index 000000000..ba3ed1a8d --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicBayesTreeClique.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicBayesTreeClique" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicBayesTreeClique` represents a single node within a `SymbolicBayesTree`. Each clique corresponds to a set of variables (frontal variables) that were eliminated together during the multifrontal elimination process.\n", + "\n", + "Key aspects of a clique:\n", + "* **Conditional:** It stores the `SymbolicConditional` P(Frontals | Parents/Separator) that results from eliminating the frontal variables.\n", + "* **Tree Structure:** It maintains pointers to its parent and children cliques within the Bayes Tree, defining the tree's topology.\n", + "* **Frontal and Separator Variables:** Implicitly defines the frontal variables (eliminated in this clique) and separator variables (parents in the Bayes Tree, connecting it to its parent clique).\n", + "\n", + "Users typically interact with the `SymbolicBayesTree` as a whole, but understanding the clique structure is helpful for comprehending how the Bayes Tree represents the factored distribution and facilitates efficient inference." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicFactorGraph, Ordering\n", + "# SymbolicBayesTreeClique is accessed *through* a SymbolicBayesTree\n", + "from gtsam.symbol_shorthand import X, L\n", + "import graphviz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Accessing and Inspecting Cliques\n", + "\n", + "Cliques are obtained by first creating a `SymbolicBayesTree` (usually via elimination) and then accessing its nodes." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bayes Tree has 4 cliques.\n", + "Number of roots: 1\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "x1, x2\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "x0 : x1\n", + "\n", + "\n", + "\n", + "6->7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "l2 : x1\n", + "\n", + "\n", + "\n", + "6->9\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "l1 : x0\n", + "\n", + "\n", + "\n", + "7->8\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Create a factor graph\n", + "graph = SymbolicFactorGraph()\n", + "graph.push_factor(X(0))\n", + "graph.push_factor(X(0), X(1))\n", + "graph.push_factor(X(1), X(2))\n", + "graph.push_factor(X(0), L(1))\n", + "graph.push_factor(X(1), L(2))\n", + "\n", + "# Eliminate to get a Bayes Tree\n", + "ordering = Ordering.ColamdSymbolicFactorGraph(graph)\n", + "bayes_tree = graph.eliminateMultifrontal(ordering)\n", + "\n", + "print(f\"Bayes Tree has {bayes_tree.size()} cliques.\")\n", + "\n", + "roots = bayes_tree.roots()\n", + "print(f\"Number of roots: {len(roots)}\")\n", + "\n", + "# Visualize the Bayes tree using graphviz\n", + "display(graphviz.Source(bayes_tree.dot()))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Inspecting Clique 0:\n", + " Clique Structure: P( x1 x2)\n", + " Associated Conditional: P( P( x1 x2)\n", + " Is Root? True\n", + " Parent Clique is None (likely a root clique).\n", + " Number of Children: 2\n", + "\n", + "Inspecting Clique 0:\n", + " Clique Structure: P( x0 | x1)\n", + " Associated Conditional: P( P( x0 | x1)\n", + " Is Root? False\n", + " Parent Clique exists.\n", + " Number of Children: 1\n", + "\n", + "Inspecting Clique 0:\n", + " Clique Structure: P( l1 | x0)\n", + " Associated Conditional: P( P( l1 | x0)\n", + " Is Root? False\n", + " Parent Clique exists.\n", + " Number of Children: 0\n", + "\n", + "Inspecting Clique 0:\n", + " Clique Structure: P( l2 | x1)\n", + " Associated Conditional: P( P( l2 | x1)\n", + " Is Root? False\n", + " Parent Clique exists.\n", + " Number of Children: 0\n" + ] + } + ], + "source": [ + "def inspect(clique):\n", + " print(\"\\nInspecting Clique 0:\")\n", + " clique.print(\" Clique Structure: \")\n", + " \n", + " # Get the conditional stored in the clique\n", + " conditional = clique.conditional()\n", + " if conditional:\n", + " conditional.print(\" Associated Conditional: P(\")\n", + " else:\n", + " print(\" Clique has no associated conditional (might be empty root)\")\n", + " \n", + " # Check properties\n", + " print(f\" Is Root? {clique.isRoot()}\")\n", + " # Accessing parent/children is possible in C++ but might be less direct or typical in Python wrapper usage\n", + " # Parent clique (careful, might be null for root)\n", + " parent_clique = clique.parent() \n", + " if parent_clique:\n", + " print(\" Parent Clique exists.\")\n", + " else:\n", + " print(\" Parent Clique is None (likely a root clique).\")\n", + " \n", + " print(f\" Number of Children: {clique.nrChildren()}\") # Example if method existed\n", + "\n", + "def traverse_clique(clique):\n", + " inspect(clique)\n", + " for j in range(clique.nrChildren()):\n", + " traverse_clique(clique[j])\n", + "\n", + "for root in roots:\n", + " traverse_clique(root)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicConditional.ipynb b/gtsam/symbolic/doc/SymbolicConditional.ipynb new file mode 100644 index 000000000..e43881ee0 --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicConditional.ipynb @@ -0,0 +1,142 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicConditional" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicConditional` represents a conditional probability distribution P(Frontals | Parents) in purely symbolic form. It only stores the keys of the frontal variables and the parent variables, without any associated numerical probability function.\n", + "\n", + "`SymbolicConditional` objects are typically produced as the result of symbolic elimination on a `SymbolicFactorGraph`. A collection of these forms a `SymbolicBayesNet`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicConditional\n", + "from gtsam.symbol_shorthand import X, L" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating SymbolicConditionals\n", + "\n", + "Conditionals specify frontal (conditioned) variables and parent variables." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "P(X(0)): P( x0)\n", + " Nr Frontals: 1, Nr Parents: 0\n", + "\n", + "P(X(1) | X(0)): P( x1 | x0)\n", + " Nr Frontals: 1, Nr Parents: 1\n", + "\n", + "P(X(2) | X(0), X(1)): P( x2 | x0 x1)\n", + " Nr Frontals: 1, Nr Parents: 2\n", + "\n", + "P(L(1) | X(0), X(1)): P( l1 | x0 x1)\n", + " Nr Frontals: 1, Nr Parents: 2\n", + "\n", + "P(X(2), L(1) | X(0), X(1)): P( x2 l1 | x0 x1)\n", + " Nr Frontals: 2, Nr Parents: 2\n" + ] + } + ], + "source": [ + "# P(X(0))\n", + "c0 = SymbolicConditional(X(0))\n", + "c0.print(\"P(X(0)): \")\n", + "print(f\" Nr Frontals: {c0.nrFrontals()}, Nr Parents: {c0.nrParents()}\\n\")\n", + "\n", + "# P(X(1) | X(0))\n", + "c1 = SymbolicConditional(X(1), X(0))\n", + "c1.print(\"P(X(1) | X(0)): \")\n", + "print(f\" Nr Frontals: {c1.nrFrontals()}, Nr Parents: {c1.nrParents()}\\n\")\n", + "\n", + "# P(X(2) | X(0), X(1))\n", + "c2 = SymbolicConditional(X(2), X(0), X(1))\n", + "c2.print(\"P(X(2) | X(0), X(1)): \")\n", + "print(f\" Nr Frontals: {c2.nrFrontals()}, Nr Parents: {c2.nrParents()}\\n\")\n", + "\n", + "# P(L(1) | X(0), X(1))\n", + "c3 = SymbolicConditional(L(1), X(0), X(1))\n", + "c3.print(\"P(L(1) | X(0), X(1)): \")\n", + "print(f\" Nr Frontals: {c3.nrFrontals()}, Nr Parents: {c3.nrParents()}\\n\")\n", + "\n", + "# Create from keys and number of frontals, e.g. P(X(2), L(1) | X(0), X(1))\n", + "# Keys = [Frontals..., Parents...]\n", + "c4 = SymbolicConditional.FromKeys([X(2), L(1), X(0), X(1)], 2)\n", + "c4.print(\"P(X(2), L(1) | X(0), X(1)): \")\n", + "print(f\" Nr Frontals: {c4.nrFrontals()}, Nr Parents: {c4.nrParents()}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicEliminationTree.ipynb b/gtsam/symbolic/doc/SymbolicEliminationTree.ipynb new file mode 100644 index 000000000..ae068282d --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicEliminationTree.ipynb @@ -0,0 +1,138 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicEliminationTree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicEliminationTree` represents the computational structure used in variable elimination, particularly in sparse Cholesky or QR factorization. Each node in the tree corresponds to the elimination of a single variable.\n", + "\n", + "The tree structure reveals dependencies: the elimination of a variable (node) depends on the results from its children in the tree. The root of the tree corresponds to the last variable eliminated. This structure is closely related to the resulting Bayes net or Bayes tree." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicEliminationTree, SymbolicFactorGraph, Ordering\n", + "from gtsam.symbol_shorthand import X, L" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a SymbolicEliminationTree\n", + "\n", + "An elimination tree is constructed from a `SymbolicFactorGraph` and an `Ordering`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Symbolic Elimination Tree:\n", + "-(x2)\n", + "Symbolic Elimination Tree:\n", + "| -(x1)\n", + "Symbolic Elimination Tree:\n", + "| - x1 x2\n", + "Symbolic Elimination Tree:\n", + "| | -(x0)\n", + "Symbolic Elimination Tree:\n", + "| | - x0\n", + "Symbolic Elimination Tree:\n", + "| | - x0 x1\n", + "Symbolic Elimination Tree:\n", + "| | | -(l1)\n", + "Symbolic Elimination Tree:\n", + "| | | - x0 l1\n", + "Symbolic Elimination Tree:\n", + "| | -(l2)\n", + "Symbolic Elimination Tree:\n", + "| | - x1 l2\n" + ] + } + ], + "source": [ + "# Create a factor graph\n", + "graph = SymbolicFactorGraph()\n", + "graph.push_factor(X(0))\n", + "graph.push_factor(X(0), X(1))\n", + "graph.push_factor(X(1), X(2))\n", + "graph.push_factor(X(0), L(1))\n", + "graph.push_factor(X(1), L(2))\n", + "\n", + "# Define an elimination ordering\n", + "ordering = Ordering([L(1), L(2), X(0), X(1), X(2)]) # Eliminate L(1) first, then X(0), X(1), X(2) last\n", + "\n", + "# Construct the elimination tree\n", + "elimination_tree = SymbolicEliminationTree(graph, ordering)\n", + "\n", + "# Print the tree structure (text representation)\n", + "elimination_tree.print(\"Symbolic Elimination Tree:\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*(Note: Direct visualization of the elimination tree structure isn't available via a simple `.dot()` method like factor graphs or Bayes nets/trees in the Python wrapper, but the print output shows the parent-child relationships.)*" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicFactor.ipynb b/gtsam/symbolic/doc/SymbolicFactor.ipynb new file mode 100644 index 000000000..06141920b --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicFactor.ipynb @@ -0,0 +1,131 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicFactor" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicFactor` represents the connectivity (topology) between variables in a factor graph without any specific numerical function associated with it. It's primarily to *illustrate* symbolic elimination. Internally, GTSAM does analyze the structure of other factor graph types without explicitly converting to a symbolic factor graph.\n", + "\n", + "It inherits from `gtsam.Factor` and stores the keys (indices) of the variables it connects." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import gtsam\n", + "from gtsam import SymbolicFactor\n", + "from gtsam.symbol_shorthand import X\n", + "\n", + "# Example Keys\n", + "x0, x1, x2 = X(0), X(1), X(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating SymbolicFactors\n", + "\n", + "SymbolicFactors can be created by specifying the keys they involve." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unary Factor f0: \n", + " x0\n", + "Binary Factor f1: \n", + " x0 x1\n", + "Ternary Factor f2: \n", + " x1 x2 x0\n", + "Factor f3 from KeyVector: \n", + " x0 x1 x2\n" + ] + } + ], + "source": [ + "# Unary factor\n", + "f0 = SymbolicFactor(x0)\n", + "f0.print(\"Unary Factor f0: \\n\")\n", + "\n", + "# Binary factor\n", + "f1 = SymbolicFactor(x0, x1)\n", + "f1.print(\"Binary Factor f1: \\n\")\n", + "\n", + "# Ternary factor\n", + "f2 = SymbolicFactor(x1, x2, x0)\n", + "f2.print(\"Ternary Factor f2: \\n\")\n", + "\n", + "# From a list of keys\n", + "keys = gtsam.KeyVector([x0, x1, x2])\n", + "f3 = SymbolicFactor.FromKeys(keys)\n", + "f3.print(\"Factor f3 from KeyVector: \\n\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicFactorGraph.ipynb b/gtsam/symbolic/doc/SymbolicFactorGraph.ipynb new file mode 100644 index 000000000..10c90e321 --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicFactorGraph.ipynb @@ -0,0 +1,479 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicFactorGraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicFactorGraph` is a factor graph consisting purely of `SymbolicFactor` objects. It represents the structure or topology of a probabilistic graphical model (like a Markov Random Field) without the numerical details.\n", + "\n", + "It's primarily used to *illustrate* symbolic elimination, which determines the structure of the resulting Bayes net or Bayes tree and finds an efficient variable elimination ordering (e.g., using COLAMD or METIS)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicFactorGraph, Ordering\n", + "from gtsam.symbol_shorthand import X, L\n", + "import graphviz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating and Manipulating Symbolic Factor Graphs" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Symbolic Factor Graph:\n", + " \n", + "size: 5\n", + "factor 0: x0\n", + "factor 1: x0 x1\n", + "factor 2: x1 x2\n", + "factor 3: x0 l1\n", + "factor 4: x1 l2\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var7782220156096217089\n", + "\n", + "l1\n", + "\n", + "\n", + "\n", + "factor3\n", + "\n", + "\n", + "\n", + "\n", + "var7782220156096217089--factor3\n", + "\n", + "\n", + "\n", + "\n", + "var7782220156096217090\n", + "\n", + "l2\n", + "\n", + "\n", + "\n", + "factor4\n", + "\n", + "\n", + "\n", + "\n", + "var7782220156096217090--factor4\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352320\n", + "\n", + "x0\n", + "\n", + "\n", + "\n", + "factor0\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352320--factor0\n", + "\n", + "\n", + "\n", + "\n", + "factor1\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352320--factor1\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352320--factor3\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321\n", + "\n", + "x1\n", + "\n", + "\n", + "\n", + "var8646911284551352321--factor1\n", + "\n", + "\n", + "\n", + "\n", + "factor2\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321--factor2\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321--factor4\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352322\n", + "\n", + "x2\n", + "\n", + "\n", + "\n", + "var8646911284551352322--factor2\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create an empty graph\n", + "graph = SymbolicFactorGraph()\n", + "\n", + "# Add factors (using convenience methods)\n", + "graph.push_factor(X(0)) # Unary\n", + "graph.push_factor(X(0), X(1))\n", + "graph.push_factor(X(1), X(2))\n", + "graph.push_factor(X(0), L(1))\n", + "graph.push_factor(X(1), L(2))\n", + "\n", + "# Print the graph structure\n", + "graph.print(\"Symbolic Factor Graph:\\n\")\n", + "\n", + "# Visualize the graph using Graphviz\n", + "graphviz.Source(graph.dot())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Symbolic Elimination\n", + "\n", + "We can perform symbolic elimination to get the structure of the resulting Bayes net or Bayes tree." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# Define an elimination ordering (can also be computed automatically)\n", + "ordering = Ordering([L(1), L(2), X(0), X(1), X(2)])" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Resulting Symbolic Bayes Net:\n", + " \n", + "size: 5\n", + "conditional 0: P( l1 | x0)\n", + "conditional 1: P( x0 | x1)\n", + "conditional 2: P( l2 | x1)\n", + "conditional 3: P( x1 | x2)\n", + "conditional 4: P( x2)\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var7782220156096217089\n", + "\n", + "l1\n", + "\n", + "\n", + "\n", + "var7782220156096217090\n", + "\n", + "l2\n", + "\n", + "\n", + "\n", + "var8646911284551352320\n", + "\n", + "x0\n", + "\n", + "\n", + "\n", + "var8646911284551352320->var7782220156096217089\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321\n", + "\n", + "x1\n", + "\n", + "\n", + "\n", + "var8646911284551352321->var7782220156096217090\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352321->var8646911284551352320\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "var8646911284551352322\n", + "\n", + "x2\n", + "\n", + "\n", + "\n", + "var8646911284551352322->var8646911284551352321\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Eliminate sequentially to get a Bayes Net structure\n", + "bayes_net = graph.eliminateSequential(ordering)\n", + "bayes_net.print(\"\\nResulting Symbolic Bayes Net:\\n\")\n", + "\n", + "# Visualize the Bayes Net using Graphviz\n", + "graphviz.Source(bayes_net.dot())" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Resulting Symbolic Bayes Tree:\n", + ": cliques: 4, variables: 5\n", + "\n", + "Resulting Symbolic Bayes Tree:\n", + "- P( x1 x2)\n", + "\n", + "Resulting Symbolic Bayes Tree:\n", + "| - P( x0 | x1)\n", + "\n", + "Resulting Symbolic Bayes Tree:\n", + "| | - P( l1 | x0)\n", + "\n", + "Resulting Symbolic Bayes Tree:\n", + "| - P( l2 | x1)\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "x1, x2\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "x0 : x1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "l2 : x1\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "l1 : x0\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Eliminate using Multifrontal method to get a Bayes Tree structure\n", + "bayes_tree = graph.eliminateMultifrontal(ordering)\n", + "bayes_tree.print(\"\\nResulting Symbolic Bayes Tree:\\n\")\n", + "\n", + "# Visualize the Bayes Tree using Graphviz\n", + "graphviz.Source(bayes_tree.dot())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/doc/SymbolicJunctionTree.ipynb b/gtsam/symbolic/doc/SymbolicJunctionTree.ipynb new file mode 100644 index 000000000..3f600b42b --- /dev/null +++ b/gtsam/symbolic/doc/SymbolicJunctionTree.ipynb @@ -0,0 +1,133 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# SymbolicJunctionTree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `SymbolicJunctionTree` (often used interchangeably with Clique Tree in GTSAM documentation) is a data structure used in multifrontal variable elimination. It's derived from an `EliminationTree`.\n", + "\n", + "Key differences from an Elimination Tree:\n", + "* **Nodes are Cliques:** Each node in a Junction Tree represents a *clique* (a group of variables eliminated together), not just a single variable.\n", + "* **Stores Factors:** Nodes (`SymbolicCluster` objects) store the symbolic factors associated with the variables in that clique.\n", + "\n", + "The Junction Tree organizes the factors and variables for efficient multifrontal elimination, which processes variables in these larger cliques simultaneously. The result of eliminating a Junction Tree is a `SymbolicBayesTree`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [], + "source": [ + "%pip install --quiet gtsam-develop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from gtsam import SymbolicJunctionTree, SymbolicEliminationTree, SymbolicFactorGraph, Ordering\n", + "from gtsam.symbol_shorthand import X, L" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a SymbolicJunctionTree\n", + "\n", + "A Junction Tree is constructed from a `SymbolicEliminationTree`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Symbolic Junction Tree:\n", + "- (6) x1 x2 \n", + "Symbolic Junction Tree:\n", + "| - (6) x0 \n", + "Symbolic Junction Tree:\n", + "| | - (2) l1 \n", + "Symbolic Junction Tree:\n", + "| - (2) l2 \n" + ] + } + ], + "source": [ + "# Create a factor graph\n", + "graph = SymbolicFactorGraph()\n", + "graph.push_factor(X(0))\n", + "graph.push_factor(X(0), X(1))\n", + "graph.push_factor(X(1), X(2))\n", + "graph.push_factor(X(0), L(1))\n", + "graph.push_factor(X(1), L(2))\n", + "\n", + "# Define an elimination ordering\n", + "ordering = Ordering([L(1), L(2), X(0), X(1), X(2)]) \n", + "\n", + "# Construct the elimination tree first\n", + "elimination_tree = SymbolicEliminationTree(graph, ordering)\n", + "\n", + "# Construct the junction tree from the elimination tree\n", + "junction_tree = SymbolicJunctionTree(elimination_tree)\n", + "\n", + "# Print the tree structure (text representation)\n", + "junction_tree.print(\"Symbolic Junction Tree:\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*(Note: The `SymbolicCluster` class represents the nodes within the Junction Tree, containing the factors and frontal/separator keys for that clique. Direct visualization is usually done via the resulting Bayes Tree.)*" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py312", + "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.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/gtsam/symbolic/symbolic.md b/gtsam/symbolic/symbolic.md new file mode 100644 index 000000000..bfae60842 --- /dev/null +++ b/gtsam/symbolic/symbolic.md @@ -0,0 +1,40 @@ +# Symbolic Module + +The `symbolic` module in GTSAM deals with the *structure* of factor graphs and Bayesian networks, independent of the specific numerical types of factors (like Gaussian or discrete). It allows for analyzing graph connectivity, determining optimal variable elimination orders, and understanding the sparsity structure of the resulting inference objects. + +This is crucial for efficient inference, as the symbolic elimination steps determine the computational complexity and memory requirements of the numerical factorization. + +The classes here are used primarily to *illustrate* symbolic elimination. Internally, GTSAM does analyze the structure of other factor graph types without explicitly converting to a symbolic factor graph. + + +## Classes + +Here's an overview of the key classes in the `symbolic` module: + +### Factor Graph + +- **[SymbolicFactor](doc/SymbolicFactor.ipynb)**: Represents the connectivity between a set of variables (keys) in a factor graph, without any specific numerical function associated with it. It defines the hyperedges of the graph. +- **[SymbolicFactorGraph](doc/SymbolicFactorGraph.ipynb)**: A collection of `SymbolicFactor` objects, representing the overall structure of a factor graph. + +### Elimination Products + +These classes represent the results of symbolic variable elimination: + +- **[SymbolicConditional](doc/SymbolicConditional.ipynb)**: Represents the structure of a conditional probability distribution P(Frontals | Parents). It stores the keys of the frontal (conditioned) and parent variables resulting from eliminating one or more variables from a set of factors. +- **[SymbolicBayesNet](doc/SymbolicBayesNet.ipynb)**: A directed acyclic graph composed of `SymbolicConditional` objects, representing the structure of a factorized distribution P(X) = Π P(Xi | Parents(Xi)). Typically results from sequential elimination. +- **[SymbolicBayesTree](doc/SymbolicBayesTree.ipynb)**: A tree structure where each node (`SymbolicBayesTreeClique`) represents a `SymbolicConditional` P(Frontals | Separator). This is the result of multifrontal elimination and is the underlying structure for efficient incremental updates (iSAM) and exact marginal computation. +- **[SymbolicBayesTreeClique](doc/SymbolicBayesTreeClique.ipynb)**: Represents a single clique (node) within a `SymbolicBayesTree`, containing a `SymbolicConditional`. + +### Elimination Structures + +These classes represent intermediate structures used during the elimination process: + +- **[SymbolicEliminationTree](doc/SymbolicEliminationTree.ipynb)**: Represents the dependency structure of sequential variable elimination. Each node corresponds to eliminating a single variable. +- **[SymbolicJunctionTree](doc/SymbolicJunctionTree.ipynb)** (Clique Tree): An intermediate structure used in multifrontal elimination, derived from an elimination tree. Each node (`SymbolicCluster`) represents a clique of variables eliminated together and stores the *factors* associated with that clique. + +**Importance** +Performing symbolic analysis can be used for: +1. Choosing an Optimal Ordering: Finding an ordering that minimizes fill-in (new connections created during elimination) is key to efficient numerical factorization. +2. Predicting Computational Cost: The structure of the Bayes net or Bayes tree determines the complexity of subsequent numerical operations like solving or marginalization. +3. Memory Allocation: Knowing the structure allows for pre-allocation of memory for the numerical factorization. +The symbolic module provides the foundation for GTSAM's efficient inference algorithms.