Fixing code & adding formulae
parent
bf885764c7
commit
aae8b5d140
|
@ -1,288 +1,397 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# BayesNet"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `BayesNet` in GTSAM represents a directed graphical model, specifically the result of running sequential variable elimination (like Cholesky or QR factorization) on a `FactorGraph`.\n",
|
||||
"\n",
|
||||
"It is essentially a collection of `Conditional` objects, ordered according to the elimination order. Each conditional represents $P(\text{variable} | \text{parents})$, where the parents are variables that appear later in the elimination ordering.\n",
|
||||
"\n",
|
||||
"Like `FactorGraph`, `BayesNet` is templated on the type of conditional it stores (e.g., `GaussianBayesNet`, `DiscreteBayesNet`)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/BayesNet.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We need concrete graph types and elimination to get a BayesNet\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianBayesNet\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a BayesNet (via Elimination)\n",
|
||||
"\n",
|
||||
"BayesNets are typically obtained by eliminating a `FactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_eliminate_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "01234567-89ab-cdef-0123-456789abcdef"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Original Factor Graph: size 3\n",
|
||||
"Factor 0: JacobianFactor(keys = [8070450532247928832], Z = [ -1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 1: JacobianFactor(keys = [8070450532247928832; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 2: JacobianFactor(keys = [8070450532247928833; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Resulting BayesNet: size 3\n",
|
||||
"Conditional 0: GaussianConditional( P(x0 | x1) = dx0 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.707107 ])\n",
|
||||
"Conditional 1: GaussianConditional( P(x1 | x2) = dx1 - R*dx2 - d), R = [ 0.666667 ], d = [ 0 ], sigmas = [ 0.816497 ])\n",
|
||||
"Conditional 2: GaussianConditional( P(x2) = dx2 - d), d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple Gaussian Factor Graph P(x0) P(x1|x0) P(x2|x1)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"print(\"Original Factor Graph:\")\n",
|
||||
"graph.print()\n",
|
||||
"\n",
|
||||
"# Eliminate sequentially using a specific ordering\n",
|
||||
"ordering = Ordering([X(0), X(1), X(2)])\n",
|
||||
"bayes_net = graph.eliminateSequential(ordering)\n",
|
||||
"\n",
|
||||
"print(\"\\nResulting BayesNet:\")\n",
|
||||
"bayes_net.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Access\n",
|
||||
"\n",
|
||||
"A `BayesNet` provides access to its constituent conditionals and basic properties."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "12345678-9abc-def0-1234-56789abcdef0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"BayesNet size: 3\n",
|
||||
"Is BayesNet empty? False\n",
|
||||
"Conditional at index 1: \n",
|
||||
"GaussianConditional( P(x1 | x2) = dx1 - R*dx2 - d), R = [ 0.666667 ], d = [ 0 ], sigmas = [ 0.816497 ])\n",
|
||||
"\n",
|
||||
"Keys in BayesNet: {8070450532247928832, 8070450532247928833, 8070450532247928834}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"BayesNet size: {bayes_net.size()}\")\n",
|
||||
"print(f\"Is BayesNet empty? {bayes_net.empty()}\")\n",
|
||||
"\n",
|
||||
"# Access conditional by index\n",
|
||||
"conditional1 = bayes_net.at(1)\n",
|
||||
"print(\"Conditional at index 1: \")\n",
|
||||
"conditional1.print()\n",
|
||||
"\n",
|
||||
"# Get all keys involved\n",
|
||||
"bn_keys = bayes_net.keys()\n",
|
||||
"print(f\"Keys in BayesNet: {bn_keys}\")\n",
|
||||
"\n",
|
||||
"# Iterate through conditionals\n",
|
||||
"# for i, conditional in enumerate(bayes_net):\n",
|
||||
"# if conditional:\n",
|
||||
"# print(f\"Conditional {i} Frontals: {conditional.frontals()}, Parents: {conditional.parents()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_eval_md"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluation and Solution\n",
|
||||
"\n",
|
||||
"The `logProbability(Values)` method computes the log probability of a variable assignment given the conditional distributions in the Bayes net. For Gaussian Bayes nets, the `optimize()` method can be used to find the maximum likelihood estimate (MLE) solution via back-substitution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_eval_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "23456789-abcd-ef01-2345-6789abcdef01"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Log Probability at [0,0,0]: -2.756815765004782\n",
|
||||
"Optimized Solution (MLE):\n",
|
||||
"Values with 3 values:\n",
|
||||
"Value x0: [0.]\n",
|
||||
"Value x1: [0.]\n",
|
||||
"Value x2: [0.]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# For GaussianBayesNet, we use VectorValues\n",
|
||||
"mle_solution = bayes_net.optimize()\n",
|
||||
"\n",
|
||||
"# Calculate log probability (requires providing values for all variables)\n",
|
||||
"log_prob = bayes_net.logProbability(mle_solution)\n",
|
||||
"print(f\"Log Probability at {mle_solution.at(X(0))[0]:.0f},{mle_solution.at(X(1))[0]:.0f},{mle_solution.at(X(2))[0]:.0f}]: {log_prob}\")\n",
|
||||
"\n",
|
||||
"print(\"Optimized Solution (MLE):\")\n",
|
||||
"mle_solution.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_viz_md"
|
||||
},
|
||||
"source": [
|
||||
"## Visualization\n",
|
||||
"\n",
|
||||
"Bayes nets can also be visualized using Graphviz."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_dot_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "3456789a-bcde-f012-3456-789abcdef012"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"digraph {\n",
|
||||
" size=\"5,5\";\n",
|
||||
"\n",
|
||||
" var8070450532247928832[label=\"x0\"];\n",
|
||||
" var8070450532247928833[label=\"x1\"];\n",
|
||||
" var8070450532247928834[label=\"x2\"];\n",
|
||||
"\n",
|
||||
" var8070450532247928834->var8070450532247928833\n",
|
||||
" var8070450532247928833->var8070450532247928832\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dot_string = bayes_net.dot()\n",
|
||||
"print(dot_string)\n",
|
||||
"\n",
|
||||
"# To render:\n",
|
||||
"# dot -Tpng bayesnet.dot -o bayesnet.png\n",
|
||||
"# import graphviz\n",
|
||||
"# graphviz.Source(dot_string)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# BayesNet"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `BayesNet` in GTSAM represents a directed graphical model, specifically the result of running sequential variable elimination (like Cholesky or QR factorization) on a `FactorGraph`.\n",
|
||||
"\n",
|
||||
"It is essentially a collection of `Conditional` objects, ordered according to the elimination order. Each conditional represents $P(\text{variable} | \text{parents})$, where the parents are variables that appear later in the elimination ordering.\n",
|
||||
"\n",
|
||||
"A Bayes net represents the joint probability distribution as a product of conditional probabilities stored in the net:\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"P(X_1, X_2, \\dots, X_N) = \\prod_{i=1}^N P(X_i | \\text{Parents}(X_i))\n",
|
||||
"$$\n",
|
||||
"The total log-probability of an assignment is the sum of the log-probabilities of its conditionals:\n",
|
||||
"$$\n",
|
||||
"\\log P(X_1, \\dots, X_N) = \\sum_{i=1}^N \\log P(X_i | \\text{Parents}(X_i))\n",
|
||||
"$$\n",
|
||||
"\n",
|
||||
"Like `FactorGraph`, `BayesNet` is templated on the type of conditional it stores (e.g., `GaussianBayesNet`, `DiscreteBayesNet`)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/BayesNet.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayesnet_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "bayesnet_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We need concrete graph types and elimination to get a BayesNet\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianBayesNet\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a BayesNet (via Elimination)\n",
|
||||
"\n",
|
||||
"BayesNets are typically obtained by eliminating a `FactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayesnet_eliminate_code",
|
||||
"outputId": "01234567-89ab-cdef-0123-456789abcdef"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Original Factor Graph:\n",
|
||||
"\n",
|
||||
"size: 3\n",
|
||||
"factor 0: \n",
|
||||
" A[x0] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 1: \n",
|
||||
" A[x0] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x1] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 2: \n",
|
||||
" A[x1] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x2] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"\n",
|
||||
"Resulting BayesNet:\n",
|
||||
"\n",
|
||||
"size: 3\n",
|
||||
"conditional 0: p(x0 | x1)\n",
|
||||
" R = [ 1.41421 ]\n",
|
||||
" S[x1] = [ -0.707107 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" logNormalizationConstant: -0.572365\n",
|
||||
" No noise model\n",
|
||||
"conditional 1: p(x1 | x2)\n",
|
||||
" R = [ 1.22474 ]\n",
|
||||
" S[x2] = [ -0.816497 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" logNormalizationConstant: -0.716206\n",
|
||||
" No noise model\n",
|
||||
"conditional 2: p(x2)\n",
|
||||
" R = [ 0.57735 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" mean: 1 elements\n",
|
||||
" x2: 0\n",
|
||||
" logNormalizationConstant: -1.46824\n",
|
||||
" No noise model\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple Gaussian Factor Graph P(x0) P(x1|x0) P(x2|x1)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"print(\"Original Factor Graph:\")\n",
|
||||
"graph.print()\n",
|
||||
"\n",
|
||||
"# Eliminate sequentially using a specific ordering\n",
|
||||
"ordering = Ordering([X(0), X(1), X(2)])\n",
|
||||
"bayes_net = graph.eliminateSequential(ordering)\n",
|
||||
"\n",
|
||||
"print(\"\\nResulting BayesNet:\")\n",
|
||||
"bayes_net.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Access\n",
|
||||
"\n",
|
||||
"A `BayesNet` provides access to its constituent conditionals and basic properties."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayesnet_access_code",
|
||||
"outputId": "12345678-9abc-def0-1234-56789abcdef0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"BayesNet size: 3\n",
|
||||
"Conditional at index 1: \n",
|
||||
"GaussianConditional p(x1 | x2)\n",
|
||||
" R = [ 1.22474 ]\n",
|
||||
" S[x2] = [ -0.816497 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" logNormalizationConstant: -0.716206\n",
|
||||
" No noise model\n",
|
||||
"Keys in BayesNet: x0x1x2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"BayesNet size: {bayes_net.size()}\")\n",
|
||||
"\n",
|
||||
"# Access conditional by index\n",
|
||||
"conditional1 = bayes_net.at(1)\n",
|
||||
"print(\"Conditional at index 1: \")\n",
|
||||
"conditional1.print()\n",
|
||||
"\n",
|
||||
"# Get all keys involved\n",
|
||||
"bn_keys = bayes_net.keys()\n",
|
||||
"print(f\"Keys in BayesNet: {bn_keys}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_eval_md"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluation and Solution\n",
|
||||
"\n",
|
||||
"The `logProbability(Values)` method computes the log probability of a variable assignment given the conditional distributions in the Bayes net. For Gaussian Bayes nets, the `optimize()` method can be used to find the maximum likelihood estimate (MLE) solution via back-substitution."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayesnet_eval_code",
|
||||
"outputId": "23456789-abcd-ef01-2345-6789abcdef01"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Log Probability at 0,0,0]: -2.7568155996140185\n",
|
||||
"Optimized Solution (MLE):\n",
|
||||
"VectorValues: 3 elements\n",
|
||||
" x0: 0\n",
|
||||
" x1: 0\n",
|
||||
" x2: 0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# For GaussianBayesNet, we use VectorValues\n",
|
||||
"mle_solution = bayes_net.optimize()\n",
|
||||
"\n",
|
||||
"# Calculate log probability (requires providing values for all variables)\n",
|
||||
"log_prob = bayes_net.logProbability(mle_solution)\n",
|
||||
"print(f\"Log Probability at {mle_solution.at(X(0))[0]:.0f},{mle_solution.at(X(1))[0]:.0f},{mle_solution.at(X(2))[0]:.0f}]: {log_prob}\")\n",
|
||||
"\n",
|
||||
"print(\"Optimized Solution (MLE):\")\n",
|
||||
"mle_solution.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayesnet_viz_md"
|
||||
},
|
||||
"source": [
|
||||
"## Visualization\n",
|
||||
"\n",
|
||||
"Bayes nets can also be visualized using Graphviz."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayesnet_dot_code",
|
||||
"outputId": "3456789a-bcde-f012-3456-789abcdef012"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"digraph {\n",
|
||||
" size=\"5,5\";\n",
|
||||
"\n",
|
||||
" var8646911284551352320[label=\"x0\"];\n",
|
||||
" var8646911284551352321[label=\"x1\"];\n",
|
||||
" var8646911284551352322[label=\"x2\"];\n",
|
||||
"\n",
|
||||
" var8646911284551352322->var8646911284551352321\n",
|
||||
" var8646911284551352321->var8646911284551352320\n",
|
||||
"}\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/svg+xml": [
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
|
||||
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
|
||||
"<!-- Generated by graphviz version 2.50.0 (0)\n",
|
||||
" -->\n",
|
||||
"<!-- Pages: 1 -->\n",
|
||||
"<svg width=\"62pt\" height=\"188pt\"\n",
|
||||
" viewBox=\"0.00 0.00 62.00 188.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
|
||||
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 184)\">\n",
|
||||
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-184 58,-184 58,4 -4,4\"/>\n",
|
||||
"<!-- var8646911284551352320 -->\n",
|
||||
"<g id=\"node1\" class=\"node\">\n",
|
||||
"<title>var8646911284551352320</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"27\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x0</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321 -->\n",
|
||||
"<g id=\"node2\" class=\"node\">\n",
|
||||
"<title>var8646911284551352321</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x1</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321->var8646911284551352320 -->\n",
|
||||
"<g id=\"edge2\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352321->var8646911284551352320</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M27,-71.7C27,-63.98 27,-54.71 27,-46.11\"/>\n",
|
||||
"<polygon fill=\"black\" stroke=\"black\" points=\"30.5,-46.1 27,-36.1 23.5,-46.1 30.5,-46.1\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352322 -->\n",
|
||||
"<g id=\"node3\" class=\"node\">\n",
|
||||
"<title>var8646911284551352322</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"27\" y=\"-158.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x2</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352322->var8646911284551352321 -->\n",
|
||||
"<g id=\"edge1\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352322->var8646911284551352321</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M27,-143.7C27,-135.98 27,-126.71 27,-118.11\"/>\n",
|
||||
"<polygon fill=\"black\" stroke=\"black\" points=\"30.5,-118.1 27,-108.1 23.5,-118.1 30.5,-118.1\"/>\n",
|
||||
"</g>\n",
|
||||
"</g>\n",
|
||||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<graphviz.sources.Source at 0x2c3022fcc20>"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dot_string = bayes_net.dot()\n",
|
||||
"print(dot_string)\n",
|
||||
"\n",
|
||||
"# To render:\n",
|
||||
"# dot -Tpng bayesnet.dot -o bayesnet.png\n",
|
||||
"import graphviz\n",
|
||||
"graphviz.Source(dot_string)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,340 +1,477 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# BayesTree"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `BayesTree` is a graphical model that represents the result of multifrontal variable elimination on a `FactorGraph`. It is a tree structure where each node is a 'clique' containing a set of conditional distributions $P(\text{Frontals} | \text{Separator})$.\n",
|
||||
"\n",
|
||||
"Key properties:\n",
|
||||
"* **Cliques:** Each node (clique) groups variables that are eliminated together.\n",
|
||||
"* **Frontal Variables:** Variables eliminated within a specific clique.\n",
|
||||
"* **Separator Variables:** Variables shared between a clique and its parent in the tree. These variables were eliminated higher up in the tree.\n",
|
||||
"* **Tree Structure:** Represents the dependencies introduced during elimination more compactly than a Bayes net, especially for sparse problems.\n",
|
||||
"\n",
|
||||
"Like `FactorGraph` and `BayesNet`, `BayesTree` is templated on the type of conditional/clique (e.g., `GaussianBayesTree`)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/BayesTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayestree_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayestree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We need concrete graph types and elimination to get a BayesTree\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianBayesTree\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a BayesTree (via Elimination)\n",
|
||||
"\n",
|
||||
"BayesTrees are typically obtained by performing multifrontal elimination on a `FactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayestree_eliminate_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "456789ab-cdef-0123-4567-89abcdef0123"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Original Factor Graph: size 7\n",
|
||||
"Factor 0: JacobianFactor(keys = [8070450532247928832], Z = [ -1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 1: JacobianFactor(keys = [8070450532247928832; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 2: JacobianFactor(keys = [8070450532247928833; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 3: JacobianFactor(keys = [7783684379976990720; 8070450532247928832], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 4: JacobianFactor(keys = [7783684379976990720; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 5: JacobianFactor(keys = [7783684379976990721; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 6: JacobianFactor(keys = [7783684379976990721; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Resulting BayesTree: cliques: 3, variables: 5\n",
|
||||
"Root(s):\n",
|
||||
"Conditional density P(l1, x1 | l2, x2) = P(l1 | x1) P(x1 | l2, x2) \n",
|
||||
" size: 2\n",
|
||||
" Conditional P(l1 | x1): GaussianConditional( P(l1 | x1) = dl1 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n",
|
||||
" Conditional P(x1 | l2, x2): GaussianConditional( P(x1 | l2, x2) = dx1 - R1*dl2 - R2*dx2 - d), R1 = [ 0.333333 ], R2 = [ 0.333333 ], d = [ 0 ], sigmas = [ 0.745356 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(l2, x2 | x0) = P(l2 | x2) P(x2 | x0) \n",
|
||||
" size: 2\n",
|
||||
" Conditional P(l2 | x2): GaussianConditional( P(l2 | x2) = dl2 - R*dx2 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n",
|
||||
" Conditional P(x2 | x0): GaussianConditional( P(x2 | x0) = dx2 - R*dx0 - d), R = [ 0.2 ], d = [ 0 ], sigmas = [ 0.774597 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(x0) = P(x0) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x0): GaussianConditional( P(x0) = dx0 - d), d = [ 0 ], sigmas = [ 0.894427 ])\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple Gaussian Factor Graph (more complex this time)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model) # Prior on x0\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # x0 -> x1\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model) # x1 -> x2\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model) # l1 -> x0 (measurement)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # l1 -> x1 (measurement)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # l2 -> x1 (measurement)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model) # l2 -> x2 (measurement)\n",
|
||||
"\n",
|
||||
"print(\"Original Factor Graph:\")\n",
|
||||
"graph.print()\n",
|
||||
"\n",
|
||||
"# Eliminate multifrontally using COLAMD ordering\n",
|
||||
"ordering = Ordering.Colamd(graph)\n",
|
||||
"# Note: Multifrontal typically yields multiple roots if graph is disconnected\n",
|
||||
"bayes_tree = graph.eliminateMultifrontal(ordering)\n",
|
||||
"\n",
|
||||
"print(\"\\nResulting BayesTree:\")\n",
|
||||
"bayes_tree.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Access\n",
|
||||
"\n",
|
||||
"A `BayesTree` allows access to its root cliques and provides a way to look up the clique containing a specific variable."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayestree_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "56789abc-def0-1234-5678-9abcdef01234"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"BayesTree number of cliques: 3\n",
|
||||
"Number of roots: 1\n",
|
||||
"Root clique 0 conditional frontals: [7783684379976990720, 8070450532247928833]\n",
|
||||
"\n",
|
||||
"Clique containing x1 (8070450532247928833):\n",
|
||||
"Conditional density P(l1, x1 | l2, x2) = P(l1 | x1) P(x1 | l2, x2) \n",
|
||||
" size: 2\n",
|
||||
" Conditional P(l1 | x1): GaussianConditional( P(l1 | x1) = dl1 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n",
|
||||
" Conditional P(x1 | l2, x2): GaussianConditional( P(x1 | l2, x2) = dx1 - R1*dl2 - R2*dx2 - d), R1 = [ 0.333333 ], R2 = [ 0.333333 ], d = [ 0 ], sigmas = [ 0.745356 ])\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"BayesTree number of cliques: {bayes_tree.size()}\")\n",
|
||||
"\n",
|
||||
"# Access roots\n",
|
||||
"roots = bayes_tree.roots()\n",
|
||||
"print(f\"Number of roots: {len(roots)}\")\n",
|
||||
"if roots:\n",
|
||||
" # Access the conditional associated with the first root clique\n",
|
||||
" root_conditional = roots[0].conditional()\n",
|
||||
" print(f\"Root clique 0 conditional frontals: {root_conditional.frontals()}\")\n",
|
||||
"\n",
|
||||
"# Find the clique containing a specific variable\n",
|
||||
"clique_x1 = bayes_tree.clique(X(1))\n",
|
||||
"# clique_x1 is a shared pointer to the clique (e.g., GaussianBayesTreeClique)\n",
|
||||
"print(f\"\\nClique containing x1 ({X(1)}):\")\n",
|
||||
"clique_x1.print() # Print the clique itself"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_solve_md"
|
||||
},
|
||||
"source": [
|
||||
"## Solution and Marginals\n",
|
||||
"\n",
|
||||
"Similar to `BayesNet`, `BayesTree` (specifically derived types like `GaussianBayesTree`) provides an `optimize()` method for finding the MLE solution. It also allows for efficient computation of marginals on individual variables or joint marginals on pairs of variables using belief propagation or shortcut evaluation on the tree."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayestree_solve_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "6789abcd-ef01-2345-6789-abcdef012345"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Optimized Solution (MLE):\n",
|
||||
"Values with 5 values:\n",
|
||||
"Value l1: [0.]\n",
|
||||
"Value l2: [0.]\n",
|
||||
"Value x0: [0.]\n",
|
||||
"Value x1: [0.]\n",
|
||||
"Value x2: [0.]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Marginal Factor on x1:\n",
|
||||
"Conditional density P(x1 | l2, x2) = P(x1 | l2, x2) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x1 | l2, x2): GaussianConditional( P(x1 | l2, x2) = dx1 - R1*dl2 - R2*dx2 - d), R1 = [ 0.333333 ], R2 = [ 0.333333 ], d = [ 0 ], sigmas = [ 0.745356 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Joint Marginal Factor Graph on (x0, x2):\n",
|
||||
"Factor Graph: size 2\n",
|
||||
"Factor 0: GaussianConditional( P(x0 | x2) = dx0 - R*dx2 - d), R = [ 0.25 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Factor 1: GaussianConditional( P(x2) = dx2 - d), d = [ 0 ], sigmas = [ 0.774597 ])\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Optimize to find the MLE solution (for GaussianBayesTree)\n",
|
||||
"mle_solution = bayes_tree.optimize()\n",
|
||||
"print(\"Optimized Solution (MLE):\")\n",
|
||||
"mle_solution.print()\n",
|
||||
"\n",
|
||||
"# Compute marginal factor on a single variable (returns a Conditional)\n",
|
||||
"marginal_x1 = bayes_tree.marginalFactor(X(1))\n",
|
||||
"print(\"\\nMarginal Factor on x1:\")\n",
|
||||
"marginal_x1.print()\n",
|
||||
"\n",
|
||||
"# Compute joint marginal factor graph on two variables\n",
|
||||
"joint_x0_x2 = bayes_tree.joint(X(0), X(2))\n",
|
||||
"print(\"\\nJoint Marginal Factor Graph on (x0, x2):\")\n",
|
||||
"joint_x0_x2.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_viz_md"
|
||||
},
|
||||
"source": [
|
||||
"## Visualization\n",
|
||||
"\n",
|
||||
"Bayes trees can be visualized using Graphviz."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bayestree_dot_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "789abcde-f012-3456-789a-bcdef0123456"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"digraph G{\n",
|
||||
"0[label=\"l1, x1 : l2, x2\"];\n",
|
||||
"0->1\n",
|
||||
"1[label=\"l2, x2 : x0\"];\n",
|
||||
"1->2\n",
|
||||
"2[label=\"x0 : \"];\n",
|
||||
"}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dot_string = bayes_tree.dot()\n",
|
||||
"print(dot_string)\n",
|
||||
"\n",
|
||||
"# To render:\n",
|
||||
"# dot -Tpng bayestree.dot -o bayestree.png\n",
|
||||
"# import graphviz\n",
|
||||
"# graphviz.Source(dot_string)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# BayesTree"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `BayesTree` is a graphical model that represents the result of multifrontal variable elimination on a `FactorGraph`. It is a tree structure where each node is a 'clique' containing a set of conditional distributions $P(\text{Frontals} | \text{Separator})$.\n",
|
||||
"\n",
|
||||
"Each clique k contains a conditional $P(F_k∣S_k)$, where $F_k$ are frontal variables and $S_k$ are separator variables. The joint probability distribution encoded by the Bayes tree is given by the product of all clique conditionals:\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"P(X) = \\prod_k P(F_k | S_k)\n",
|
||||
"$$\n",
|
||||
"Alternatively, it can be expressed using clique $P(C_k) = P(F_k, S_k)$ and separator $P(S_k)$ marginals (though GTSAM stores conditionals):\n",
|
||||
"$$\n",
|
||||
"P(X) = \\frac{\\prod_{\\text{cliques } k} P(C_k)}{\\prod_{\\text{separators } S} P(S)^{\\nu(S)-1}}\n",
|
||||
"$$\n",
|
||||
"where $\\nu(S)$ is the number of cliques containing the separator $S$. The first formula $P(X) = \\prod_k P(F_k | S_k)$ is more directly related to the GTSAM `BayesTree` structure.\n",
|
||||
"\n",
|
||||
"Key properties:\n",
|
||||
"* **Cliques:** Each node (clique) groups variables that are eliminated together.\n",
|
||||
"* **Frontal Variables:** Variables eliminated within a specific clique.\n",
|
||||
"* **Separator Variables:** Variables shared between a clique and its parent in the tree. These variables were eliminated higher up in the tree.\n",
|
||||
"* **Tree Structure:** Represents the dependencies introduced during elimination more compactly than a Bayes net, especially for sparse problems.\n",
|
||||
"\n",
|
||||
"Like `FactorGraph` and `BayesNet`, `BayesTree` is templated on the type of conditional/clique (e.g., `GaussianBayesTree`)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/BayesTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "bayestree_pip_code"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Requirement already satisfied: gtsam in c:\\users\\porte\\miniconda3\\envs\\gtsam\\lib\\site-packages (4.3a0)Note: you may need to restart the kernel to use updated packages.\n",
|
||||
"\n",
|
||||
"Requirement already satisfied: numpy>=1.11.0 in c:\\users\\porte\\miniconda3\\envs\\gtsam\\lib\\site-packages (from gtsam) (2.2.1)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "bayestree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We need concrete graph types and elimination to get a BayesTree\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianBayesTree, VariableIndex\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a BayesTree (via Elimination)\n",
|
||||
"\n",
|
||||
"BayesTrees are typically obtained by performing multifrontal elimination on a `FactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayestree_eliminate_code",
|
||||
"outputId": "456789ab-cdef-0123-4567-89abcdef0123"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Original Factor Graph:\n",
|
||||
"\n",
|
||||
"size: 7\n",
|
||||
"factor 0: \n",
|
||||
" A[x0] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 1: \n",
|
||||
" A[x0] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x1] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 2: \n",
|
||||
" A[x1] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x2] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 3: \n",
|
||||
" A[l1] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x0] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 4: \n",
|
||||
" A[l1] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x1] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 5: \n",
|
||||
" A[l2] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x1] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 6: \n",
|
||||
" A[l2] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x2] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"\n",
|
||||
"Resulting BayesTree:\n",
|
||||
": cliques: 2, variables: 5\n",
|
||||
"- p(x1 l2 x2 )\n",
|
||||
" R = [ 1.61245 -0.620174 -0.620174 ]\n",
|
||||
" [ 0 1.27098 -1.08941 ]\n",
|
||||
" [ 0 0 0.654654 ]\n",
|
||||
" d = [ 0 0 0 ]\n",
|
||||
" mean: 3 elements\n",
|
||||
" l2: 0\n",
|
||||
" x1: 0\n",
|
||||
" x2: 0\n",
|
||||
" logNormalizationConstant: -2.46292\n",
|
||||
" No noise model\n",
|
||||
"| - p(l1 x0 | x1)\n",
|
||||
" R = [ 1.41421 -0.707107 ]\n",
|
||||
" [ 0 1.58114 ]\n",
|
||||
" S[x1] = [ -0.707107 ]\n",
|
||||
" [ -0.948683 ]\n",
|
||||
" d = [ 0 0 ]\n",
|
||||
" logNormalizationConstant: -1.03316\n",
|
||||
" No noise model\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple Gaussian Factor Graph (more complex this time)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model) # Prior on x0\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # x0 -> x1\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model) # x1 -> x2\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model) # l1 -> x0 (measurement)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # l1 -> x1 (measurement)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # l2 -> x1 (measurement)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model) # l2 -> x2 (measurement)\n",
|
||||
"\n",
|
||||
"print(\"Original Factor Graph:\")\n",
|
||||
"graph.print()\n",
|
||||
"\n",
|
||||
"# Eliminate multifrontally using COLAMD ordering\n",
|
||||
"ordering = Ordering.Colamd(VariableIndex(graph))\n",
|
||||
"# Note: Multifrontal typically yields multiple roots if graph is disconnected\n",
|
||||
"bayes_tree = graph.eliminateMultifrontal(ordering)\n",
|
||||
"\n",
|
||||
"print(\"\\nResulting BayesTree:\")\n",
|
||||
"bayes_tree.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Access\n",
|
||||
"\n",
|
||||
"A `BayesTree` allows access to its root cliques and provides a way to look up the clique containing a specific variable."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayestree_access_code",
|
||||
"outputId": "56789abc-def0-1234-5678-9abcdef01234"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"BayesTree number of cliques: 2\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "AttributeError",
|
||||
"evalue": "'gtsam.gtsam.GaussianBayesTree' object has no attribute 'roots'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[4], line 4\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBayesTree number of cliques: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mbayes_tree\u001b[38;5;241m.\u001b[39msize()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 3\u001b[0m \u001b[38;5;66;03m# Access roots\u001b[39;00m\n\u001b[1;32m----> 4\u001b[0m roots \u001b[38;5;241m=\u001b[39m \u001b[43mbayes_tree\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mroots\u001b[49m()\n\u001b[0;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNumber of roots: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mlen\u001b[39m(roots)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 6\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m roots:\n\u001b[0;32m 7\u001b[0m \u001b[38;5;66;03m# Access the conditional associated with the first root clique\u001b[39;00m\n",
|
||||
"\u001b[1;31mAttributeError\u001b[0m: 'gtsam.gtsam.GaussianBayesTree' object has no attribute 'roots'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"BayesTree number of cliques: {bayes_tree.size()}\")\n",
|
||||
"\n",
|
||||
"# Access roots\n",
|
||||
"roots = bayes_tree.roots()\n",
|
||||
"print(f\"Number of roots: {len(roots)}\")\n",
|
||||
"if roots:\n",
|
||||
" # Access the conditional associated with the first root clique\n",
|
||||
" root_conditional = roots[0].conditional()\n",
|
||||
" print(f\"Root clique 0 conditional frontals: {root_conditional.frontals()}\")\n",
|
||||
"\n",
|
||||
"# Find the clique containing a specific variable\n",
|
||||
"clique_x1 = bayes_tree.clique(X(1))\n",
|
||||
"# clique_x1 is a shared pointer to the clique (e.g., GaussianBayesTreeClique)\n",
|
||||
"print(f\"\\nClique containing x1 ({X(1)}):\")\n",
|
||||
"clique_x1.print() # Print the clique itself"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_solve_md"
|
||||
},
|
||||
"source": [
|
||||
"## Solution and Marginals\n",
|
||||
"\n",
|
||||
"Similar to `BayesNet`, `BayesTree` (specifically derived types like `GaussianBayesTree`) provides an `optimize()` method for finding the MLE solution. It also allows for efficient computation of marginals on individual variables or joint marginals on pairs of variables using belief propagation or shortcut evaluation on the tree."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayestree_solve_code",
|
||||
"outputId": "6789abcd-ef01-2345-6789-abcdef012345"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Optimized Solution (MLE):\n",
|
||||
"VectorValues: 5 elements\n",
|
||||
" l1: 0\n",
|
||||
" l2: 0\n",
|
||||
" x0: 0\n",
|
||||
" x1: 0\n",
|
||||
" x2: 0\n",
|
||||
"\n",
|
||||
"Marginal Factor on x1:\n",
|
||||
"GaussianConditional p(x1)\n",
|
||||
" R = [ 0.774597 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" mean: 1 elements\n",
|
||||
" x1: 0\n",
|
||||
" logNormalizationConstant: -1.17435\n",
|
||||
" No noise model\n",
|
||||
"\n",
|
||||
"Joint Marginal Factor Graph on (x0, x2):\n",
|
||||
"\n",
|
||||
"size: 2\n",
|
||||
"factor 0: p(x0 | x2)\n",
|
||||
" R = [ 1.32288 ]\n",
|
||||
" S[x2] = [ -0.566947 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" logNormalizationConstant: -0.639131\n",
|
||||
" No noise model\n",
|
||||
"factor 1: p(x2)\n",
|
||||
" R = [ 0.654654 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" mean: 1 elements\n",
|
||||
" x2: 0\n",
|
||||
" logNormalizationConstant: -1.34259\n",
|
||||
" No noise model\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Optimize to find the MLE solution (for GaussianBayesTree)\n",
|
||||
"mle_solution = bayes_tree.optimize()\n",
|
||||
"print(\"Optimized Solution (MLE):\")\n",
|
||||
"mle_solution.print()\n",
|
||||
"\n",
|
||||
"# Compute marginal factor on a single variable (returns a Conditional)\n",
|
||||
"marginal_x1 = bayes_tree.marginalFactor(X(1))\n",
|
||||
"print(\"\\nMarginal Factor on x1:\")\n",
|
||||
"marginal_x1.print()\n",
|
||||
"\n",
|
||||
"# Compute joint marginal factor graph on two variables\n",
|
||||
"joint_x0_x2 = bayes_tree.joint(X(0), X(2))\n",
|
||||
"print(\"\\nJoint Marginal Factor Graph on (x0, x2):\")\n",
|
||||
"joint_x0_x2.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bayestree_viz_md"
|
||||
},
|
||||
"source": [
|
||||
"## Visualization\n",
|
||||
"\n",
|
||||
"Bayes trees can be visualized using Graphviz."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bayestree_dot_code",
|
||||
"outputId": "789abcde-f012-3456-789a-bcdef0123456"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"digraph G{\n",
|
||||
"0[label=\"x1, l2, x2\"];\n",
|
||||
"0->1\n",
|
||||
"1[label=\"l1, x0 : x1\"];\n",
|
||||
"}\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/svg+xml": [
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
|
||||
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
|
||||
"<!-- Generated by graphviz version 2.50.0 (0)\n",
|
||||
" -->\n",
|
||||
"<!-- Title: G Pages: 1 -->\n",
|
||||
"<svg width=\"103pt\" height=\"116pt\"\n",
|
||||
" viewBox=\"0.00 0.00 102.89 116.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
|
||||
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 112)\">\n",
|
||||
"<title>G</title>\n",
|
||||
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-112 98.89,-112 98.89,4 -4,4\"/>\n",
|
||||
"<!-- 0 -->\n",
|
||||
"<g id=\"node1\" class=\"node\">\n",
|
||||
"<title>0</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"47.45\" cy=\"-90\" rx=\"44.69\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"47.45\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x1, l2, x2</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- 1 -->\n",
|
||||
"<g id=\"node2\" class=\"node\">\n",
|
||||
"<title>1</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"47.45\" cy=\"-18\" rx=\"47.39\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"47.45\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">l1, x0 : x1</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- 0->1 -->\n",
|
||||
"<g id=\"edge1\" class=\"edge\">\n",
|
||||
"<title>0->1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M47.45,-71.7C47.45,-63.98 47.45,-54.71 47.45,-46.11\"/>\n",
|
||||
"<polygon fill=\"black\" stroke=\"black\" points=\"50.95,-46.1 47.45,-36.1 43.95,-46.1 50.95,-46.1\"/>\n",
|
||||
"</g>\n",
|
||||
"</g>\n",
|
||||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<graphviz.sources.Source at 0x1241c40dbe0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dot_string = bayes_tree.dot()\n",
|
||||
"print(dot_string)\n",
|
||||
"\n",
|
||||
"# To render:\n",
|
||||
"# dot -Tpng bayestree.dot -o bayestree.png\n",
|
||||
"import graphviz\n",
|
||||
"graphviz.Source(dot_string)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,168 +1,206 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# ClusterTree"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `ClusterTree` is a more general structure than an `EliminationTree` or `JunctionTree`. It represents a tree where each node (a 'Cluster') contains a subset of factors from an original factor graph. The key property is that the tree must be 'family preserving', meaning each original factor must belong entirely within a single cluster.\n",
|
||||
"\n",
|
||||
"`ClusterTree` itself is a base class. `EliminatableClusterTree` adds the ability to perform elimination, and `JunctionTree` is a specific type of `EliminatableClusterTree` derived from an `EliminationTree`.\n",
|
||||
"\n",
|
||||
"Direct use of `ClusterTree` in typical Python applications is less common than `JunctionTree` or `BayesTree`, as it's often an intermediate representation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/ClusterTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "clustertree_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "clustertree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"# Note: ClusterTree itself might not be directly exposed or used.\n",
|
||||
"# We typically interact with JunctionTree or BayesTree which build upon it.\n",
|
||||
"# We'll demonstrate concepts using JunctionTree which inherits Cluster features.\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, JunctionTree, GaussianBayesTree\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_concept_md"
|
||||
},
|
||||
"source": [
|
||||
"## Concept and Relation to JunctionTree\n",
|
||||
"\n",
|
||||
"A `JunctionTree` *is a* `ClusterTree` (specifically, an `EliminatableClusterTree`). It's constructed during multifrontal elimination. Each node in the `JunctionTree` is a `Cluster` containing factors that will be eliminated together to form a clique in the resulting `BayesTree`.\n",
|
||||
"\n",
|
||||
"We will create a `JunctionTree` and examine its properties, which include those inherited from `ClusterTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "clustertree_jt_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "ef012345-6789-abcd-ef01-23456789abcd"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Junction Tree (as ClusterTree): \n",
|
||||
"Root(s):\n",
|
||||
"Cluster (1) keys: l1 x1 \n",
|
||||
" Factor 0: JacobianFactor(keys = [7783684379976990720; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Cluster (1) keys: l2 x2 \n",
|
||||
" Factor 0: JacobianFactor(keys = [7783684379976990721; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Cluster (1) keys: x0 \n",
|
||||
" Factor 0: JacobianFactor(keys = [8070450532247928832], Z = [ -1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n",
|
||||
"Accessing a root cluster (node):\n",
|
||||
"Cluster (1) keys: l1 x1 \n",
|
||||
" Factor 0: JacobianFactor(keys = [7783684379976990720; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n",
|
||||
"Number of roots: 1\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a graph (same as BayesTree example)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"\n",
|
||||
"ordering = Ordering.Colamd(graph)\n",
|
||||
"\n",
|
||||
"# Create a Junction Tree (implicitly uses ClusterTree structure)\n",
|
||||
"# Note: JunctionTree constructor might not be directly exposed.\n",
|
||||
"# It's usually an intermediate in eliminateMultifrontal.\n",
|
||||
"# We might need to construct it indirectly or focus on BayesTree access.\n",
|
||||
"\n",
|
||||
"# Let's get the BayesTree first, as JunctionTree creation is internal.\n",
|
||||
"bayes_tree = graph.eliminateMultifrontal(ordering)\n",
|
||||
"\n",
|
||||
"# We can print the BayesTree, which shows the cluster structure\n",
|
||||
"# (Cliques in BayesTree correspond to Clusters in JunctionTree)\n",
|
||||
"print(\"Junction Tree (as ClusterTree): \")\n",
|
||||
"bayes_tree.print() # Printing BayesTree shows clique structure\n",
|
||||
"\n",
|
||||
"# Access root cluster(s)\n",
|
||||
"roots = bayes_tree.roots()\n",
|
||||
"if roots:\n",
|
||||
" print(\"\\nAccessing a root cluster (node):\")\n",
|
||||
" root_clique = roots[0]\n",
|
||||
" # In the JunctionTree, this node would contain the factors that *produced*\n",
|
||||
" # the conditional in the BayesTree clique. We can see the involved keys.\n",
|
||||
" root_clique.print(\"\", gtsam.DefaultKeyFormatter) # Print clique details\n",
|
||||
"\n",
|
||||
"print(f\"\\nNumber of roots: {len(roots)}\")\n",
|
||||
"\n",
|
||||
"# Direct instantiation or manipulation of Cluster/JunctionTree nodes\n",
|
||||
"# is less common in Python than using the results (BayesTree)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# ClusterTree"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `ClusterTree` is a more general structure than an `EliminationTree` or `JunctionTree`. It represents a tree where each node (a 'Cluster') contains a subset of factors from an original factor graph. The key property is that the tree must be 'family preserving', meaning each original factor must belong entirely within a single cluster.\n",
|
||||
"\n",
|
||||
"`ClusterTree` itself is a base class. `EliminatableClusterTree` adds the ability to perform elimination, and `JunctionTree` is a specific type of `EliminatableClusterTree` derived from an `EliminationTree`.\n",
|
||||
"\n",
|
||||
"Direct use of `ClusterTree` in typical Python applications is less common than `JunctionTree` or `BayesTree`, as it's often an intermediate representation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/ClusterTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "clustertree_pip_code"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Requirement already satisfied: gtsam in c:\\users\\porte\\miniconda3\\envs\\gtsam\\lib\\site-packages (4.3a0)Note: you may need to restart the kernel to use updated packages.\n",
|
||||
"\n",
|
||||
"Requirement already satisfied: numpy>=1.11.0 in c:\\users\\porte\\miniconda3\\envs\\gtsam\\lib\\site-packages (from gtsam) (2.2.1)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"id": "clustertree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# Note: ClusterTree itself might not be directly exposed or used.\n",
|
||||
"# We typically interact with JunctionTree or BayesTree which build upon it.\n",
|
||||
"# We'll demonstrate concepts using JunctionTree which inherits Cluster features.\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, VariableIndex, GaussianBayesTree\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "clustertree_concept_md"
|
||||
},
|
||||
"source": [
|
||||
"## Concept and Relation to JunctionTree\n",
|
||||
"\n",
|
||||
"A `JunctionTree` *is a* `ClusterTree` (specifically, an `EliminatableClusterTree`). It's constructed during multifrontal elimination. Each node in the `JunctionTree` is a `Cluster` containing factors that will be eliminated together to form a clique in the resulting `BayesTree`.\n",
|
||||
"\n",
|
||||
"We will create a `JunctionTree` and examine its properties, which include those inherited from `ClusterTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "clustertree_jt_code",
|
||||
"outputId": "ef012345-6789-abcd-ef01-23456789abcd"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Junction Tree (as ClusterTree): \n",
|
||||
": cliques: 2, variables: 5\n",
|
||||
"- p(x1 l2 x2 )\n",
|
||||
" R = [ 1.61245 -0.620174 -0.620174 ]\n",
|
||||
" [ 0 1.27098 -1.08941 ]\n",
|
||||
" [ 0 0 0.654654 ]\n",
|
||||
" d = [ 0 0 0 ]\n",
|
||||
" mean: 3 elements\n",
|
||||
" l2: 0\n",
|
||||
" x1: 0\n",
|
||||
" x2: 0\n",
|
||||
" logNormalizationConstant: -2.46292\n",
|
||||
" No noise model\n",
|
||||
"| - p(l1 x0 | x1)\n",
|
||||
" R = [ 1.41421 -0.707107 ]\n",
|
||||
" [ 0 1.58114 ]\n",
|
||||
" S[x1] = [ -0.707107 ]\n",
|
||||
" [ -0.948683 ]\n",
|
||||
" d = [ 0 0 ]\n",
|
||||
" logNormalizationConstant: -1.03316\n",
|
||||
" No noise model\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "AttributeError",
|
||||
"evalue": "'gtsam.gtsam.GaussianBayesTree' object has no attribute 'roots'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[8], line 28\u001b[0m\n\u001b[0;32m 25\u001b[0m bayes_tree\u001b[38;5;241m.\u001b[39mprint() \u001b[38;5;66;03m# Printing BayesTree shows clique structure\u001b[39;00m\n\u001b[0;32m 27\u001b[0m \u001b[38;5;66;03m# Access root cluster(s)\u001b[39;00m\n\u001b[1;32m---> 28\u001b[0m roots \u001b[38;5;241m=\u001b[39m \u001b[43mbayes_tree\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mroots\u001b[49m()\n\u001b[0;32m 29\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m roots:\n\u001b[0;32m 30\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124mAccessing a root cluster (node):\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
|
||||
"\u001b[1;31mAttributeError\u001b[0m: 'gtsam.gtsam.GaussianBayesTree' object has no attribute 'roots'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a graph (same as BayesTree example)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"\n",
|
||||
"ordering = Ordering.Colamd(VariableIndex(graph))\n",
|
||||
"\n",
|
||||
"# Create a Junction Tree (implicitly uses ClusterTree structure)\n",
|
||||
"# Note: JunctionTree constructor might not be directly exposed.\n",
|
||||
"# It's usually an intermediate in eliminateMultifrontal.\n",
|
||||
"# We might need to construct it indirectly or focus on BayesTree access.\n",
|
||||
"\n",
|
||||
"# Let's get the BayesTree first, as JunctionTree creation is internal.\n",
|
||||
"bayes_tree = graph.eliminateMultifrontal(ordering)\n",
|
||||
"\n",
|
||||
"# We can print the BayesTree, which shows the cluster structure\n",
|
||||
"# (Cliques in BayesTree correspond to Clusters in JunctionTree)\n",
|
||||
"print(\"Junction Tree (as ClusterTree): \")\n",
|
||||
"bayes_tree.print() # Printing BayesTree shows clique structure\n",
|
||||
"\n",
|
||||
"# Access root cluster(s)\n",
|
||||
"roots = bayes_tree.roots()\n",
|
||||
"if roots:\n",
|
||||
" print(\"\\nAccessing a root cluster (node):\")\n",
|
||||
" root_clique = roots[0]\n",
|
||||
" # In the JunctionTree, this node would contain the factors that *produced*\n",
|
||||
" # the conditional in the BayesTree clique. We can see the involved keys.\n",
|
||||
" root_clique.print(\"\", gtsam.DefaultKeyFormatter) # Print clique details\n",
|
||||
"\n",
|
||||
"print(f\"\\nNumber of roots: {len(roots)}\")\n",
|
||||
"\n",
|
||||
"# Direct instantiation or manipulation of Cluster/JunctionTree nodes\n",
|
||||
"# is less common in Python than using the results (BayesTree)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,206 +1,273 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# Conditional"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"`gtsam.Conditional` is the base class for conditional probability distributions or densities that result from variable elimination. Conditionals are essentially specialized factors representing $P(\text{Frontals} | \text{Parents})$.\n",
|
||||
"\n",
|
||||
"Like `gtsam.Factor`, you typically don't instantiate `gtsam.Conditional` directly. Instead, you work with derived classes obtained from elimination, such as:\n",
|
||||
"* `gtsam.GaussianConditional`\n",
|
||||
"* `gtsam.DiscreteConditional`\n",
|
||||
"* `gtsam.HybridGaussianConditional`\n",
|
||||
"* `gtsam.SymbolicConditional`\n",
|
||||
"\n",
|
||||
"This notebook demonstrates the common interface provided by the base class."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Conditional.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "conditional_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "conditional_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We need concrete graph types and elimination to get a Conditional\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_example_md"
|
||||
},
|
||||
"source": [
|
||||
"## Example: Obtaining and Inspecting a Conditional\n",
|
||||
"\n",
|
||||
"We'll create a simple `GaussianFactorGraph` and eliminate one variable to get a `GaussianConditional`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "conditional_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "def01234-5678-9abc-def0-123456789abc"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Eliminating x0 from graph:\n",
|
||||
"Factor Graph: size 2\n",
|
||||
"Factor 0: JacobianFactor(keys = [8070450532247928832], Z = [ -1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"Factor 1: JacobianFactor(keys = [8070450532247928832; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Resulting BayesNet: size 1\n",
|
||||
"Conditional 0: GaussianConditional( P(x0 | x1) = dx0 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.707107 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Conditional Keys (all): [8070450532247928832, 8070450532247928833]\n",
|
||||
"Number of Frontals: 1\n",
|
||||
"Frontal Keys: [8070450532247928832] (x0)\n",
|
||||
"First Frontal Key: 8070450532247928832 (x0)\n",
|
||||
"Number of Parents: 1\n",
|
||||
"Parent Keys: [8070450532247928833] (x1)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple Gaussian Factor Graph P(x0) P(x1|x0)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model1 = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"model2 = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"\n",
|
||||
"# Prior on x0\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model1)\n",
|
||||
"# Factor between x0 and x1\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model2)\n",
|
||||
"\n",
|
||||
"print(\"Eliminating x0 from graph:\")\n",
|
||||
"graph.print()\n",
|
||||
"\n",
|
||||
"# Eliminate x0\n",
|
||||
"ordering = Ordering([X(0)])\n",
|
||||
"bayes_net, remaining_graph = graph.eliminatePartialSequential(ordering)\n",
|
||||
"\n",
|
||||
"print(\"\\nResulting BayesNet:\")\n",
|
||||
"bayes_net.print()\n",
|
||||
"\n",
|
||||
"# Get the resulting conditional P(x0 | x1)\n",
|
||||
"# In this case, it's a GaussianConditional\n",
|
||||
"conditional = bayes_net.at(0) # or bayes_net[0]\n",
|
||||
"\n",
|
||||
"# Access methods from the Conditional base class\n",
|
||||
"print(f\"Conditional Keys (all): {conditional.keys()}\")\n",
|
||||
"print(f\"Number of Frontals: {conditional.nrFrontals()}\")\n",
|
||||
"print(f\"Frontal Keys: {conditional.frontals()} ({gtsam.DefaultKeyFormatter(list(conditional.frontals())[0])})\")\n",
|
||||
"print(f\"First Frontal Key: {conditional.firstFrontalKey()} ({gtsam.DefaultKeyFormatter(conditional.firstFrontalKey())})\")\n",
|
||||
"print(f\"Number of Parents: {conditional.nrParents()}\")\n",
|
||||
"print(f\"Parent Keys: {conditional.parents()} ({gtsam.DefaultKeyFormatter(list(conditional.parents())[0])})\")\n",
|
||||
"\n",
|
||||
"# Conditional objects can also be printed\n",
|
||||
"# conditional.print(\"P(x0 | x1): \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_eval_md"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluation (Derived Class Methods)\n",
|
||||
"\n",
|
||||
"Concrete conditional classes provide methods like `logProbability(values)` or `evaluate(values)` to compute the conditional probability (or density) given values for the parent variables. These methods are defined in the derived classes, not the `Conditional` base class itself."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "conditional_eval_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Example for GaussianConditional (requires VectorValues)\n",
|
||||
"vector_values = gtsam.VectorValues()\n",
|
||||
"# vector_values.insert(X(0), np.array([0.0])) # Value for frontal variable\n",
|
||||
"vector_values.insert(X(1), np.array([1.0])) # Value for parent variable\n",
|
||||
"\n",
|
||||
"# These methods are specific to GaussianConditional / other concrete types\n",
|
||||
"try:\n",
|
||||
" log_prob = conditional.logProbability(vector_values)\n",
|
||||
" # print(f\"\\nLog Probability P(x0|x1=1.0): {log_prob}\")\n",
|
||||
" prob = conditional.evaluate(vector_values)\n",
|
||||
" # print(f\"Probability P(x0|x1=1.0): {prob}\")\n",
|
||||
"except AttributeError:\n",
|
||||
" print(\"\\nNote: logProbability/evaluate called on base Conditional pointer, needs derived type.\")\n",
|
||||
" # In C++, you'd typically have a shared_ptr<GaussianConditional>.\n",
|
||||
" # In Python, if you know the type, you might access methods directly,\n",
|
||||
" # but the base class wrapper doesn't expose derived methods.\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# To properly evaluate, you often use the BayesNet/BayesTree directly\n",
|
||||
"# bayes_net.logProbability(vector_values)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# Conditional"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"`gtsam.Conditional` is the base class for conditional probability distributions or densities that result from variable elimination.\n",
|
||||
"\n",
|
||||
"Let $F$ be the set of frontal variables and $S$ be the set of parent (separator) variables. A conditional represents:\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"P(F | S)\n",
|
||||
"$$\n",
|
||||
"The methods `evaluate`, `logProbability`, and `error` are related:\n",
|
||||
"$$\n",
|
||||
"\\text{evaluate}(F, S) = P(F | S)\n",
|
||||
"$$\n",
|
||||
"$$\n",
|
||||
"\\text{logProbability}(F, S) = \\log P(F | S)\n",
|
||||
"$$\n",
|
||||
"$$\n",
|
||||
"\\text{logProbability}(F, S) = -(\\text{negLogConstant} + \\text{error}(F, S))\n",
|
||||
"$$\n",
|
||||
"where `negLogConstant` is $-\\log k$ for the normalization constant $k$ ensuring $\\int P(F|S) dF = 1$.\n",
|
||||
"\n",
|
||||
"Like `gtsam.Factor`, you typically don't instantiate `gtsam.Conditional` directly. Instead, you work with derived classes obtained from elimination, such as:\n",
|
||||
"* `gtsam.GaussianConditional`\n",
|
||||
"* `gtsam.DiscreteConditional`\n",
|
||||
"* `gtsam.HybridGaussianConditional`\n",
|
||||
"* `gtsam.SymbolicConditional`\n",
|
||||
"\n",
|
||||
"This notebook demonstrates the common interface provided by the base class."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Conditional.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "conditional_pip_code"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Requirement already satisfied: gtsam in c:\\users\\porte\\miniconda3\\envs\\gtsam\\lib\\site-packages (4.3a0)\n",
|
||||
"Requirement already satisfied: numpy>=1.11.0 in c:\\users\\porte\\miniconda3\\envs\\gtsam\\lib\\site-packages (from gtsam) (2.2.1)\n",
|
||||
"Note: you may need to restart the kernel to use updated packages.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "conditional_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We need concrete graph types and elimination to get a Conditional\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_example_md"
|
||||
},
|
||||
"source": [
|
||||
"## Example: Obtaining and Inspecting a Conditional\n",
|
||||
"\n",
|
||||
"We'll create a simple `GaussianFactorGraph` and eliminate one variable to get a `GaussianConditional`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "conditional_create_code",
|
||||
"outputId": "def01234-5678-9abc-def0-123456789abc"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Eliminating x0 from graph:\n",
|
||||
"\n",
|
||||
"size: 2\n",
|
||||
"factor 0: \n",
|
||||
" A[x0] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"factor 1: \n",
|
||||
" A[x0] = [\n",
|
||||
"\t-1\n",
|
||||
"]\n",
|
||||
" A[x1] = [\n",
|
||||
"\t1\n",
|
||||
"]\n",
|
||||
" b = [ 0 ]\n",
|
||||
" Noise model: unit (1) \n",
|
||||
"\n",
|
||||
"Resulting BayesNet:\n",
|
||||
"\n",
|
||||
"size: 1\n",
|
||||
"conditional 0: p(x0 | x1)\n",
|
||||
" R = [ 1.41421 ]\n",
|
||||
" S[x1] = [ -0.707107 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" logNormalizationConstant: -0.572365\n",
|
||||
" No noise model\n",
|
||||
"Conditional Keys (all): [8646911284551352320, 8646911284551352321]\n",
|
||||
"First Frontal Key: 8646911284551352320 (x0)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple Gaussian Factor Graph P(x0) P(x1|x0)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model1 = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"model2 = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"\n",
|
||||
"# Prior on x0\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model1)\n",
|
||||
"# Factor between x0 and x1\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model2)\n",
|
||||
"\n",
|
||||
"print(\"Eliminating x0 from graph:\")\n",
|
||||
"graph.print()\n",
|
||||
"\n",
|
||||
"# Eliminate x0\n",
|
||||
"ordering = Ordering([X(0)])\n",
|
||||
"bayes_net, remaining_graph = graph.eliminatePartialSequential(ordering)\n",
|
||||
"\n",
|
||||
"print(\"\\nResulting BayesNet:\")\n",
|
||||
"bayes_net.print()\n",
|
||||
"\n",
|
||||
"# Get the resulting conditional P(x0 | x1)\n",
|
||||
"# In this case, it's a GaussianConditional\n",
|
||||
"conditional = bayes_net.at(0) # or bayes_net[0]\n",
|
||||
"\n",
|
||||
"# Access methods from the Conditional base class\n",
|
||||
"print(f\"Conditional Keys (all): {conditional.keys()}\")\n",
|
||||
"print(f\"First Frontal Key: {conditional.firstFrontalKey()} ({gtsam.DefaultKeyFormatter(conditional.firstFrontalKey())})\")\n",
|
||||
"\n",
|
||||
"# Conditional objects can also be printed\n",
|
||||
"# conditional.print(\"P(x0 | x1): \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "conditional_eval_md"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluation (Derived Class Methods)\n",
|
||||
"\n",
|
||||
"Concrete conditional classes provide methods like `logProbability(values)` or `evaluate(values)` to compute the conditional probability (or density) given values for the parent variables. These methods are defined in the derived classes, not the `Conditional` base class itself."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"id": "conditional_eval_code"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"Log Probability P(x0|x1=1.0): -0.8223649429247\n",
|
||||
"Probability P(x0|x1=1.0): 0.43939128946772243\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"-0.8223649429247"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Example for GaussianConditional (requires VectorValues)\n",
|
||||
"vector_values = gtsam.VectorValues()\n",
|
||||
"vector_values.insert(X(0), np.array([0.0])) # Value for frontal variable\n",
|
||||
"vector_values.insert(X(1), np.array([1.0])) # Value for parent variable\n",
|
||||
"\n",
|
||||
"# These methods are specific to GaussianConditional / other concrete types\n",
|
||||
"try:\n",
|
||||
" log_prob = conditional.logProbability(vector_values)\n",
|
||||
" print(f\"\\nLog Probability P(x0|x1=1.0): {log_prob}\")\n",
|
||||
" prob = conditional.evaluate(vector_values)\n",
|
||||
" print(f\"Probability P(x0|x1=1.0): {prob}\")\n",
|
||||
"except AttributeError:\n",
|
||||
" print(\"\\nNote: logProbability/evaluate called on base Conditional pointer, needs derived type.\")\n",
|
||||
" # In C++, you'd typically have a shared_ptr<GaussianConditional>.\n",
|
||||
" # In Python, if you know the type, you might access methods directly,\n",
|
||||
" # but the base class wrapper doesn't expose derived methods.\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"# To properly evaluate, you often use the BayesNet/BayesTree directly\n",
|
||||
"bayes_net.logProbability(vector_values)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,289 +1,331 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# DotWriter"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"The `DotWriter` class is a helper utility in GTSAM used to customize the generation of Graphviz `.dot` file strings for visualizing factor graphs, Bayes nets, and Bayes trees.\n",
|
||||
"\n",
|
||||
"It allows you to control aspects like figure size, whether factors are plotted as points, how edges are drawn, and specify explicit positions for variables and factors."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/DotWriter.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dotwriter_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam graphviz"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dotwriter_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import DotWriter\n",
|
||||
"from gtsam import SymbolicFactorGraph # Example graph type\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"import graphviz # For rendering\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating and Configuring a DotWriter\n",
|
||||
"\n",
|
||||
"You create a `DotWriter` object and can then modify its public member variables to change the output format."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dotwriter_config_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"writer = DotWriter(\n",
|
||||
" figureWidthInches = 8.0,\n",
|
||||
" figureHeightInches = 5.0,\n",
|
||||
" plotFactorPoints = True, # Draw black dots for factors\n",
|
||||
" connectKeysToFactor = True, # Draw edges from variables to factor dots\n",
|
||||
" binaryEdges = False # Don't simplify binary factors to single edges\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# --- Configuration Options ---\n",
|
||||
"\n",
|
||||
"# Specify explicit positions (used by neato -n)\n",
|
||||
"writer.variablePositions = {\n",
|
||||
" X(0): gtsam.Point2(0, 0),\n",
|
||||
" X(1): gtsam.Point2(2, 0),\n",
|
||||
" X(2): gtsam.Point2(4, 0),\n",
|
||||
" L(1): gtsam.Point2(1, 2),\n",
|
||||
" L(2): gtsam.Point2(3, 2)\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Specify position hints (alternative, uses symbol char and index)\n",
|
||||
"# writer.positionHints = {'x': 0.0, 'l': 2.0} # Puts 'x' vars at y=0, 'l' vars at y=2\n",
|
||||
"\n",
|
||||
"# Specify which variables should be boxes\n",
|
||||
"writer.boxes = {L(1), L(2)}\n",
|
||||
"\n",
|
||||
"# Specify factor positions (less common)\n",
|
||||
"# writer.factorPositions = {3: gtsam.Point2(0.5, 1.0)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_usage_md"
|
||||
},
|
||||
"source": [
|
||||
"## Usage with Graph Objects\n",
|
||||
"\n",
|
||||
"The configured `DotWriter` object is passed as an argument to the `.dot()` method of `FactorGraph`, `BayesNet`, or `BayesTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dotwriter_graph_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "bcdef012-3456-789a-bcde-f0123456789a"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/svg+xml": [
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"288pt\" height=\"180pt\" viewBox=\"0 0 288 180\">\n",
|
||||
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 176)\">\n",
|
||||
"<title>%0</title>\n",
|
||||
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-176 284,-176 284,4 -4,4\"></polygon>\n",
|
||||
"<g id=\"node1\" class=\"node\">\n",
|
||||
"<title>var8070450532247928832</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"0\" cy=\"-168\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"0\" y=\"-164.3\" font-family=\"Times,serif\" font-size=\"14.00\">x0</text>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node6\" class=\"node\">\n",
|
||||
"<title>factor0</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"0\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge1\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928832--factor0</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M0,-149.7C0,-136.29 0,-116.94 0,-100.2\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node2\" class=\"node\">\n",
|
||||
"<title>var8070450532247928833</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"72\" cy=\"-168\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"72\" y=\"-164.3\" font-family=\"Times,serif\" font-size=\"14.00\">x1</text>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node7\" class=\"node\">\n",
|
||||
"<title>factor1</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"36\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge2\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928832--factor1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M10.86,-151.33C16.65,-138.34 25.54,-118.42 31.25,-104.29\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge3\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928833--factor1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M61.14,-151.33C55.35,-138.34 46.46,-118.42 40.75,-104.29\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node3\" class=\"node\">\n",
|
||||
"<title>var8070450532247928834</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"144\" cy=\"-168\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"144\" y=\"-164.3\" font-family=\"Times,serif\" font-size=\"14.00\">x2</text>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node8\" class=\"node\">\n",
|
||||
"<title>factor2</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"108\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge4\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928833--factor2</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M82.86,-151.33C88.65,-138.34 97.54,-118.42 103.25,-104.29\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge5\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928834--factor2</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M133.14,-151.33C127.35,-138.34 118.46,-118.42 112.75,-104.29\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node4\" class=\"node\">\n",
|
||||
"<title>var7783684379976990720</title>\n",
|
||||
"<polygon fill=\"none\" stroke=\"black\" points=\"36,-36 0,-36 0,0 36,0 36,-36\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"18\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">l1</text>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node9\" class=\"node\">\n",
|
||||
"<title>factor3</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"9\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge6\" class=\"edge\">\n",
|
||||
"<title>var7783684379976990720--factor3</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M15.54,-36.4C14.23,-45.91 12.2,-60.18 10.73,-71.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge7\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928832--factor3</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M2.46,-149.6C3.77,-140.09 5.8,-125.82 7.27,-114.27\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node10\" class=\"node\">\n",
|
||||
"<title>factor4</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"63\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge8\" class=\"edge\">\n",
|
||||
"<title>var7783684379976990720--factor4</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M28.86,-36.4C36.18,-46.75 47.65,-62.56 55.44,-73.58\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge9\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928833--factor4</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M70.01,-149.85C68.75,-140.33 66.89,-126.08 65.46,-114.56\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node5\" class=\"node\">\n",
|
||||
"<title>var7783684379976990721</title>\n",
|
||||
"<polygon fill=\"none\" stroke=\"black\" points=\"108,-36 72,-36 72,0 108,0 108,-36\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"90\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">l2</text>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node11\" class=\"node\">\n",
|
||||
"<title>factor5</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"81\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge10\" class=\"edge\">\n",
|
||||
"<title>var7783684379976990721--factor5</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M87.54,-36.4C86.23,-45.91 84.2,-60.18 82.73,-71.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge11\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928833--factor5</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M74.46,-149.6C75.77,-140.09 77.8,-125.82 79.27,-114.27\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"node12\" class=\"node\">\n",
|
||||
"<title>factor6</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"135\" cy=\"-86\" rx=\"4\" ry=\"4\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge12\" class=\"edge\">\n",
|
||||
"<title>var7783684379976990721--factor6</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M100.86,-36.4C108.18,-46.75 119.65,-62.56 127.44,-73.58\"/>\n",
|
||||
"</g>\n",
|
||||
"<g id=\"edge13\" class=\"edge\">\n",
|
||||
"<title>var8070450532247928834--factor6</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M142.01,-149.85C140.75,-140.33 138.89,-126.08 137.46,-114.56\"/>\n",
|
||||
"</g>\n",
|
||||
"</g>\n",
|
||||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<graphviz.files.Source at 0x7f87c4e3f340>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create the same graph as in VariableIndex example\n",
|
||||
"graph = SymbolicFactorGraph()\n",
|
||||
"graph.push_factor(X(0)) # Factor 0\n",
|
||||
"graph.push_factor(X(0), X(1)) # Factor 1\n",
|
||||
"graph.push_factor(X(1), X(2)) # Factor 2\n",
|
||||
"graph.push_factor(X(0), L(1)) # Factor 3\n",
|
||||
"graph.push_factor(X(1), L(1)) # Factor 4\n",
|
||||
"graph.push_factor(X(1), L(2)) # Factor 5\n",
|
||||
"graph.push_factor(X(2), L(2)) # Factor 6\n",
|
||||
"\n",
|
||||
"# Generate dot string using the configured writer\n",
|
||||
"dot_string = graph.dot(writer=writer)\n",
|
||||
"# print(dot_string)\n",
|
||||
"\n",
|
||||
"# Render the graph\n",
|
||||
"graphviz.Source(dot_string)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# DotWriter"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"The `DotWriter` class is a helper utility in GTSAM used to customize the generation of Graphviz `.dot` file strings for visualizing factor graphs, Bayes nets, and Bayes trees.\n",
|
||||
"\n",
|
||||
"It allows you to control aspects like figure size, whether factors are plotted as points, how edges are drawn, and specify explicit positions for variables and factors."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/DotWriter.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dotwriter_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam graphviz"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "dotwriter_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import DotWriter\n",
|
||||
"from gtsam import SymbolicFactorGraph # Example graph type\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"import graphviz # For rendering\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating and Configuring a DotWriter\n",
|
||||
"\n",
|
||||
"You create a `DotWriter` object and can then modify its public member variables to change the output format."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"id": "dotwriter_config_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"writer = DotWriter(\n",
|
||||
" figureWidthInches = 8.0,\n",
|
||||
" figureHeightInches = 5.0,\n",
|
||||
" plotFactorPoints = True, # Draw black dots for factors\n",
|
||||
" connectKeysToFactor = True, # Draw edges from variables to factor dots\n",
|
||||
" binaryEdges = False # Don't simplify binary factors to single edges\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# --- Configuration Options ---\n",
|
||||
"\n",
|
||||
"# Specify explicit positions (used by neato -n)\n",
|
||||
"writer.variablePositions = {\n",
|
||||
" X(0): gtsam.Point2(0, 0),\n",
|
||||
" X(1): gtsam.Point2(2, 0),\n",
|
||||
" X(2): gtsam.Point2(4, 0),\n",
|
||||
" L(1): gtsam.Point2(1, 2),\n",
|
||||
" L(2): gtsam.Point2(3, 2)\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Specify position hints (alternative, uses symbol char and index)\n",
|
||||
"# writer.positionHints = {'x': 0.0, 'l': 2.0} # Puts 'x' vars at y=0, 'l' vars at y=2\n",
|
||||
"\n",
|
||||
"# Specify which variables should be boxes\n",
|
||||
"writer.boxes = {L(1), L(2)}\n",
|
||||
"\n",
|
||||
"# Specify factor positions (less common)\n",
|
||||
"# writer.factorPositions = {3: gtsam.Point2(0.5, 1.0)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dotwriter_usage_md"
|
||||
},
|
||||
"source": [
|
||||
"## Usage with Graph Objects\n",
|
||||
"\n",
|
||||
"The configured `DotWriter` object is passed as an argument to the `.dot()` method of `FactorGraph`, `BayesNet`, or `BayesTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "dotwriter_graph_code",
|
||||
"outputId": "bcdef012-3456-789a-bcde-f0123456789a"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/svg+xml": [
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
|
||||
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
|
||||
"<!-- Generated by graphviz version 2.50.0 (0)\n",
|
||||
" -->\n",
|
||||
"<!-- Pages: 1 -->\n",
|
||||
"<svg width=\"350pt\" height=\"84pt\"\n",
|
||||
" viewBox=\"0.00 0.00 350.00 83.60\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
|
||||
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 79.6)\">\n",
|
||||
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-79.6 346,-79.6 346,4 -4,4\"/>\n",
|
||||
"<!-- var7782220156096217089 -->\n",
|
||||
"<g id=\"node1\" class=\"node\">\n",
|
||||
"<title>var7782220156096217089</title>\n",
|
||||
"<polygon fill=\"none\" stroke=\"black\" points=\"126,-75.6 72,-75.6 72,-39.6 126,-39.6 126,-75.6\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"99\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">l1</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor3 -->\n",
|
||||
"<g id=\"node9\" class=\"node\">\n",
|
||||
"<title>factor3</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"75\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var7782220156096217089--factor3 -->\n",
|
||||
"<g id=\"edge7\" class=\"edge\">\n",
|
||||
"<title>var7782220156096217089--factor3</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M91.41,-39.58C85.25,-25.79 77.31,-7.97 75.42,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor4 -->\n",
|
||||
"<g id=\"node10\" class=\"node\">\n",
|
||||
"<title>factor4</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"123\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var7782220156096217089--factor4 -->\n",
|
||||
"<g id=\"edge9\" class=\"edge\">\n",
|
||||
"<title>var7782220156096217089--factor4</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M106.59,-39.58C112.75,-25.79 120.69,-7.97 122.58,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var7782220156096217090 -->\n",
|
||||
"<g id=\"node2\" class=\"node\">\n",
|
||||
"<title>var7782220156096217090</title>\n",
|
||||
"<polygon fill=\"none\" stroke=\"black\" points=\"342,-75.6 288,-75.6 288,-39.6 342,-39.6 342,-75.6\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"315\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">l2</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor5 -->\n",
|
||||
"<g id=\"node11\" class=\"node\">\n",
|
||||
"<title>factor5</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"243\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var7782220156096217090--factor5 -->\n",
|
||||
"<g id=\"edge11\" class=\"edge\">\n",
|
||||
"<title>var7782220156096217090--factor5</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M292.22,-39.58C273.76,-25.79 249.92,-7.97 244.25,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor6 -->\n",
|
||||
"<g id=\"node12\" class=\"node\">\n",
|
||||
"<title>factor6</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"279\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var7782220156096217090--factor6 -->\n",
|
||||
"<g id=\"edge13\" class=\"edge\">\n",
|
||||
"<title>var7782220156096217090--factor6</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M303.61,-39.58C294.38,-25.79 282.46,-7.97 279.62,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352320 -->\n",
|
||||
"<g id=\"node3\" class=\"node\">\n",
|
||||
"<title>var8646911284551352320</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"27\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x0</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor0 -->\n",
|
||||
"<g id=\"node6\" class=\"node\">\n",
|
||||
"<title>factor0</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"27\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352320--factor0 -->\n",
|
||||
"<g id=\"edge1\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352320--factor0</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M27,-39.58C27,-25.79 27,-7.97 27,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor1 -->\n",
|
||||
"<g id=\"node7\" class=\"node\">\n",
|
||||
"<title>factor1</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"100\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352320--factor1 -->\n",
|
||||
"<g id=\"edge2\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352320--factor1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M44.67,-43.58C64.25,-29.15 93.78,-7.38 99.15,-3.43\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352320--factor3 -->\n",
|
||||
"<g id=\"edge6\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352320--factor3</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M40.37,-41.61C52.8,-27.68 69.99,-8.41 74.09,-3.81\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321 -->\n",
|
||||
"<g id=\"node4\" class=\"node\">\n",
|
||||
"<title>var8646911284551352321</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"171\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"171\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x1</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321--factor1 -->\n",
|
||||
"<g id=\"edge3\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352321--factor1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M153.81,-43.58C134.77,-29.15 106.05,-7.38 100.83,-3.43\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor2 -->\n",
|
||||
"<g id=\"node8\" class=\"node\">\n",
|
||||
"<title>factor2</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"178\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321--factor2 -->\n",
|
||||
"<g id=\"edge4\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352321--factor2</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M173.21,-39.58C175.01,-25.79 177.33,-7.97 177.88,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321--factor4 -->\n",
|
||||
"<g id=\"edge8\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352321--factor4</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M157.63,-41.61C145.2,-27.68 128.01,-8.41 123.91,-3.81\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321--factor5 -->\n",
|
||||
"<g id=\"edge10\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352321--factor5</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M188.43,-43.58C207.74,-29.15 236.87,-7.38 242.16,-3.43\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352322 -->\n",
|
||||
"<g id=\"node5\" class=\"node\">\n",
|
||||
"<title>var8646911284551352322</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"243\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"243\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x2</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352322--factor2 -->\n",
|
||||
"<g id=\"edge5\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352322--factor2</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M226.6,-43.02C209.75,-29.08 185.18,-8.74 179.3,-3.87\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352322--factor6 -->\n",
|
||||
"<g id=\"edge12\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352322--factor6</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M253.61,-40.75C262.9,-26.86 275.37,-8.22 278.34,-3.78\"/>\n",
|
||||
"</g>\n",
|
||||
"</g>\n",
|
||||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<graphviz.sources.Source at 0x1cdd6cacc20>"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create the same graph as in VariableIndex example\n",
|
||||
"graph = SymbolicFactorGraph()\n",
|
||||
"graph.push_factor(X(0)) # Factor 0\n",
|
||||
"graph.push_factor(X(0), X(1)) # Factor 1\n",
|
||||
"graph.push_factor(X(1), X(2)) # Factor 2\n",
|
||||
"graph.push_factor(X(0), L(1)) # Factor 3\n",
|
||||
"graph.push_factor(X(1), L(1)) # Factor 4\n",
|
||||
"graph.push_factor(X(1), L(2)) # Factor 5\n",
|
||||
"graph.push_factor(X(2), L(2)) # Factor 6\n",
|
||||
"\n",
|
||||
"# Generate dot string using the configured writer\n",
|
||||
"dot_string = graph.dot(writer=writer)\n",
|
||||
"# print(dot_string)\n",
|
||||
"\n",
|
||||
"# Render the graph\n",
|
||||
"graphviz.Source(dot_string)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,154 +1,167 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# EdgeKey"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"An `EdgeKey` is a utility class in GTSAM used to encode a pair of 32-bit unsigned integers into a single 64-bit `gtsam.Key`. This can be useful for representing edges in a graph or other paired relationships where each element of the pair fits within 32 bits."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/EdgeKey.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "edgekey_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "edgekey_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import EdgeKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"An `EdgeKey` can be created by providing two 32-bit unsigned integers (`i` and `j`). It can also be created by decoding an existing `gtsam.Key` (integer), assuming it was encoded using the `EdgeKey` format."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "edgekey_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "cdef1234-5678-90ab-cdef-1234567890ab"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"EdgeKey from (10, 20): {10, 20}\n",
|
||||
"EdgeKey from key 42949672980: {10, 20}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create EdgeKey from integers i=10, j=20\n",
|
||||
"ekey1 = EdgeKey(10, 20)\n",
|
||||
"print(f\"EdgeKey from (10, 20): {ekey1}\") # Uses __str__ which calls operator std::string\n",
|
||||
"\n",
|
||||
"# Get the underlying integer key\n",
|
||||
"key1 = ekey1.key()\n",
|
||||
"\n",
|
||||
"# Reconstruct EdgeKey from the key\n",
|
||||
"ekey2 = EdgeKey(key1)\n",
|
||||
"print(f\"EdgeKey from key {key1}: {ekey2}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Usage\n",
|
||||
"\n",
|
||||
"You can access the original `i` and `j` values and the combined `Key`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "edgekey_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "def12345-6789-0abc-def1-234567890abc"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"EdgeKey: {123, 456}\n",
|
||||
" i: 123\n",
|
||||
" j: 456\n",
|
||||
" Key: 528280977848\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"edge = EdgeKey(123, 456)\n",
|
||||
"\n",
|
||||
"print(f\"EdgeKey: {edge}\")\n",
|
||||
"print(f\" i: {edge.i()}\")\n",
|
||||
"print(f\" j: {edge.j()}\")\n",
|
||||
"print(f\" Key: {edge.key()}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# EdgeKey"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"An `EdgeKey` is a utility class in GTSAM used to encode a pair of 32-bit unsigned integers into a single 64-bit `gtsam.Key`. This can be useful for representing edges in a graph or other paired relationships where each element of the pair fits within 32 bits."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/EdgeKey.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "edgekey_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "edgekey_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import EdgeKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"An `EdgeKey` can be created by providing two 32-bit unsigned integers (`i` and `j`). It can also be created by decoding an existing `gtsam.Key` (integer), assuming it was encoded using the `EdgeKey` format."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "edgekey_create_code",
|
||||
"outputId": "cdef1234-5678-90ab-cdef-1234567890ab"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"EdgeKey from (10, 20): {10, 20}\n",
|
||||
"\n",
|
||||
"EdgeKey from key 42949672980: {10, 20}\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create EdgeKey from integers i=10, j=20\n",
|
||||
"ekey1 = EdgeKey(10, 20)\n",
|
||||
"print(f\"EdgeKey from (10, 20): {ekey1}\") # Uses __str__ which calls operator std::string\n",
|
||||
"\n",
|
||||
"# Get the underlying integer key\n",
|
||||
"key1 = ekey1.key()\n",
|
||||
"\n",
|
||||
"# Reconstruct EdgeKey from the key\n",
|
||||
"ekey2 = EdgeKey(key1)\n",
|
||||
"print(f\"EdgeKey from key {key1}: {ekey2}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edgekey_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Usage\n",
|
||||
"\n",
|
||||
"You can access the original `i` and `j` values and the combined `Key`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "edgekey_access_code",
|
||||
"outputId": "def12345-6789-0abc-def1-234567890abc"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"EdgeKey: {123, 456}\n",
|
||||
"\n",
|
||||
" i: 123\n",
|
||||
" j: 456\n",
|
||||
" Key: 528280977864\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"edge = EdgeKey(123, 456)\n",
|
||||
"\n",
|
||||
"print(f\"EdgeKey: {edge}\")\n",
|
||||
"print(f\" i: {edge.i()}\")\n",
|
||||
"print(f\" j: {edge.j()}\")\n",
|
||||
"print(f\" Key: {edge.key()}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,198 +1,220 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# EliminationTree"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"An `EliminationTree` represents the computational structure of sequential variable elimination (like Gaussian elimination) on a `FactorGraph` given a specific `Ordering`.\n",
|
||||
"\n",
|
||||
"Each node in the tree corresponds to a variable being eliminated. The children of a node represent variables that were eliminated earlier and produced factors involving the parent variable. The factors originally involving the variable at a node are stored at that node.\n",
|
||||
"\n",
|
||||
"Eliminating an `EliminationTree` yields a `BayesNet`.\n",
|
||||
"\n",
|
||||
"While fundamental to the theory, direct manipulation of `EliminationTree` objects in Python is less common than using the `eliminateSequential` method on a `FactorGraph`, which uses the `EliminationTree` internally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/EliminationTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "etree_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "etree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# EliminationTree is templated, need concrete types\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianEliminationTree, GaussianBayesNet\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating an EliminationTree\n",
|
||||
"\n",
|
||||
"An `EliminationTree` is constructed from a `FactorGraph` and an `Ordering`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "etree_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "f0123456-789a-bcde-f012-3456789abcde"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Elimination Tree: \n",
|
||||
"Root(s):\n",
|
||||
"Node (x2)\n",
|
||||
"JacobianFactor(keys = [8070450532247928833; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"JacobianFactor(keys = [7783684379976990721; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (l2)\n",
|
||||
" JacobianFactor(keys = [7783684379976990721; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (x1)\n",
|
||||
" JacobianFactor(keys = [8070450532247928832; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" JacobianFactor(keys = [7783684379976990720; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (l1)\n",
|
||||
" JacobianFactor(keys = [7783684379976990720; 8070450532247928832], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (x0)\n",
|
||||
" JacobianFactor(keys = [8070450532247928832], Z = [ -1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a graph (same as BayesTree example)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"\n",
|
||||
"# Define an ordering\n",
|
||||
"ordering = Ordering([X(0), L(1), X(1), L(2), X(2)])\n",
|
||||
"\n",
|
||||
"# Construct the Elimination Tree\n",
|
||||
"elimination_tree = GaussianEliminationTree(graph, ordering)\n",
|
||||
"\n",
|
||||
"elimination_tree.print(\"Elimination Tree: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_eliminate_md"
|
||||
},
|
||||
"source": [
|
||||
"## Elimination\n",
|
||||
"\n",
|
||||
"The primary use of an `EliminationTree` is to perform sequential elimination to produce a `BayesNet`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "etree_eliminate_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "01234567-89ab-cdef-0123-456789abcdef"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"BayesNet from EliminationTree: size 5\n",
|
||||
"Conditional 0: GaussianConditional( P(x0 | l1) = dx0 - R*dl1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Conditional 1: GaussianConditional( P(l1 | x1) = dl1 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Conditional 2: GaussianConditional( P(x1 | l2, x2) = dx1 - R1*dl2 - R2*dx2 - d), R1 = [ 0.333333 ], R2 = [ 0.333333 ], d = [ 0 ], sigmas = [ 0.745356 ])\n",
|
||||
"Conditional 3: GaussianConditional( P(l2 | x2) = dl2 - R*dx2 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Conditional 4: GaussianConditional( P(x2) = dx2 - d), d = [ 0 ], sigmas = [ 0.774597 ])\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# The eliminate function needs to be specified (e.g., EliminateGaussian)\n",
|
||||
"# In Python, this is usually handled internally by graph.eliminateSequential\n",
|
||||
"# but the C++ EliminationTree has an eliminate method.\n",
|
||||
"\n",
|
||||
"# Let's call the graph's eliminateSequential which uses the tree internally\n",
|
||||
"bayes_net, remaining_graph = graph.eliminatePartialSequential(ordering)\n",
|
||||
"\n",
|
||||
"print(\"BayesNet from EliminationTree:\")\n",
|
||||
"bayes_net.print()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# EliminationTree"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"An `EliminationTree` represents the computational structure of sequential variable elimination (like Gaussian elimination) on a `FactorGraph` given a specific `Ordering`.\n",
|
||||
"\n",
|
||||
"Each node in the tree corresponds to a variable being eliminated. The children of a node represent variables that were eliminated earlier and produced factors involving the parent variable. The factors originally involving the variable at a node are stored at that node.\n",
|
||||
"\n",
|
||||
"Eliminating an `EliminationTree` yields a `BayesNet`.\n",
|
||||
"\n",
|
||||
"While fundamental to the theory, direct manipulation of `EliminationTree` objects in Python is less common than using the `eliminateSequential` method on a `FactorGraph`, which uses the `EliminationTree` internally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/EliminationTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "etree_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "etree_import_code"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ImportError",
|
||||
"evalue": "cannot import name 'GaussianEliminationTree' from 'gtsam' (c:\\Users\\porte\\miniconda3\\envs\\gtsam\\Lib\\site-packages\\gtsam\\__init__.py)",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mImportError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[2], line 5\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mnumpy\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[0;32m 4\u001b[0m \u001b[38;5;66;03m# EliminationTree is templated, need concrete types\u001b[39;00m\n\u001b[1;32m----> 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mgtsam\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m GaussianFactorGraph, Ordering, GaussianEliminationTree, GaussianBayesNet\n\u001b[0;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mgtsam\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m symbol_shorthand\n\u001b[0;32m 8\u001b[0m X \u001b[38;5;241m=\u001b[39m symbol_shorthand\u001b[38;5;241m.\u001b[39mX\n",
|
||||
"\u001b[1;31mImportError\u001b[0m: cannot import name 'GaussianEliminationTree' from 'gtsam' (c:\\Users\\porte\\miniconda3\\envs\\gtsam\\Lib\\site-packages\\gtsam\\__init__.py)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# EliminationTree is templated, need concrete types\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianEliminationTree, GaussianBayesNet\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating an EliminationTree\n",
|
||||
"\n",
|
||||
"An `EliminationTree` is constructed from a `FactorGraph` and an `Ordering`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "etree_create_code",
|
||||
"outputId": "f0123456-789a-bcde-f012-3456789abcde"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Elimination Tree: \n",
|
||||
"Root(s):\n",
|
||||
"Node (x2)\n",
|
||||
"JacobianFactor(keys = [8070450532247928833; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"JacobianFactor(keys = [7783684379976990721; 8070450532247928834], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (l2)\n",
|
||||
" JacobianFactor(keys = [7783684379976990721; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (x1)\n",
|
||||
" JacobianFactor(keys = [8070450532247928832; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" JacobianFactor(keys = [7783684379976990720; 8070450532247928833], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (l1)\n",
|
||||
" JacobianFactor(keys = [7783684379976990720; 8070450532247928832], A[0] = [ -1 1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
" Children:\n",
|
||||
" Node (x0)\n",
|
||||
" JacobianFactor(keys = [8070450532247928832], Z = [ -1 ], b = [ 0 ], model = diagonal sigmas [1])\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a graph (same as BayesTree example)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"\n",
|
||||
"# Define an ordering\n",
|
||||
"ordering = Ordering([X(0), L(1), X(1), L(2), X(2)])\n",
|
||||
"\n",
|
||||
"# Construct the Elimination Tree\n",
|
||||
"elimination_tree = GaussianEliminationTree(graph, ordering)\n",
|
||||
"\n",
|
||||
"elimination_tree.print(\"Elimination Tree: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "etree_eliminate_md"
|
||||
},
|
||||
"source": [
|
||||
"## Elimination\n",
|
||||
"\n",
|
||||
"The primary use of an `EliminationTree` is to perform sequential elimination to produce a `BayesNet`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "etree_eliminate_code",
|
||||
"outputId": "01234567-89ab-cdef-0123-456789abcdef"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"BayesNet from EliminationTree: size 5\n",
|
||||
"Conditional 0: GaussianConditional( P(x0 | l1) = dx0 - R*dl1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Conditional 1: GaussianConditional( P(l1 | x1) = dl1 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Conditional 2: GaussianConditional( P(x1 | l2, x2) = dx1 - R1*dl2 - R2*dx2 - d), R1 = [ 0.333333 ], R2 = [ 0.333333 ], d = [ 0 ], sigmas = [ 0.745356 ])\n",
|
||||
"Conditional 3: GaussianConditional( P(l2 | x2) = dl2 - R*dx2 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"Conditional 4: GaussianConditional( P(x2) = dx2 - d), d = [ 0 ], sigmas = [ 0.774597 ])\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# The eliminate function needs to be specified (e.g., EliminateGaussian)\n",
|
||||
"# In Python, this is usually handled internally by graph.eliminateSequential\n",
|
||||
"# but the C++ EliminationTree has an eliminate method.\n",
|
||||
"\n",
|
||||
"# Let's call the graph's eliminateSequential which uses the tree internally\n",
|
||||
"bayes_net, remaining_graph = graph.eliminatePartialSequential(ordering)\n",
|
||||
"\n",
|
||||
"print(\"BayesNet from EliminationTree:\")\n",
|
||||
"bayes_net.print()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,178 +1,199 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# Factor"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"`gtsam.Factor` is the abstract base class for all factors in GTSAM, including nonlinear factors, Gaussian factors, discrete factors, and conditionals. It defines the basic interface common to all factors, primarily centered around the set of variables (keys) the factor involves.\n",
|
||||
"\n",
|
||||
"You typically do not instantiate `gtsam.Factor` directly but rather work with its derived classes like `gtsam.NonlinearFactor`, `gtsam.JacobianFactor`, `gtsam.DiscreteFactor`, etc."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Factor.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "factor_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "factor_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam.utils.test_case import GtsamTestCase\n",
|
||||
"\n",
|
||||
"# We need a concrete factor type for demonstration\n",
|
||||
"from gtsam import PriorFactorPose2, BetweenFactorPose2, Pose2, Rot2, Point2\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_interface_md"
|
||||
},
|
||||
"source": [
|
||||
"## Basic Interface\n",
|
||||
"\n",
|
||||
"All factors provide methods to access the keys of the variables they involve."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "factor_keys_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "01234567-89ab-cdef-0123-456789abcdef"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Prior factor keys: [8070450532247928832] (x0)\n",
|
||||
"Prior factor size: 1\n",
|
||||
"Between factor keys: [8070450532247928832, 8070450532247928833] (x0, x1)\n",
|
||||
"Between factor size: 2\n",
|
||||
"Is prior factor empty? False\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"noise_model = gtsam.noiseModel.Diagonal.Sigmas(Point3(0.1, 0.1, 0.05))\n",
|
||||
"\n",
|
||||
"# Create some concrete factors\n",
|
||||
"prior_factor = PriorFactorPose2(X(0), Pose2(0, 0, 0), noise_model)\n",
|
||||
"between_factor = BetweenFactorPose2(X(0), X(1), Pose2(1, 0, 0), noise_model)\n",
|
||||
"\n",
|
||||
"# Access keys (methods inherited from gtsam.Factor)\n",
|
||||
"prior_keys = prior_factor.keys()\n",
|
||||
"print(f\"Prior factor keys: {prior_keys} ({gtsam.DefaultKeyFormatter(prior_keys[0])})\")\n",
|
||||
"print(f\"Prior factor size: {prior_factor.size()}\")\n",
|
||||
"\n",
|
||||
"between_keys = between_factor.keys()\n",
|
||||
"print(f\"Between factor keys: {between_keys} ({gtsam.DefaultKeyFormatter(between_keys[0])}, {gtsam.DefaultKeyFormatter(between_keys[1])})\")\n",
|
||||
"print(f\"Between factor size: {between_factor.size()}\")\n",
|
||||
"\n",
|
||||
"print(f\"Is prior factor empty? {prior_factor.empty()}\")\n",
|
||||
"\n",
|
||||
"# Factors can be printed\n",
|
||||
"# prior_factor.print(\"Prior Factor: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_error_md"
|
||||
},
|
||||
"source": [
|
||||
"## Error Function\n",
|
||||
"\n",
|
||||
"A key method for many factor types (especially nonlinear and Gaussian) is `error(Values)`. This evaluates the negative log-likelihood of the factor given a specific assignment of variable values. For optimization, the goal is typically to find the `Values` that minimize the total error (sum of errors from all factors)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "factor_error_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "12345678-9abc-def0-1234-56789abcdef0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Error at ground truth: 0.0\n",
|
||||
"Error with incorrect x1: 50.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"values = gtsam.Values()\n",
|
||||
"values.insert(X(0), Pose2(0, 0, 0))\n",
|
||||
"values.insert(X(1), Pose2(1, 0, 0))\n",
|
||||
"\n",
|
||||
"# Evaluate error (example with BetweenFactor)\n",
|
||||
"error1 = between_factor.error(values)\n",
|
||||
"print(f\"Error at ground truth: {error1}\")\n",
|
||||
"\n",
|
||||
"# Change a value and recalculate error\n",
|
||||
"values.update(X(1), Pose2(0, 0, 0))\n",
|
||||
"error2 = between_factor.error(values)\n",
|
||||
"print(f\"Error with incorrect x1: {error2:.1f}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# Factor"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"`gtsam.Factor` is the abstract base class for all factors in GTSAM, including nonlinear factors, Gaussian factors, discrete factors, and conditionals. It defines the basic interface common to all factors, primarily centered around the set of variables (keys) the factor involves.\n",
|
||||
"\n",
|
||||
"You typically do not instantiate `gtsam.Factor` directly but rather work with its derived classes like `gtsam.NonlinearFactor`, `gtsam.JacobianFactor`, `gtsam.DiscreteFactor`, etc.\n",
|
||||
"\n",
|
||||
"The `error` function of a factor is typically related to a probability or likelihood $P(X)$ or $\\phi(X)$ via the negative log-likelihood:\n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"\\text{error}(X) = - \\log \\phi(X) + K\n",
|
||||
"$$\n",
|
||||
"or equivalently:\n",
|
||||
"$$\n",
|
||||
"\\phi(X) \\propto \\exp(-\\text{error}(X))\n",
|
||||
"$$\n",
|
||||
"where $X$ are the variables involved in the factor, $\\phi(X)$ is the potential function (proportional to probability or likelihood), and $K$ is some constant. Minimizing the error corresponds to maximizing the probability/likelihood represented by the factor."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Factor.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "factor_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"id": "factor_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam.utils.test_case import GtsamTestCase\n",
|
||||
"\n",
|
||||
"# We need a concrete factor type for demonstration\n",
|
||||
"from gtsam import PriorFactorPose2, BetweenFactorPose2, Pose2, Point3\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_interface_md"
|
||||
},
|
||||
"source": [
|
||||
"## Basic Interface\n",
|
||||
"\n",
|
||||
"All factors provide methods to access the keys of the variables they involve."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "factor_keys_code",
|
||||
"outputId": "01234567-89ab-cdef-0123-456789abcdef"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Prior factor keys: [8646911284551352320] (x0)\n",
|
||||
"Prior factor size: 1\n",
|
||||
"Between factor keys: [8646911284551352320, 8646911284551352321] (x0, x1)\n",
|
||||
"Between factor size: 2\n",
|
||||
"Is prior factor empty? False\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"noise_model = gtsam.noiseModel.Diagonal.Sigmas(Point3(0.1, 0.1, 0.05))\n",
|
||||
"\n",
|
||||
"# Create some concrete factors\n",
|
||||
"prior_factor = PriorFactorPose2(X(0), Pose2(0, 0, 0), noise_model)\n",
|
||||
"between_factor = BetweenFactorPose2(X(0), X(1), Pose2(1, 0, 0), noise_model)\n",
|
||||
"\n",
|
||||
"# Access keys (methods inherited from gtsam.Factor)\n",
|
||||
"prior_keys = prior_factor.keys()\n",
|
||||
"print(f\"Prior factor keys: {prior_keys} ({gtsam.DefaultKeyFormatter(prior_keys[0])})\")\n",
|
||||
"print(f\"Prior factor size: {prior_factor.size()}\")\n",
|
||||
"\n",
|
||||
"between_keys = between_factor.keys()\n",
|
||||
"print(f\"Between factor keys: {between_keys} ({gtsam.DefaultKeyFormatter(between_keys[0])}, {gtsam.DefaultKeyFormatter(between_keys[1])})\")\n",
|
||||
"print(f\"Between factor size: {between_factor.size()}\")\n",
|
||||
"\n",
|
||||
"print(f\"Is prior factor empty? {prior_factor.empty()}\")\n",
|
||||
"\n",
|
||||
"# Factors can be printed\n",
|
||||
"# prior_factor.print(\"Prior Factor: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "factor_error_md"
|
||||
},
|
||||
"source": [
|
||||
"## Error Function\n",
|
||||
"\n",
|
||||
"A key method for many factor types (especially nonlinear and Gaussian) is `error(Values)`. This evaluates the negative log-likelihood of the factor given a specific assignment of variable values. For optimization, the goal is typically to find the `Values` that minimize the total error (sum of errors from all factors)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "factor_error_code",
|
||||
"outputId": "12345678-9abc-def0-1234-56789abcdef0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Error at ground truth: 0.0\n",
|
||||
"Error with incorrect x1: 50.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"values = gtsam.Values()\n",
|
||||
"values.insert(X(0), Pose2(0, 0, 0))\n",
|
||||
"values.insert(X(1), Pose2(1, 0, 0))\n",
|
||||
"\n",
|
||||
"# Evaluate error (example with BetweenFactor)\n",
|
||||
"error1 = between_factor.error(values)\n",
|
||||
"print(f\"Error at ground truth: {error1}\")\n",
|
||||
"\n",
|
||||
"# Change a value and recalculate error\n",
|
||||
"values.update(X(1), Pose2(0, 0, 0))\n",
|
||||
"error2 = between_factor.error(values)\n",
|
||||
"print(f\"Error with incorrect x1: {error2:.1f}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,298 +1,374 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# FactorGraph"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `FactorGraph` represents a factor graph, a bipartite graph connecting variables and factors. In GTSAM, the `FactorGraph` class (and its templated instantiations like `GaussianFactorGraph`, `NonlinearFactorGraph`, etc.) primarily stores a collection of factors.\n",
|
||||
"\n",
|
||||
"This class serves as the base for different types of factor graphs. You typically work with specific instantiations like `gtsam.GaussianFactorGraph` or `gtsam.NonlinearFactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/FactorGraph.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# Example uses NonlinearFactorGraph, but concepts apply to others\n",
|
||||
"from gtsam import NonlinearFactorGraph, PriorFactorPose2, BetweenFactorPose2, Pose2, Point3\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization and Adding Factors\n",
|
||||
"\n",
|
||||
"A `FactorGraph` is typically created empty and factors are added individually or from containers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "23456789-abcd-ef01-2345-6789abcdef01"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Graph size after adding factors: 3\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"graph = NonlinearFactorGraph()\n",
|
||||
"\n",
|
||||
"# Define noise models\n",
|
||||
"prior_noise = gtsam.noiseModel.Diagonal.Sigmas(Point3(0.1, 0.1, 0.05))\n",
|
||||
"odometry_noise = gtsam.noiseModel.Diagonal.Sigmas(Point3(0.2, 0.2, 0.1))\n",
|
||||
"\n",
|
||||
"# Create factors\n",
|
||||
"factor1 = PriorFactorPose2(X(0), Pose2(0, 0, 0), prior_noise)\n",
|
||||
"factor2 = BetweenFactorPose2(X(0), X(1), Pose2(1, 0, 0), odometry_noise)\n",
|
||||
"factor3 = BetweenFactorPose2(X(1), X(2), Pose2(1, 0, 0), odometry_noise)\n",
|
||||
"\n",
|
||||
"# Add factors to the graph\n",
|
||||
"graph.add(factor1) # add is synonym for push_back\n",
|
||||
"graph.push_back(factor2)\n",
|
||||
"\n",
|
||||
"# Can also add using += or ,\n",
|
||||
"graph += factor3\n",
|
||||
"# graph += factor2, factor3 # Chaining also works\n",
|
||||
"\n",
|
||||
"print(f\"Graph size after adding factors: {graph.size()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_access_md"
|
||||
},
|
||||
"source": [
|
||||
"## Accessing Factors and Properties"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "3456789a-bcde-f012-3456-789abcdef012"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Is graph empty? False\n",
|
||||
"Number of factors (size): 3\n",
|
||||
"Number of non-null factors (nrFactors): 3\n",
|
||||
"Factor at index 1: \n",
|
||||
"BetweenFactor on 8070450532247928832 8070450532247928833\n",
|
||||
" measured: (1, 0, 0)\n",
|
||||
" noise model: diagonal sigmas [0.2; 0.2; 0.1]\n",
|
||||
"\n",
|
||||
"Keys involved in the graph: {8070450532247928832, 8070450532247928833, 8070450532247928834}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Is graph empty? {graph.empty()}\")\n",
|
||||
"print(f\"Number of factors (size): {graph.size()}\")\n",
|
||||
"print(f\"Number of non-null factors (nrFactors): {graph.nrFactors()}\") # Useful if factors were removed\n",
|
||||
"\n",
|
||||
"# Access factor by index\n",
|
||||
"retrieved_factor = graph.at(1)\n",
|
||||
"print(\"Factor at index 1: \")\n",
|
||||
"retrieved_factor.print()\n",
|
||||
"\n",
|
||||
"# Get all unique keys involved in the graph\n",
|
||||
"all_keys = graph.keys() # Returns a KeySet\n",
|
||||
"print(f\"Keys involved in the graph: {all_keys}\")\n",
|
||||
"\n",
|
||||
"# Iterate through factors\n",
|
||||
"# for i, factor in enumerate(graph):\n",
|
||||
"# if factor:\n",
|
||||
"# print(f\"Factor {i} keys: {factor.keys()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_error_md"
|
||||
},
|
||||
"source": [
|
||||
"## Graph Error\n",
|
||||
"\n",
|
||||
"The `error(Values)` method calculates the total error of the graph for a given assignment of variable values. This is the sum of the errors from each individual factor."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_error_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "456789ab-cdef-0123-4567-89abcdef0123"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Total graph error at ground truth: 0.0\n",
|
||||
"Total graph error with incorrect x2: 50.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"values = gtsam.Values()\n",
|
||||
"values.insert(X(0), Pose2(0, 0, 0))\n",
|
||||
"values.insert(X(1), Pose2(1, 0, 0))\n",
|
||||
"values.insert(X(2), Pose2(2, 0, 0))\n",
|
||||
"\n",
|
||||
"total_error1 = graph.error(values)\n",
|
||||
"print(f\"Total graph error at ground truth: {total_error1}\")\n",
|
||||
"\n",
|
||||
"# Introduce an error\n",
|
||||
"values.update(X(2), Pose2(1, 0, 0))\n",
|
||||
"total_error2 = graph.error(values)\n",
|
||||
"print(f\"Total graph error with incorrect x2: {total_error2:.1f}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_viz_md"
|
||||
},
|
||||
"source": [
|
||||
"## Graph Visualization\n",
|
||||
"\n",
|
||||
"Factor graphs can be visualized using Graphviz via the `dot()` method."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_dot_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "56789abc-def0-1234-5678-9abcdef01234"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"graph {\n",
|
||||
" size=\"5,5\";\n",
|
||||
"\n",
|
||||
" var8070450532247928832[label=\"x0\"];\n",
|
||||
" var8070450532247928833[label=\"x1\"];\n",
|
||||
" var8070450532247928834[label=\"x2\"];\n",
|
||||
"\n",
|
||||
" factor0[label=\"\", shape=point];\n",
|
||||
" var8070450532247928832--factor0;\n",
|
||||
" factor1[label=\"\", shape=point];\n",
|
||||
" var8070450532247928832--factor1;\n",
|
||||
" var8070450532247928833--factor1;\n",
|
||||
" factor2[label=\"\", shape=point];\n",
|
||||
" var8070450532247928833--factor2;\n",
|
||||
" var8070450532247928834--factor2;\n",
|
||||
"}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dot_string = graph.dot()\n",
|
||||
"print(dot_string)\n",
|
||||
"\n",
|
||||
"# To render, save dot_string to a file (e.g., graph.dot) and run:\n",
|
||||
"# dot -Tpng graph.dot -o graph.png\n",
|
||||
"# Or use a Python library like graphviz\n",
|
||||
"# import graphviz\n",
|
||||
"# graphviz.Source(dot_string)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_elim_md"
|
||||
},
|
||||
"source": [
|
||||
"## Elimination\n",
|
||||
"\n",
|
||||
"A key purpose of factor graphs is inference via variable elimination. `FactorGraph` itself doesn't perform elimination, but its derived classes (like `GaussianFactorGraph`, `SymbolicFactorGraph`) inherit `eliminateSequential` and `eliminateMultifrontal` methods from `EliminateableFactorGraph`."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# FactorGraph"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `FactorGraph` represents a factor graph, a bipartite graph connecting variables and factors. In GTSAM, the `FactorGraph` class (and its templated instantiations like `GaussianFactorGraph`, `NonlinearFactorGraph`, etc.) primarily stores a collection of factors.\n",
|
||||
"\n",
|
||||
"This class serves as the base for different types of factor graphs. You typically work with specific instantiations like `gtsam.GaussianFactorGraph` or `gtsam.NonlinearFactorGraph`.\n",
|
||||
"\n",
|
||||
"The total probability $P(X)$ represented by a factor graph is proportional to the product of its individual factor potentials $\\phi_i$:\n",
|
||||
"$$\n",
|
||||
"P(X) \\propto \\prod_i \\phi_i(X_i)\n",
|
||||
"$$\n",
|
||||
"where $X_i$ are the variables involved in factor $i$. In terms of error (negative log-likelihood):\n",
|
||||
"$$\n",
|
||||
"P(X) \\propto \\exp\\left(-\\sum_i \\text{error}_i(X_i)\\right)\n",
|
||||
"$$\n",
|
||||
"The total error for the graph given an assignment $X$ is the sum of the errors of the individual factors:\n",
|
||||
"$$\n",
|
||||
"\\text{error}(X) = \\sum_i \\text{error}_i(X_i)\n",
|
||||
"$$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/FactorGraph.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fg_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "fg_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# Example uses NonlinearFactorGraph, but concepts apply to others\n",
|
||||
"from gtsam import NonlinearFactorGraph, PriorFactorPose2, BetweenFactorPose2, Pose2, Point3\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization and Adding Factors\n",
|
||||
"\n",
|
||||
"A `FactorGraph` is typically created empty and factors are added individually or from containers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "fg_create_code",
|
||||
"outputId": "23456789-abcd-ef01-2345-6789abcdef01"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Graph size after adding factors: 2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"graph = NonlinearFactorGraph()\n",
|
||||
"\n",
|
||||
"# Define noise models\n",
|
||||
"prior_noise = gtsam.noiseModel.Diagonal.Sigmas(Point3(0.1, 0.1, 0.05))\n",
|
||||
"odometry_noise = gtsam.noiseModel.Diagonal.Sigmas(Point3(0.2, 0.2, 0.1))\n",
|
||||
"\n",
|
||||
"# Create factors\n",
|
||||
"factor1 = PriorFactorPose2(X(0), Pose2(0, 0, 0), prior_noise)\n",
|
||||
"factor2 = BetweenFactorPose2(X(0), X(1), Pose2(1, 0, 0), odometry_noise)\n",
|
||||
"factor3 = BetweenFactorPose2(X(1), X(2), Pose2(1, 0, 0), odometry_noise)\n",
|
||||
"\n",
|
||||
"# Add factors to the graph\n",
|
||||
"graph.add(factor1) # add is synonym for push_back\n",
|
||||
"graph.push_back(factor2)\n",
|
||||
"\n",
|
||||
"print(f\"Graph size after adding factors: {graph.size()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_access_md"
|
||||
},
|
||||
"source": [
|
||||
"## Accessing Factors and Properties"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "fg_access_code",
|
||||
"outputId": "3456789a-bcde-f012-3456-789abcdef012"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Is graph empty? False\n",
|
||||
"Number of factors (size): 2\n",
|
||||
"Number of non-null factors (nrFactors): 2\n",
|
||||
"Factor at index 1: \n",
|
||||
"BetweenFactor(x0,x1)\n",
|
||||
" measured: (1, 0, 0)\n",
|
||||
" noise model: diagonal sigmas [0.2; 0.2; 0.1];\n",
|
||||
"Keys involved in the graph: x0x1\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Is graph empty? {graph.empty()}\")\n",
|
||||
"print(f\"Number of factors (size): {graph.size()}\")\n",
|
||||
"print(f\"Number of non-null factors (nrFactors): {graph.nrFactors()}\") # Useful if factors were removed\n",
|
||||
"\n",
|
||||
"# Access factor by index\n",
|
||||
"retrieved_factor = graph.at(1)\n",
|
||||
"print(\"Factor at index 1: \")\n",
|
||||
"retrieved_factor.print()\n",
|
||||
"\n",
|
||||
"# Get all unique keys involved in the graph\n",
|
||||
"all_keys = graph.keys() # Returns a KeySet\n",
|
||||
"print(f\"Keys involved in the graph: {all_keys}\")\n",
|
||||
"\n",
|
||||
"# Iterate through factors\n",
|
||||
"# for i, factor in enumerate(graph):\n",
|
||||
"# if factor:\n",
|
||||
"# print(f\"Factor {i} keys: {factor.keys()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_error_md"
|
||||
},
|
||||
"source": [
|
||||
"## Graph Error\n",
|
||||
"\n",
|
||||
"The `error(Values)` method calculates the total error of the graph for a given assignment of variable values. This is the sum of the errors from each individual factor."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "fg_error_code",
|
||||
"outputId": "456789ab-cdef-0123-4567-89abcdef0123"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Total graph error at ground truth: 0.0\n",
|
||||
"Total graph error with incorrect x2: 0.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"values = gtsam.Values()\n",
|
||||
"values.insert(X(0), Pose2(0, 0, 0))\n",
|
||||
"values.insert(X(1), Pose2(1, 0, 0))\n",
|
||||
"values.insert(X(2), Pose2(2, 0, 0))\n",
|
||||
"\n",
|
||||
"total_error1 = graph.error(values)\n",
|
||||
"print(f\"Total graph error at ground truth: {total_error1}\")\n",
|
||||
"\n",
|
||||
"# Introduce an error\n",
|
||||
"values.update(X(2), Pose2(1, 0, 0))\n",
|
||||
"total_error2 = graph.error(values)\n",
|
||||
"print(f\"Total graph error with incorrect x2: {total_error2:.1f}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_viz_md"
|
||||
},
|
||||
"source": [
|
||||
"## Graph Visualization\n",
|
||||
"\n",
|
||||
"Factor graphs can be visualized using Graphviz via the `dot()` method."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "fg_dot_code",
|
||||
"outputId": "56789abc-def0-1234-5678-9abcdef01234"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"graph {\n",
|
||||
" size=\"5,5\";\n",
|
||||
"\n",
|
||||
" var8646911284551352320[label=\"x0\", pos=\"0,0!\"];\n",
|
||||
" var8646911284551352321[label=\"x1\", pos=\"0,1!\"];\n",
|
||||
"\n",
|
||||
" factor0[label=\"\", shape=point];\n",
|
||||
" var8646911284551352320--factor0;\n",
|
||||
" factor1[label=\"\", shape=point];\n",
|
||||
" var8646911284551352320--factor1;\n",
|
||||
" var8646911284551352321--factor1;\n",
|
||||
"}\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/svg+xml": [
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
|
||||
" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
|
||||
"<!-- Generated by graphviz version 2.50.0 (0)\n",
|
||||
" -->\n",
|
||||
"<!-- Pages: 1 -->\n",
|
||||
"<svg width=\"134pt\" height=\"84pt\"\n",
|
||||
" viewBox=\"0.00 0.00 134.00 83.60\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
|
||||
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 79.6)\">\n",
|
||||
"<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-79.6 130,-79.6 130,4 -4,4\"/>\n",
|
||||
"<!-- var8646911284551352320 -->\n",
|
||||
"<g id=\"node1\" class=\"node\">\n",
|
||||
"<title>var8646911284551352320</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"27\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x0</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor0 -->\n",
|
||||
"<g id=\"node3\" class=\"node\">\n",
|
||||
"<title>factor0</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"27\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352320--factor0 -->\n",
|
||||
"<g id=\"edge1\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352320--factor0</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M27,-39.58C27,-25.79 27,-7.97 27,-3.73\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- factor1 -->\n",
|
||||
"<g id=\"node4\" class=\"node\">\n",
|
||||
"<title>factor1</title>\n",
|
||||
"<ellipse fill=\"black\" stroke=\"black\" cx=\"74\" cy=\"-1.8\" rx=\"1.8\" ry=\"1.8\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352320--factor1 -->\n",
|
||||
"<g id=\"edge2\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352320--factor1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M40.09,-41.61C52.26,-27.68 69.09,-8.41 73.11,-3.81\"/>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321 -->\n",
|
||||
"<g id=\"node2\" class=\"node\">\n",
|
||||
"<title>var8646911284551352321</title>\n",
|
||||
"<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-57.6\" rx=\"27\" ry=\"18\"/>\n",
|
||||
"<text text-anchor=\"middle\" x=\"99\" y=\"-53.9\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">x1</text>\n",
|
||||
"</g>\n",
|
||||
"<!-- var8646911284551352321--factor1 -->\n",
|
||||
"<g id=\"edge3\" class=\"edge\">\n",
|
||||
"<title>var8646911284551352321--factor1</title>\n",
|
||||
"<path fill=\"none\" stroke=\"black\" d=\"M91.36,-40.17C84.93,-26.32 76.46,-8.1 74.44,-3.76\"/>\n",
|
||||
"</g>\n",
|
||||
"</g>\n",
|
||||
"</svg>\n"
|
||||
],
|
||||
"text/plain": [
|
||||
"<graphviz.sources.Source at 0x1c24ffedfd0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"dot_string = graph.dot(values)\n",
|
||||
"print(dot_string)\n",
|
||||
"\n",
|
||||
"# To render, save dot_string to a file (e.g., graph.dot) and run:\n",
|
||||
"# dot -Tpng graph.dot -o graph.png\n",
|
||||
"# Or use a Python library like graphviz\n",
|
||||
"import graphviz\n",
|
||||
"graphviz.Source(dot_string)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fg_elim_md"
|
||||
},
|
||||
"source": [
|
||||
"## Elimination\n",
|
||||
"\n",
|
||||
"A key purpose of factor graphs is inference via variable elimination. `FactorGraph` itself doesn't perform elimination, but its derived classes (like `GaussianFactorGraph`, `SymbolicFactorGraph`) inherit `eliminateSequential` and `eliminateMultifrontal` methods from `EliminateableFactorGraph`."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,276 +1,253 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# ISAM"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"`gtsam.ISAM` (Incremental Smoothing and Mapping) is a class that inherits from `BayesTree` and adds an `update` method. This method allows for efficient incremental updates to the solution when new factors (e.g., new measurements) are added to the problem.\n",
|
||||
"\n",
|
||||
"Instead of re-eliminating the entire factor graph from scratch, iSAM identifies the part of the Bayes tree affected by the new factors, removes that part, and re-eliminates only the necessary variables, merging the results back into the existing tree.\n",
|
||||
"\n",
|
||||
"Like `BayesTree`, it's templated (e.g., `GaussianISAM` which inherits from `GaussianBayesTree`). For practical applications requiring incremental updates, `ISAM2` is often preferred due to further optimizations like fluid relinearization and support for variable removal, but `ISAM` demonstrates the core incremental update concept based on the Bayes tree."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/ISAM.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "isam_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "isam_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# Use Gaussian variants for demonstration\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianISAM, GaussianBayesTree\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"An `ISAM` object can be created empty or initialized from an existing `BayesTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "isam_init_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "89abcdef-0123-4567-89ab-cdef01234567"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Initial ISAM (empty):\n",
|
||||
": cliques: 0, variables: 0\n",
|
||||
"\n",
|
||||
"Initial BayesTree:\n",
|
||||
": cliques: 1, variables: 1\n",
|
||||
"Root(s):\n",
|
||||
"Conditional density P(x0) = P(x0) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x0): GaussianConditional( P(x0) = dx0 - d), d = [ 0 ], sigmas = [ 1 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"ISAM from BayesTree:\n",
|
||||
": cliques: 1, variables: 1\n",
|
||||
"Root(s):\n",
|
||||
"Conditional density P(x0) = P(x0) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x0): GaussianConditional( P(x0) = dx0 - d), d = [ 0 ], sigmas = [ 1 ])\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create an empty ISAM object\n",
|
||||
"isam1 = GaussianISAM()\n",
|
||||
"print(\"Initial ISAM (empty):\")\n",
|
||||
"isam1.print()\n",
|
||||
"\n",
|
||||
"# Create from an existing Bayes Tree (e.g., from an initial batch solve)\n",
|
||||
"initial_graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"initial_graph.add(X(0), -np.eye(1), np.zeros(1), model) # Prior on x0\n",
|
||||
"\n",
|
||||
"initial_bayes_tree = initial_graph.eliminateMultifrontal(Ordering([X(0)]))\n",
|
||||
"print(\"Initial BayesTree:\")\n",
|
||||
"initial_bayes_tree.print()\n",
|
||||
"\n",
|
||||
"isam2 = GaussianISAM(initial_bayes_tree)\n",
|
||||
"print(\"ISAM from BayesTree:\")\n",
|
||||
"isam2.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_update_md"
|
||||
},
|
||||
"source": [
|
||||
"## Incremental Update\n",
|
||||
"\n",
|
||||
"The core functionality is the `update(newFactors)` method."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "isam_update_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "9abcdef0-1234-5678-9abc-def012345678"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"ISAM after first update (x0, x1):\n",
|
||||
": cliques: 2, variables: 2\n",
|
||||
"Root(s):\n",
|
||||
"Conditional density P(x0 | x1) = P(x0 | x1) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x0 | x1): GaussianConditional( P(x0 | x1) = dx0 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.707107 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(x1) = P(x1) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x1): GaussianConditional( P(x1) = dx1 - d), d = [ 0 ], sigmas = [ 0.707107 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"ISAM after second update (x0, x1, x2):\n",
|
||||
": cliques: 3, variables: 3\n",
|
||||
"Root(s):\n",
|
||||
"Conditional density P(x0 | x1) = P(x0 | x1) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x0 | x1): GaussianConditional( P(x0 | x1) = dx0 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.707107 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(x1 | x2) = P(x1 | x2) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x1 | x2): GaussianConditional( P(x1 | x2) = dx1 - R*dx2 - d), R = [ 0.666667 ], d = [ 0 ], sigmas = [ 0.816497 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(x2) = P(x2) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x2): GaussianConditional( P(x2) = dx2 - d), d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Start with the ISAM object containing the prior on x0\n",
|
||||
"isam = GaussianISAM(initial_bayes_tree)\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"\n",
|
||||
"# --- First Update ---\n",
|
||||
"new_factors1 = GaussianFactorGraph()\n",
|
||||
"new_factors1.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # x0 -> x1\n",
|
||||
"isam.update(new_factors1)\n",
|
||||
"\n",
|
||||
"print(\"ISAM after first update (x0, x1):\")\n",
|
||||
"isam.print()\n",
|
||||
"\n",
|
||||
"# --- Second Update ---\n",
|
||||
"new_factors2 = GaussianFactorGraph()\n",
|
||||
"new_factors2.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model) # x1 -> x2\n",
|
||||
"isam.update(new_factors2)\n",
|
||||
"\n",
|
||||
"print(\"\\nISAM after second update (x0, x1, x2):\")\n",
|
||||
"isam.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_solve_md"
|
||||
},
|
||||
"source": [
|
||||
"## Solution and Marginals\n",
|
||||
"\n",
|
||||
"Since `ISAM` inherits from `BayesTree`, you can use the same methods like `optimize()` and `marginalFactor()` after performing updates."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "isam_solve_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "abcdef01-2345-6789-abcd-ef0123456789"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Optimized Solution after updates:\n",
|
||||
"Values with 3 values:\n",
|
||||
"Value x0: [0.]\n",
|
||||
"Value x1: [0.]\n",
|
||||
"Value x2: [0.]\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Get the solution from the final ISAM state\n",
|
||||
"solution = isam.optimize()\n",
|
||||
"print(\"Optimized Solution after updates:\")\n",
|
||||
"solution.print()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# ISAM"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"`gtsam.ISAM` (Incremental Smoothing and Mapping) is a class that inherits from `BayesTree` and adds an `update` method. This method allows for efficient incremental updates to the solution when new factors (e.g., new measurements) are added to the problem.\n",
|
||||
"\n",
|
||||
"Instead of re-eliminating the entire factor graph from scratch, iSAM identifies the part of the Bayes tree affected by the new factors, removes that part, and re-eliminates only the necessary variables, merging the results back into the existing tree.\n",
|
||||
"\n",
|
||||
"Like `BayesTree`, it's templated (e.g., `GaussianISAM` which inherits from `GaussianBayesTree`). For practical applications requiring incremental updates, `ISAM2` is often preferred due to further optimizations like fluid relinearization and support for variable removal, but `ISAM` demonstrates the core incremental update concept based on the Bayes tree."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/ISAM.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "isam_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "isam_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# Use Gaussian variants for demonstration\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianISAM, GaussianBayesTree\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"An `ISAM` object can be created empty or initialized from an existing `BayesTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "isam_init_code",
|
||||
"outputId": "89abcdef-0123-4567-89ab-cdef01234567"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Initial BayesTree:\n",
|
||||
": cliques: 1, variables: 1\n",
|
||||
"- p(x0)\n",
|
||||
" R = [ 1 ]\n",
|
||||
" d = [ 0 ]\n",
|
||||
" mean: 1 elements\n",
|
||||
" x0: 0\n",
|
||||
" logNormalizationConstant: -0.918939\n",
|
||||
" No noise model\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "__init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.GaussianISAM()\n\nInvoked with: : cliques: 1, variables: 1\n- p(x0)\n R = [ 1 ]\n d = [ 0 ]\n mean: 1 elements\n x0: 0\n logNormalizationConstant: -0.918939\n No noise model\n",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[5], line 13\u001b[0m\n\u001b[0;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInitial BayesTree:\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 11\u001b[0m initial_bayes_tree\u001b[38;5;241m.\u001b[39mprint()\n\u001b[1;32m---> 13\u001b[0m isam2 \u001b[38;5;241m=\u001b[39m \u001b[43mGaussianISAM\u001b[49m\u001b[43m(\u001b[49m\u001b[43minitial_bayes_tree\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 14\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mISAM from BayesTree:\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 15\u001b[0m isam2\u001b[38;5;241m.\u001b[39mprint()\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: __init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.GaussianISAM()\n\nInvoked with: : cliques: 1, variables: 1\n- p(x0)\n R = [ 1 ]\n d = [ 0 ]\n mean: 1 elements\n x0: 0\n logNormalizationConstant: -0.918939\n No noise model\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create an empty ISAM object\n",
|
||||
"isam1 = GaussianISAM()\n",
|
||||
"\n",
|
||||
"# Create from an existing Bayes Tree (e.g., from an initial batch solve)\n",
|
||||
"initial_graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"initial_graph.add(X(0), -np.eye(1), np.zeros(1), model) # Prior on x0\n",
|
||||
"\n",
|
||||
"initial_bayes_tree = initial_graph.eliminateMultifrontal(Ordering([X(0)]))\n",
|
||||
"print(\"Initial BayesTree:\")\n",
|
||||
"initial_bayes_tree.print()\n",
|
||||
"\n",
|
||||
"isam2 = GaussianISAM(initial_bayes_tree)\n",
|
||||
"print(\"ISAM from BayesTree:\")\n",
|
||||
"isam2.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_update_md"
|
||||
},
|
||||
"source": [
|
||||
"## Incremental Update\n",
|
||||
"\n",
|
||||
"The core functionality is the `update(newFactors)` method."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "isam_update_code",
|
||||
"outputId": "9abcdef0-1234-5678-9abc-def012345678"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "__init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.GaussianISAM()\n\nInvoked with: : cliques: 1, variables: 1\n- p(x0)\n R = [ 1 ]\n d = [ 0 ]\n mean: 1 elements\n x0: 0\n logNormalizationConstant: -0.918939\n No noise model\n",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[6], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# Start with the ISAM object containing the prior on x0\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m isam \u001b[38;5;241m=\u001b[39m \u001b[43mGaussianISAM\u001b[49m\u001b[43m(\u001b[49m\u001b[43minitial_bayes_tree\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3\u001b[0m model \u001b[38;5;241m=\u001b[39m gtsam\u001b[38;5;241m.\u001b[39mnoiseModel\u001b[38;5;241m.\u001b[39mIsotropic\u001b[38;5;241m.\u001b[39mSigma(\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m1.0\u001b[39m)\n\u001b[0;32m 5\u001b[0m \u001b[38;5;66;03m# --- First Update ---\u001b[39;00m\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: __init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.GaussianISAM()\n\nInvoked with: : cliques: 1, variables: 1\n- p(x0)\n R = [ 1 ]\n d = [ 0 ]\n mean: 1 elements\n x0: 0\n logNormalizationConstant: -0.918939\n No noise model\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Start with the ISAM object containing the prior on x0\n",
|
||||
"isam = GaussianISAM(initial_bayes_tree)\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"\n",
|
||||
"# --- First Update ---\n",
|
||||
"new_factors1 = GaussianFactorGraph()\n",
|
||||
"new_factors1.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model) # x0 -> x1\n",
|
||||
"isam.update(new_factors1)\n",
|
||||
"\n",
|
||||
"print(\"ISAM after first update (x0, x1):\")\n",
|
||||
"isam.print()\n",
|
||||
"\n",
|
||||
"# --- Second Update ---\n",
|
||||
"new_factors2 = GaussianFactorGraph()\n",
|
||||
"new_factors2.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model) # x1 -> x2\n",
|
||||
"isam.update(new_factors2)\n",
|
||||
"\n",
|
||||
"print(\"\\nISAM after second update (x0, x1, x2):\")\n",
|
||||
"isam.print()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "isam_solve_md"
|
||||
},
|
||||
"source": [
|
||||
"## Solution and Marginals\n",
|
||||
"\n",
|
||||
"Since `ISAM` inherits from `BayesTree`, you can use the same methods like `optimize()` and `marginalFactor()` after performing updates."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "isam_solve_code",
|
||||
"outputId": "abcdef01-2345-6789-abcd-ef0123456789"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "NameError",
|
||||
"evalue": "name 'isam' is not defined",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[7], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# Get the solution from the final ISAM state\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m solution \u001b[38;5;241m=\u001b[39m \u001b[43misam\u001b[49m\u001b[38;5;241m.\u001b[39moptimize()\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mOptimized Solution after updates:\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 4\u001b[0m solution\u001b[38;5;241m.\u001b[39mprint()\n",
|
||||
"\u001b[1;31mNameError\u001b[0m: name 'isam' is not defined"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Get the solution from the final ISAM state\n",
|
||||
"solution = isam.optimize()\n",
|
||||
"print(\"Optimized Solution after updates:\")\n",
|
||||
"solution.print()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,160 +1,166 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# JunctionTree"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `JunctionTree` is an intermediate data structure used in GTSAM's multifrontal variable elimination. It is a `ClusterTree` where each node (cluster) corresponds to a clique in the chordal graph formed during elimination.\n",
|
||||
"\n",
|
||||
"Key differences from related structures:\n",
|
||||
"* **vs. EliminationTree:** Junction tree nodes can represent the elimination of multiple variables simultaneously (a 'frontal' set), whereas elimination tree nodes typically represent single variable eliminations.\n",
|
||||
"* **vs. BayesTree:** A JunctionTree node contains the original factors associated with the variables being eliminated in that clique. A BayesTree node contains the *result* of eliminating those factors (i.e., a conditional density $P(\text{Frontals} | \text{Separator})$).\n",
|
||||
"\n",
|
||||
"Like `EliminationTree`, direct manipulation of `JunctionTree` objects in Python is uncommon. It's primarily an internal structure used by `eliminateMultifrontal` when producing a `BayesTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/JunctionTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jtree_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jtree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# JunctionTree is templated, need concrete types\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, GaussianJunctionTree, GaussianBayesTree\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a JunctionTree\n",
|
||||
"\n",
|
||||
"A `JunctionTree` is typically constructed from an `EliminationTree` as part of the multifrontal elimination process. The direct constructor might not be exposed in Python, as it's usually created internally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jtree_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "12345678-9abc-def0-1234-56789abcdef0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Resulting BayesTree (structure mirrors JunctionTree):\n",
|
||||
": cliques: 3, variables: 5\n",
|
||||
"Root(s):\n",
|
||||
"Conditional density P(l1, x1 | l2, x2) = P(l1 | x1) P(x1 | l2, x2) \n",
|
||||
" size: 2\n",
|
||||
" Conditional P(l1 | x1): GaussianConditional( P(l1 | x1) = dl1 - R*dx1 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n",
|
||||
" Conditional P(x1 | l2, x2): GaussianConditional( P(x1 | l2, x2) = dx1 - R1*dl2 - R2*dx2 - d), R1 = [ 0.333333 ], R2 = [ 0.333333 ], d = [ 0 ], sigmas = [ 0.745356 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(l2, x2 | x0) = P(l2 | x2) P(x2 | x0) \n",
|
||||
" size: 2\n",
|
||||
" Conditional P(l2 | x2): GaussianConditional( P(l2 | x2) = dl2 - R*dx2 - d), R = [ 0.5 ], d = [ 0 ], sigmas = [ 0.866025 ])\n",
|
||||
"\n",
|
||||
" Conditional P(x2 | x0): GaussianConditional( P(x2 | x0) = dx2 - R*dx0 - d), R = [ 0.2 ], d = [ 0 ], sigmas = [ 0.774597 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" Children:\n",
|
||||
" Conditional density P(x0) = P(x0) \n",
|
||||
" size: 1\n",
|
||||
" Conditional P(x0): GaussianConditional( P(x0) = dx0 - d), d = [ 0 ], sigmas = [ 0.894427 ])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a graph (same as BayesTree example)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"\n",
|
||||
"ordering = Ordering.Colamd(graph)\n",
|
||||
"\n",
|
||||
"# Perform multifrontal elimination, which uses a JunctionTree internally\n",
|
||||
"bayes_tree, remaining_graph = graph.eliminatePartialMultifrontal(ordering)\n",
|
||||
"\n",
|
||||
"# The resulting BayesTree reflects the structure of the intermediate JunctionTree\n",
|
||||
"print(\"Resulting BayesTree (structure mirrors JunctionTree):\")\n",
|
||||
"bayes_tree.print()\n",
|
||||
"\n",
|
||||
"# Accessing the JunctionTree directly isn't typical in Python workflows.\n",
|
||||
"# Its structure is implicitly captured by the BayesTree cliques."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# JunctionTree"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `JunctionTree` is an intermediate data structure used in GTSAM's multifrontal variable elimination. It is a `ClusterTree` where each node (cluster) corresponds to a clique in the chordal graph formed during elimination.\n",
|
||||
"\n",
|
||||
"Key differences from related structures:\n",
|
||||
"* **vs. EliminationTree:** Junction tree nodes can represent the elimination of multiple variables simultaneously (a 'frontal' set), whereas elimination tree nodes typically represent single variable eliminations.\n",
|
||||
"* **vs. BayesTree:** A JunctionTree node contains the original factors associated with the variables being eliminated in that clique. A BayesTree node contains the *result* of eliminating those factors (i.e., a conditional density $P(\text{Frontals} | \text{Separator})$).\n",
|
||||
"\n",
|
||||
"Like `EliminationTree`, direct manipulation of `JunctionTree` objects in Python is uncommon. It's primarily an internal structure used by `eliminateMultifrontal` when producing a `BayesTree`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/JunctionTree.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jtree_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"id": "jtree_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# JunctionTree is templated, need concrete types\n",
|
||||
"from gtsam import GaussianFactorGraph, Ordering, VariableIndex\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jtree_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a JunctionTree\n",
|
||||
"\n",
|
||||
"A `JunctionTree` is typically constructed from an `EliminationTree` as part of the multifrontal elimination process. The direct constructor might not be exposed in Python, as it's usually created internally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "jtree_create_code",
|
||||
"outputId": "12345678-9abc-def0-1234-56789abcdef0"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Resulting BayesTree (structure mirrors JunctionTree):\n",
|
||||
": cliques: 2, variables: 5\n",
|
||||
"- p(x1 l2 x2 )\n",
|
||||
" R = [ 1.61245 -0.620174 -0.620174 ]\n",
|
||||
" [ 0 1.27098 -1.08941 ]\n",
|
||||
" [ 0 0 0.654654 ]\n",
|
||||
" d = [ 0 0 0 ]\n",
|
||||
" mean: 3 elements\n",
|
||||
" l2: 0\n",
|
||||
" x1: 0\n",
|
||||
" x2: 0\n",
|
||||
" logNormalizationConstant: -2.46292\n",
|
||||
" No noise model\n",
|
||||
"| - p(l1 x0 | x1)\n",
|
||||
" R = [ 1.41421 -0.707107 ]\n",
|
||||
" [ 0 1.58114 ]\n",
|
||||
" S[x1] = [ -0.707107 ]\n",
|
||||
" [ -0.948683 ]\n",
|
||||
" d = [ 0 0 ]\n",
|
||||
" logNormalizationConstant: -1.03316\n",
|
||||
" No noise model\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a graph (same as BayesTree example)\n",
|
||||
"graph = GaussianFactorGraph()\n",
|
||||
"model = gtsam.noiseModel.Isotropic.Sigma(1, 1.0)\n",
|
||||
"graph.add(X(0), -np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(0), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(X(1), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(0), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(1), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(1), np.eye(1), np.zeros(1), model)\n",
|
||||
"graph.add(L(2), -np.eye(1), X(2), np.eye(1), np.zeros(1), model)\n",
|
||||
"\n",
|
||||
"ordering = Ordering.Colamd(VariableIndex(graph))\n",
|
||||
"\n",
|
||||
"# Perform multifrontal elimination, which uses a JunctionTree internally\n",
|
||||
"bayes_tree, remaining_graph = graph.eliminatePartialMultifrontal(ordering)\n",
|
||||
"\n",
|
||||
"# The resulting BayesTree reflects the structure of the intermediate JunctionTree\n",
|
||||
"print(\"Resulting BayesTree (structure mirrors JunctionTree):\")\n",
|
||||
"bayes_tree.print()\n",
|
||||
"\n",
|
||||
"# Accessing the JunctionTree directly isn't typical in Python workflows.\n",
|
||||
"# Its structure is implicitly captured by the BayesTree cliques."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,202 +1,218 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# Key"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `Key` in GTSAM is simply a `typedef` for `std::uint64_t`. It serves as a unique identifier for variables within a factor graph or values within a `Values` container. While you can use raw integer keys, GTSAM provides helper classes like `Symbol` and `LabeledSymbol` to create semantically meaningful keys that encode type and index information within the 64-bit integer."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Key.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "key_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "key_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import Symbol, LabeledSymbol\n",
|
||||
"import numpy as np"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_usage_md"
|
||||
},
|
||||
"source": [
|
||||
"## Basic Usage\n",
|
||||
"\n",
|
||||
"Keys are typically created using `Symbol` or `LabeledSymbol` and then implicitly or explicitly cast to the `Key` type (integer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "key_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol Key (x0): 8070450532247928832\n",
|
||||
"Type: <class 'int'>\n",
|
||||
"LabeledSymbol Key (aB1): 6988956933745737729\n",
|
||||
"Type: <class 'int'>\n",
|
||||
"Plain Integer Key: 12345\n",
|
||||
"Type: <class 'int'>\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sym = Symbol('x', 0)\n",
|
||||
"key_from_symbol = sym.key() # Or just 'sym' where a Key is expected\n",
|
||||
"print(f\"Symbol Key (x0): {key_from_symbol}\")\n",
|
||||
"print(f\"Type: {type(key_from_symbol)}\")\n",
|
||||
"\n",
|
||||
"lsym = LabeledSymbol('a', 'B', 1)\n",
|
||||
"key_from_labeled_symbol = lsym.key()\n",
|
||||
"print(f\"LabeledSymbol Key (aB1): {key_from_labeled_symbol}\")\n",
|
||||
"print(f\"Type: {type(key_from_labeled_symbol)}\")\n",
|
||||
"\n",
|
||||
"# You can also use plain integers, but it's less descriptive\n",
|
||||
"plain_key = 12345\n",
|
||||
"print(f\"Plain Integer Key: {plain_key}\")\n",
|
||||
"print(f\"Type: {type(plain_key)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_formatter_md"
|
||||
},
|
||||
"source": [
|
||||
"## Key Formatting\n",
|
||||
"\n",
|
||||
"When printing GTSAM objects that contain keys (like Factor Graphs or Values), you can specify a `KeyFormatter` to control how keys are displayed. The default formatter tries to interpret keys as `Symbol`s. `MultiRobotKeyFormatter` also checks for `LabeledSymbol`s."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "key_format_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Default Formatter:\n",
|
||||
" Symbol Key: x0\n",
|
||||
" LabeledSymbol Key: 6988956933745737729 (Default doesn't know LabeledSymbol)\n",
|
||||
" Plain Key: 12345\n",
|
||||
"\n",
|
||||
"MultiRobot Formatter:\n",
|
||||
" Symbol Key: x0\n",
|
||||
" LabeledSymbol Key: aB1\n",
|
||||
" Plain Key: 12345\n",
|
||||
"\n",
|
||||
"Custom Formatter:\n",
|
||||
" Symbol Key: KEY[x0]\n",
|
||||
" LabeledSymbol Key: KEY[aB1]\n",
|
||||
" Plain Key: KEY[12345]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Default Formatter:\")\n",
|
||||
"print(f\" Symbol Key: {gtsam.DefaultKeyFormatter(key_from_symbol)}\")\n",
|
||||
"print(f\" LabeledSymbol Key: {gtsam.DefaultKeyFormatter(key_from_labeled_symbol)}\")\n",
|
||||
"print(f\" Plain Key: {gtsam.DefaultKeyFormatter(plain_key)}\")\n",
|
||||
"\n",
|
||||
"print(\"MultiRobot Formatter:\")\n",
|
||||
"print(f\" Symbol Key: {gtsam.MultiRobotKeyFormatter(key_from_symbol)}\")\n",
|
||||
"print(f\" LabeledSymbol Key: {gtsam.MultiRobotKeyFormatter(key_from_labeled_symbol)}\")\n",
|
||||
"print(f\" Plain Key: {gtsam.MultiRobotKeyFormatter(plain_key)}\")\n",
|
||||
"\n",
|
||||
"# Example of a custom formatter\n",
|
||||
"def my_formatter(key):\n",
|
||||
" # Try interpreting as LabeledSymbol, then Symbol, then default\n",
|
||||
" try:\n",
|
||||
" lsym = gtsam.LabeledSymbol(key)\n",
|
||||
" if lsym.label() != 0: # Check if it's likely a valid LabeledSymbol\n",
|
||||
" return f\"KEY[{lsym.string()}]\"\n",
|
||||
" except:\n",
|
||||
" pass\n",
|
||||
" try:\n",
|
||||
" sym = gtsam.Symbol(key)\n",
|
||||
" if sym.chr() != 0: # Check if it's likely a valid Symbol\n",
|
||||
" return f\"KEY[{sym.string()}]\"\n",
|
||||
" except:\n",
|
||||
" pass\n",
|
||||
" return f\"KEY[{key}]\"\n",
|
||||
"\n",
|
||||
"print(\"Custom Formatter:\")\n",
|
||||
"print(f\" Symbol Key: {my_formatter(key_from_symbol)}\")\n",
|
||||
"print(f\" LabeledSymbol Key: {my_formatter(key_from_labeled_symbol)}\")\n",
|
||||
"print(f\" Plain Key: {my_formatter(plain_key)}\")\n",
|
||||
"\n",
|
||||
"# KeyVectors, KeyLists, KeySets can also be printed using formatters\n",
|
||||
"key_vector = gtsam.KeyVector([key_from_symbol, key_from_labeled_symbol, plain_key])\n",
|
||||
"# key_vector.print(\"My Vector: \", my_formatter) # .print() method uses formatter directly"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# Key"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `Key` in GTSAM is simply a `typedef` for `std::uint64_t`. It serves as a unique identifier for variables within a factor graph or values within a `Values` container. While you can use raw integer keys, GTSAM provides helper classes like `Symbol` and `LabeledSymbol` to create semantically meaningful keys that encode type and index information within the 64-bit integer."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Key.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "key_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"id": "key_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import Symbol, LabeledSymbol\n",
|
||||
"import numpy as np"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_usage_md"
|
||||
},
|
||||
"source": [
|
||||
"## Basic Usage\n",
|
||||
"\n",
|
||||
"Keys are typically created using `Symbol` or `LabeledSymbol` and then implicitly or explicitly cast to the `Key` type (integer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "key_create_code",
|
||||
"outputId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol Key (x0): 8646911284551352320\n",
|
||||
"Type: <class 'int'>\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "__init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'a', 'B', 1",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[3], line 6\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mSymbol Key (x0): \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkey_from_symbol\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mType: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(key_from_symbol)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m----> 6\u001b[0m lsym \u001b[38;5;241m=\u001b[39m \u001b[43mLabeledSymbol\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43ma\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mB\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 7\u001b[0m key_from_labeled_symbol \u001b[38;5;241m=\u001b[39m lsym\u001b[38;5;241m.\u001b[39mkey()\n\u001b[0;32m 8\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLabeledSymbol Key (aB1): \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkey_from_labeled_symbol\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: __init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'a', 'B', 1"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sym = Symbol('x', 0)\n",
|
||||
"key_from_symbol = sym.key() # Or just 'sym' where a Key is expected\n",
|
||||
"print(f\"Symbol Key (x0): {key_from_symbol}\")\n",
|
||||
"print(f\"Type: {type(key_from_symbol)}\")\n",
|
||||
"\n",
|
||||
"lsym = LabeledSymbol('a', 'B', 1)\n",
|
||||
"key_from_labeled_symbol = lsym.key()\n",
|
||||
"print(f\"LabeledSymbol Key (aB1): {key_from_labeled_symbol}\")\n",
|
||||
"print(f\"Type: {type(key_from_labeled_symbol)}\")\n",
|
||||
"\n",
|
||||
"# You can also use plain integers, but it's less descriptive\n",
|
||||
"plain_key = 12345\n",
|
||||
"print(f\"Plain Integer Key: {plain_key}\")\n",
|
||||
"print(f\"Type: {type(plain_key)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "key_formatter_md"
|
||||
},
|
||||
"source": [
|
||||
"## Key Formatting\n",
|
||||
"\n",
|
||||
"When printing GTSAM objects that contain keys (like Factor Graphs or Values), you can specify a `KeyFormatter` to control how keys are displayed. The default formatter tries to interpret keys as `Symbol`s. `MultiRobotKeyFormatter` also checks for `LabeledSymbol`s."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "key_format_code",
|
||||
"outputId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Default Formatter:\n",
|
||||
" Symbol Key: x0\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "NameError",
|
||||
"evalue": "name 'key_from_labeled_symbol' is not defined",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[4], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDefault Formatter:\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m Symbol Key: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mgtsam\u001b[38;5;241m.\u001b[39mDefaultKeyFormatter(key_from_symbol)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m----> 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m LabeledSymbol Key: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mgtsam\u001b[38;5;241m.\u001b[39mDefaultKeyFormatter(\u001b[43mkey_from_labeled_symbol\u001b[49m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m Plain Key: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mgtsam\u001b[38;5;241m.\u001b[39mDefaultKeyFormatter(plain_key)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMultiRobot Formatter:\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
|
||||
"\u001b[1;31mNameError\u001b[0m: name 'key_from_labeled_symbol' is not defined"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(\"Default Formatter:\")\n",
|
||||
"print(f\" Symbol Key: {gtsam.DefaultKeyFormatter(key_from_symbol)}\")\n",
|
||||
"print(f\" LabeledSymbol Key: {gtsam.DefaultKeyFormatter(key_from_labeled_symbol)}\")\n",
|
||||
"print(f\" Plain Key: {gtsam.DefaultKeyFormatter(plain_key)}\")\n",
|
||||
"\n",
|
||||
"print(\"MultiRobot Formatter:\")\n",
|
||||
"print(f\" Symbol Key: {gtsam.MultiRobotKeyFormatter(key_from_symbol)}\")\n",
|
||||
"print(f\" LabeledSymbol Key: {gtsam.MultiRobotKeyFormatter(key_from_labeled_symbol)}\")\n",
|
||||
"print(f\" Plain Key: {gtsam.MultiRobotKeyFormatter(plain_key)}\")\n",
|
||||
"\n",
|
||||
"# Example of a custom formatter\n",
|
||||
"def my_formatter(key):\n",
|
||||
" # Try interpreting as LabeledSymbol, then Symbol, then default\n",
|
||||
" try:\n",
|
||||
" lsym = gtsam.LabeledSymbol(key)\n",
|
||||
" if lsym.label() != 0: # Check if it's likely a valid LabeledSymbol\n",
|
||||
" return f\"KEY[{lsym.string()}]\"\n",
|
||||
" except:\n",
|
||||
" pass\n",
|
||||
" try:\n",
|
||||
" sym = gtsam.Symbol(key)\n",
|
||||
" if sym.chr() != 0: # Check if it's likely a valid Symbol\n",
|
||||
" return f\"KEY[{sym.string()}]\"\n",
|
||||
" except:\n",
|
||||
" pass\n",
|
||||
" return f\"KEY[{key}]\"\n",
|
||||
"\n",
|
||||
"print(\"Custom Formatter:\")\n",
|
||||
"print(f\" Symbol Key: {my_formatter(key_from_symbol)}\")\n",
|
||||
"print(f\" LabeledSymbol Key: {my_formatter(key_from_labeled_symbol)}\")\n",
|
||||
"print(f\" Plain Key: {my_formatter(plain_key)}\")\n",
|
||||
"\n",
|
||||
"# KeyVectors, KeyLists, KeySets can also be printed using formatters\n",
|
||||
"key_vector = gtsam.KeyVector([key_from_symbol, key_from_labeled_symbol, plain_key])\n",
|
||||
"# key_vector.print(\"My Vector: \", my_formatter) # .print() method uses formatter directly"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,200 +1,217 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# LabeledSymbol"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `LabeledSymbol` is a specialized version of `gtsam.Symbol` designed primarily for multi-robot applications or scenarios where an additional label is needed besides the type character and index. It encodes a type character (`unsigned char`), a label character (`unsigned char`), and an index (`uint64_t`) into a single 64-bit `gtsam.Key`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/LabeledSymbol.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lsymbol_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lsymbol_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import LabeledSymbol"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"A `LabeledSymbol` can be created by providing a type character, a label character, and an index. It can also be created by decoding an existing `gtsam.Key` (integer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lsymbol_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "f1a2b3c4-d5e6-7890-f1a2-bcdef1234567"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"LabeledSymbol from char/label/index: xA7\n",
|
||||
"LabeledSymbol from key 8107010787457335296: xA0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create LabeledSymbol 'x' from robot 'A' with index 7\n",
|
||||
"lsym1 = LabeledSymbol('x', 'A', 7)\n",
|
||||
"print(f\"LabeledSymbol from char/label/index: {lsym1.string()}\")\n",
|
||||
"\n",
|
||||
"# Get the underlying integer key\n",
|
||||
"key1 = lsym1.key()\n",
|
||||
"\n",
|
||||
"# Reconstruct LabeledSymbol from the key\n",
|
||||
"# Note: Decoding a key assumes it was encoded as a LabeledSymbol.\n",
|
||||
"# If you decode a standard Symbol key, the label might be garbage.\n",
|
||||
"x0_key = gtsam.Symbol('x', 0).key()\n",
|
||||
"lsym2 = LabeledSymbol(x0_key)\n",
|
||||
"print(f\"LabeledSymbol from key {x0_key}: {lsym2.string()}\") # Label might be non-printable or 0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Usage\n",
|
||||
"\n",
|
||||
"You can access the type character, label character, index, and underlying integer key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lsymbol_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "a2b3c4d5-e6f7-8901-a2b3-cdef12345678"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"LabeledSymbol: lB3\n",
|
||||
" Char (Type): l\n",
|
||||
" Label (Robot): B\n",
|
||||
" Index: 3\n",
|
||||
" Key: 7783740146724503555\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"robotB_landmark = LabeledSymbol('l', 'B', 3)\n",
|
||||
"\n",
|
||||
"print(f\"LabeledSymbol: {robotB_landmark.string()}\")\n",
|
||||
"print(f\" Char (Type): {robotB_landmark.chr()}\")\n",
|
||||
"print(f\" Label (Robot): {robotB_landmark.label()}\")\n",
|
||||
"print(f\" Index: {robotB_landmark.index()}\")\n",
|
||||
"print(f\" Key: {robotB_landmark.key()}\")\n",
|
||||
"\n",
|
||||
"# LabeledSymbols are often used directly where Keys are expected.\n",
|
||||
"# Use the MultiRobotKeyFormatter for printing these keys meaningfully.\n",
|
||||
"# e.g., graph.print(\"Graph: \\n\", gtsam.MultiRobotKeyFormatter)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_shorthand_md"
|
||||
},
|
||||
"source": [
|
||||
"## Shorthand Function\n",
|
||||
"\n",
|
||||
"GTSAM provides a convenient shorthand function `gtsam.mrsymbol(c, label, j)`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lsymbol_shorthand_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "b3c4d5e6-f7a8-9012-b3c4-def123456789"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"LabeledSymbol('p', 'C', 2).key() == gtsam.mrsymbol(ord('p'), ord('C'), 2): True\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Note: mrsymbol expects integer representations of chars (use ord())\n",
|
||||
"pc2_key = gtsam.mrsymbol(ord('p'), ord('C'), 2)\n",
|
||||
"\n",
|
||||
"print(f\"LabeledSymbol('p', 'C', 2).key() == gtsam.mrsymbol(ord('p'), ord('C'), 2): {LabeledSymbol('p', 'C', 2).key() == pc2_key}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# LabeledSymbol"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `LabeledSymbol` is a specialized version of `gtsam.Symbol` designed primarily for multi-robot applications or scenarios where an additional label is needed besides the type character and index. It encodes a type character (`unsigned char`), a label character (`unsigned char`), and an index (`uint64_t`) into a single 64-bit `gtsam.Key`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/LabeledSymbol.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lsymbol_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "lsymbol_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import LabeledSymbol"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"A `LabeledSymbol` can be created by providing a type character, a label character, and an index. It can also be created by decoding an existing `gtsam.Key` (integer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "lsymbol_create_code",
|
||||
"outputId": "f1a2b3c4-d5e6-7890-f1a2-bcdef1234567"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "__init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'x', 'A', 7",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[2], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# Create LabeledSymbol 'x' from robot 'A' with index 7\u001b[39;00m\n\u001b[1;32m----> 2\u001b[0m lsym1 \u001b[38;5;241m=\u001b[39m \u001b[43mLabeledSymbol\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mx\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mA\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m7\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLabeledSymbol from char/label/index: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mlsym1\u001b[38;5;241m.\u001b[39mstring()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 5\u001b[0m \u001b[38;5;66;03m# Get the underlying integer key\u001b[39;00m\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: __init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'x', 'A', 7"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create LabeledSymbol 'x' from robot 'A' with index 7\n",
|
||||
"lsym1 = LabeledSymbol('x', 'A', 7)\n",
|
||||
"print(f\"LabeledSymbol from char/label/index: {lsym1.string()}\")\n",
|
||||
"\n",
|
||||
"# Get the underlying integer key\n",
|
||||
"key1 = lsym1.key()\n",
|
||||
"\n",
|
||||
"# Reconstruct LabeledSymbol from the key\n",
|
||||
"# Note: Decoding a key assumes it was encoded as a LabeledSymbol.\n",
|
||||
"# If you decode a standard Symbol key, the label might be garbage.\n",
|
||||
"x0_key = gtsam.Symbol('x', 0).key()\n",
|
||||
"lsym2 = LabeledSymbol(x0_key)\n",
|
||||
"print(f\"LabeledSymbol from key {x0_key}: {lsym2.string()}\") # Label might be non-printable or 0"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Usage\n",
|
||||
"\n",
|
||||
"You can access the type character, label character, index, and underlying integer key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "lsymbol_access_code",
|
||||
"outputId": "a2b3c4d5-e6f7-8901-a2b3-cdef12345678"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "__init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'l', 'B', 3",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[3], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m robotB_landmark \u001b[38;5;241m=\u001b[39m \u001b[43mLabeledSymbol\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43ml\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mB\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLabeledSymbol: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mrobotB_landmark\u001b[38;5;241m.\u001b[39mstring()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m Char (Type): \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mrobotB_landmark\u001b[38;5;241m.\u001b[39mchr()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: __init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'l', 'B', 3"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"robotB_landmark = LabeledSymbol('l', 'B', 3)\n",
|
||||
"\n",
|
||||
"print(f\"LabeledSymbol: {robotB_landmark.string()}\")\n",
|
||||
"print(f\" Char (Type): {robotB_landmark.chr()}\")\n",
|
||||
"print(f\" Label (Robot): {robotB_landmark.label()}\")\n",
|
||||
"print(f\" Index: {robotB_landmark.index()}\")\n",
|
||||
"print(f\" Key: {robotB_landmark.key()}\")\n",
|
||||
"\n",
|
||||
"# LabeledSymbols are often used directly where Keys are expected.\n",
|
||||
"# Use the MultiRobotKeyFormatter for printing these keys meaningfully.\n",
|
||||
"# e.g., graph.print(\"Graph: \\n\", gtsam.MultiRobotKeyFormatter)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "lsymbol_shorthand_md"
|
||||
},
|
||||
"source": [
|
||||
"## Shorthand Function\n",
|
||||
"\n",
|
||||
"GTSAM provides a convenient shorthand function `gtsam.mrsymbol(c, label, j)`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "lsymbol_shorthand_code",
|
||||
"outputId": "b3c4d5e6-f7a8-9012-b3c4-def123456789"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "__init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'p', 'C', 2",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[4], line 4\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m# Note: mrsymbol expects integer representations of chars (use ord())\u001b[39;00m\n\u001b[0;32m 2\u001b[0m pc2_key \u001b[38;5;241m=\u001b[39m gtsam\u001b[38;5;241m.\u001b[39mmrsymbol(\u001b[38;5;28mord\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mp\u001b[39m\u001b[38;5;124m'\u001b[39m), \u001b[38;5;28mord\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mC\u001b[39m\u001b[38;5;124m'\u001b[39m), \u001b[38;5;241m2\u001b[39m)\n\u001b[1;32m----> 4\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLabeledSymbol(\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mp\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m, \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mC\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m, 2).key() == gtsam.mrsymbol(ord(\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mp\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m), ord(\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mC\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m), 2): \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[43mLabeledSymbol\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mp\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;250;43m \u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mC\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;250;43m \u001b[39;49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mkey()\u001b[38;5;250m \u001b[39m\u001b[38;5;241m==\u001b[39m\u001b[38;5;250m \u001b[39mpc2_key\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: __init__(): incompatible constructor arguments. The following argument types are supported:\n 1. gtsam.gtsam.LabeledSymbol(full_key: int)\n 2. gtsam.gtsam.LabeledSymbol(key: gtsam.gtsam.LabeledSymbol)\n 3. gtsam.gtsam.LabeledSymbol(valType: int, label: int, j: int)\n\nInvoked with: 'p', 'C', 2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Note: mrsymbol expects integer representations of chars (use ord())\n",
|
||||
"pc2_key = gtsam.mrsymbol(ord('p'), ord('C'), 2)\n",
|
||||
"\n",
|
||||
"print(f\"LabeledSymbol('p', 'C', 2).key() == gtsam.mrsymbol(ord('p'), ord('C'), 2): {LabeledSymbol('p', 'C', 2).key() == pc2_key}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,291 +1,278 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# Ordering"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"An `Ordering` specifies the order in which variables are eliminated during inference (e.g., Gaussian elimination, multifrontal QR). The choice of ordering significantly impacts the computational cost and fill-in (sparsity) of the resulting Bayes net or Bayes tree.\n",
|
||||
"\n",
|
||||
"GTSAM provides several algorithms to compute good orderings automatically (like COLAMD, METIS) or allows you to specify a custom ordering."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Ordering.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import Ordering\n",
|
||||
"# Need a graph type for automatic ordering\n",
|
||||
"from gtsam import SymbolicFactorGraph\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating an Ordering\n",
|
||||
"\n",
|
||||
"Orderings can be created manually or computed automatically from a factor graph."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_manual_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "6789abcd-ef01-2345-6789-abcdef012345"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Manual Ordering: \n",
|
||||
"Position 0: x1\n",
|
||||
"Position 1: l1\n",
|
||||
"Position 2: x2\n",
|
||||
"Position 3: l2\n",
|
||||
"Position 4: x0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Manual creation (list of keys)\n",
|
||||
"manual_ordering = Ordering([X(1), L(1), X(2), L(2), X(0)])\n",
|
||||
"manual_ordering.print(\"Manual Ordering: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_auto_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "789abcde-f012-3456-789a-bcdef0123456"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"COLAMD Ordering: \n",
|
||||
"Position 0: x0\n",
|
||||
"Position 1: l1\n",
|
||||
"Position 2: x1\n",
|
||||
"Position 3: l2\n",
|
||||
"Position 4: x2\n",
|
||||
"\n",
|
||||
"Constrained COLAMD (x0, x2 last): \n",
|
||||
"Position 0: l1\n",
|
||||
"Position 1: l2\n",
|
||||
"Position 2: x1\n",
|
||||
"Position 3: x0\n",
|
||||
"Position 4: x2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Automatic creation requires a factor graph\n",
|
||||
"# Let's use a simple SymbolicFactorGraph for structure\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(1))\n",
|
||||
"graph.push_factor(X(1), L(2))\n",
|
||||
"graph.push_factor(X(2), L(2))\n",
|
||||
"\n",
|
||||
"# COLAMD (Column Approximate Minimum Degree) ordering\n",
|
||||
"colamd_ordering = Ordering.Colamd(graph)\n",
|
||||
"colamd_ordering.print(\"COLAMD Ordering: \")\n",
|
||||
"\n",
|
||||
"# Constrained COLAMD (force x0 and x2 to be eliminated last)\n",
|
||||
"constrained_ordering = Ordering.ColamdConstrainedLast(graph, gtsam.KeyVector([X(0), X(2)]))\n",
|
||||
"constrained_ordering.print(\"Constrained COLAMD (x0, x2 last): \")\n",
|
||||
"\n",
|
||||
"# METIS ordering (if GTSAM was built with METIS support)\n",
|
||||
"try:\n",
|
||||
" metis_ordering = Ordering.Metis(graph)\n",
|
||||
" # metis_ordering.print(\"METIS Ordering: \")\n",
|
||||
"except RuntimeError as e:\n",
|
||||
" print(f\"\\nSkipping METIS: {e}\")\n",
|
||||
"\n",
|
||||
"# Natural ordering (based on key magnitude)\n",
|
||||
"natural_ordering = Ordering.Natural(graph)\n",
|
||||
"# natural_ordering.print(\"Natural Ordering: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_access_md"
|
||||
},
|
||||
"source": [
|
||||
"## Accessing Elements\n",
|
||||
"\n",
|
||||
"An `Ordering` behaves like a vector of keys."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "89abcdef-0123-4567-89ab-cdef01234567"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Ordering size: 5\n",
|
||||
"Key at position 0: x0\n",
|
||||
"Key at position 2: x1\n",
|
||||
"Iterating through ordering:\n",
|
||||
" x0\n",
|
||||
" l1\n",
|
||||
" x1\n",
|
||||
" l2\n",
|
||||
" x2\n",
|
||||
"Inverted map (Key -> Position):\n",
|
||||
" x0 -> 0\n",
|
||||
" l1 -> 1\n",
|
||||
" x1 -> 2\n",
|
||||
" l2 -> 3\n",
|
||||
" x2 -> 4\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Ordering size: {colamd_ordering.size()}\")\n",
|
||||
"\n",
|
||||
"# Access by index\n",
|
||||
"key_at_0 = colamd_ordering.at(0)\n",
|
||||
"key_at_2 = colamd_ordering[2] # Also supports [] operator\n",
|
||||
"print(f\"Key at position 0: {gtsam.DefaultKeyFormatter(key_at_0)}\")\n",
|
||||
"print(f\"Key at position 2: {gtsam.DefaultKeyFormatter(key_at_2)}\")\n",
|
||||
"\n",
|
||||
"# Iterate through the ordering\n",
|
||||
"print(\"Iterating through ordering:\")\n",
|
||||
"for key in colamd_ordering:\n",
|
||||
" print(f\" {gtsam.DefaultKeyFormatter(key)}\")\n",
|
||||
"\n",
|
||||
"# Invert the ordering (map from Key to its position)\n",
|
||||
"inverted = colamd_ordering.invert()\n",
|
||||
"print(\"Inverted map (Key -> Position):\")\n",
|
||||
"for key, pos in inverted.items():\n",
|
||||
" print(f\" {gtsam.DefaultKeyFormatter(key)} -> {pos}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_append_md"
|
||||
},
|
||||
"source": [
|
||||
"## Appending Keys\n",
|
||||
"\n",
|
||||
"You can append keys using `push_back`, `+=`, or `,`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_append_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "9abcdef0-1234-5678-9abc-def012345678"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Appended Ordering: \n",
|
||||
"Position 0: x0\n",
|
||||
"Position 1: l1\n",
|
||||
"Position 2: x1\n",
|
||||
"Position 3: l2\n",
|
||||
"Position 4: x2\n",
|
||||
"Position 5: l0\n",
|
||||
"Position 6: x3\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"appended_ordering = Ordering(colamd_ordering)\n",
|
||||
"appended_ordering.push_back(L(0))\n",
|
||||
"appended_ordering += X(3)\n",
|
||||
"# appended_ordering += L(0), X(3) # Can also chain\n",
|
||||
"\n",
|
||||
"appended_ordering.print(\"Appended Ordering: \")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# Ordering"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"An `Ordering` specifies the order in which variables are eliminated during inference (e.g., Gaussian elimination, multifrontal QR). The choice of ordering significantly impacts the computational cost and fill-in (sparsity) of the resulting Bayes net or Bayes tree.\n",
|
||||
"\n",
|
||||
"GTSAM provides several algorithms to compute good orderings automatically (like COLAMD, METIS) or allows you to specify a custom ordering."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Ordering.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ordering_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"id": "ordering_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import Ordering, VariableIndex\n",
|
||||
"# Need a graph type for automatic ordering\n",
|
||||
"from gtsam import SymbolicFactorGraph\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating an Ordering\n",
|
||||
"\n",
|
||||
"Orderings can be created manually or computed automatically from a factor graph."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "ordering_manual_code",
|
||||
"outputId": "6789abcd-ef01-2345-6789-abcdef012345"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Manual Ordering: Position 0: x1, l1, x2, l2, x0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Manual creation (list of keys)\n",
|
||||
"manual_ordering = Ordering([X(1), L(1), X(2), L(2), X(0)])\n",
|
||||
"manual_ordering.print(\"Manual Ordering: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "ordering_auto_code",
|
||||
"outputId": "789abcde-f012-3456-789a-bcdef0123456"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"COLAMD Ordering: Position 0: l1, x0, x1, l2, x2\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "AttributeError",
|
||||
"evalue": "type object 'gtsam.gtsam.Ordering' has no attribute 'ColamdConstrainedLast'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[7], line 17\u001b[0m\n\u001b[0;32m 14\u001b[0m colamd_ordering\u001b[38;5;241m.\u001b[39mprint(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCOLAMD Ordering: \u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 16\u001b[0m \u001b[38;5;66;03m# Constrained COLAMD (force x0 and x2 to be eliminated last)\u001b[39;00m\n\u001b[1;32m---> 17\u001b[0m constrained_ordering \u001b[38;5;241m=\u001b[39m \u001b[43mOrdering\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mColamdConstrainedLast\u001b[49m(graph, gtsam\u001b[38;5;241m.\u001b[39mKeyVector([X(\u001b[38;5;241m0\u001b[39m), X(\u001b[38;5;241m2\u001b[39m)]))\n\u001b[0;32m 18\u001b[0m constrained_ordering\u001b[38;5;241m.\u001b[39mprint(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mConstrained COLAMD (x0, x2 last): \u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 20\u001b[0m \u001b[38;5;66;03m# METIS ordering (if GTSAM was built with METIS support)\u001b[39;00m\n",
|
||||
"\u001b[1;31mAttributeError\u001b[0m: type object 'gtsam.gtsam.Ordering' has no attribute 'ColamdConstrainedLast'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Automatic creation requires a factor graph\n",
|
||||
"# Let's use a simple SymbolicFactorGraph for structure\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(1))\n",
|
||||
"graph.push_factor(X(1), L(2))\n",
|
||||
"graph.push_factor(X(2), L(2))\n",
|
||||
"\n",
|
||||
"# COLAMD (Column Approximate Minimum Degree) ordering\n",
|
||||
"colamd_ordering = Ordering.Colamd(VariableIndex(graph))\n",
|
||||
"colamd_ordering.print(\"COLAMD Ordering: \")\n",
|
||||
"\n",
|
||||
"# Constrained COLAMD (force x0 and x2 to be eliminated last)\n",
|
||||
"constrained_ordering = Ordering.ColamdConstrainedLast(graph, gtsam.KeyVector([X(0), X(2)]))\n",
|
||||
"constrained_ordering.print(\"Constrained COLAMD (x0, x2 last): \")\n",
|
||||
"\n",
|
||||
"# METIS ordering (if GTSAM was built with METIS support)\n",
|
||||
"try:\n",
|
||||
" metis_ordering = Ordering.Metis(graph)\n",
|
||||
" # metis_ordering.print(\"METIS Ordering: \")\n",
|
||||
"except RuntimeError as e:\n",
|
||||
" print(f\"\\nSkipping METIS: {e}\")\n",
|
||||
"\n",
|
||||
"# Natural ordering (based on key magnitude)\n",
|
||||
"natural_ordering = Ordering.Natural(graph)\n",
|
||||
"# natural_ordering.print(\"Natural Ordering: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_access_md"
|
||||
},
|
||||
"source": [
|
||||
"## Accessing Elements\n",
|
||||
"\n",
|
||||
"An `Ordering` behaves like a vector of keys."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "ordering_access_code",
|
||||
"outputId": "89abcdef-0123-4567-89ab-cdef01234567"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Ordering size: 5\n",
|
||||
"Key at position 0: l1\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "AttributeError",
|
||||
"evalue": "'gtsam.gtsam.Ordering' object has no attribute 'invert'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[11], line 8\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mKey at position 0: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mgtsam\u001b[38;5;241m.\u001b[39mDefaultKeyFormatter(key_at_0)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 7\u001b[0m \u001b[38;5;66;03m# Invert the ordering (map from Key to its position)\u001b[39;00m\n\u001b[1;32m----> 8\u001b[0m inverted \u001b[38;5;241m=\u001b[39m \u001b[43mcolamd_ordering\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvert\u001b[49m()\n\u001b[0;32m 9\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mInverted map (Key -> Position):\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 10\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, pos \u001b[38;5;129;01min\u001b[39;00m inverted\u001b[38;5;241m.\u001b[39mitems():\n",
|
||||
"\u001b[1;31mAttributeError\u001b[0m: 'gtsam.gtsam.Ordering' object has no attribute 'invert'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Ordering size: {colamd_ordering.size()}\")\n",
|
||||
"\n",
|
||||
"# Access by index\n",
|
||||
"key_at_0 = colamd_ordering.at(0)\n",
|
||||
"print(f\"Key at position 0: {gtsam.DefaultKeyFormatter(key_at_0)}\")\n",
|
||||
"\n",
|
||||
"# Invert the ordering (map from Key to its position)\n",
|
||||
"inverted = colamd_ordering.invert()\n",
|
||||
"print(\"Inverted map (Key -> Position):\")\n",
|
||||
"for key, pos in inverted.items():\n",
|
||||
" print(f\" {gtsam.DefaultKeyFormatter(key)} -> {pos}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ordering_append_md"
|
||||
},
|
||||
"source": [
|
||||
"## Appending Keys\n",
|
||||
"\n",
|
||||
"You can append keys using `push_back`, `+=`, or `,`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "ordering_append_code",
|
||||
"outputId": "9abcdef0-1234-5678-9abc-def012345678"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Appended Ordering: Position 0: l1, x0, x1, l2, x2, l0, x3\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"appended_ordering = Ordering(colamd_ordering)\n",
|
||||
"appended_ordering.push_back(L(0))\n",
|
||||
"appended_ordering.push_back(X(3))\n",
|
||||
"\n",
|
||||
"appended_ordering.print(\"Appended Ordering: \")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,199 +1,209 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# Symbol"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `Symbol` is a GTSAM class used to create semantically meaningful keys (`gtsam.Key`) for variables. It combines a character (`unsigned char`) and an index (`uint64_t`) into a single 64-bit integer key. This allows for easy identification of variable types and their indices, e.g., 'x' for poses and 'l' for landmarks, like `x0`, `x1`, `l0`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Symbol.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "symbol_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "symbol_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import Symbol"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"A `Symbol` can be created by providing a character and an index. It can also be created by decoding an existing `gtsam.Key` (integer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "symbol_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "c1d2e3f4-a5b6-7890-bcde-f12345678901"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol from char/index: x5\n",
|
||||
"Symbol from key 8070450532247928837: x5\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create Symbol 'x' with index 5\n",
|
||||
"sym1 = Symbol('x', 5)\n",
|
||||
"print(f\"Symbol from char/index: {sym1.string()}\")\n",
|
||||
"\n",
|
||||
"# Get the underlying integer key\n",
|
||||
"key1 = sym1.key()\n",
|
||||
"\n",
|
||||
"# Reconstruct Symbol from the key\n",
|
||||
"sym2 = Symbol(key1)\n",
|
||||
"print(f\"Symbol from key {key1}: {sym2.string()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Usage\n",
|
||||
"\n",
|
||||
"You can access the character, index, and underlying integer key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "symbol_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "d2e3f4a5-b6c7-8901-cdef-123456789012"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol: l10\n",
|
||||
" Char: l\n",
|
||||
" Index: 10\n",
|
||||
" Key: 7783684379976990730\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"landmark_sym = Symbol('l', 10)\n",
|
||||
"\n",
|
||||
"print(f\"Symbol: {landmark_sym.string()}\")\n",
|
||||
"print(f\" Char: {landmark_sym.chr()}\")\n",
|
||||
"print(f\" Index: {landmark_sym.index()}\")\n",
|
||||
"print(f\" Key: {landmark_sym.key()}\")\n",
|
||||
"\n",
|
||||
"# Symbols are often used directly where Keys are expected in GTSAM functions,\n",
|
||||
"# as they implicitly convert.\n",
|
||||
"# e.g., values.insert(landmark_sym, gtsam.Point3(1,2,3))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_shorthand_md"
|
||||
},
|
||||
"source": [
|
||||
"## Shorthand Functions\n",
|
||||
"\n",
|
||||
"GTSAM provides convenient shorthand functions `gtsam.symbol_shorthand.X(j)`, `gtsam.symbol_shorthand.L(j)`, etc., which are equivalent to `gtsam.Symbol('x', j)`, `gtsam.Symbol('l', j)`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "symbol_shorthand_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "e3f4a5b6-c7d8-9012-def0-234567890123"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol('x', 0).key() == gtsam.symbol_shorthand.X(0): True\n",
|
||||
"Symbol('l', 1).key() == gtsam.symbol_shorthand.L(1): True\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"x0_key = symbol_shorthand.X(0)\n",
|
||||
"l1_key = symbol_shorthand.L(1)\n",
|
||||
"\n",
|
||||
"print(f\"Symbol('x', 0).key() == gtsam.symbol_shorthand.X(0): {Symbol('x', 0).key() == x0_key}\")\n",
|
||||
"print(f\"Symbol('l', 1).key() == gtsam.symbol_shorthand.L(1): {Symbol('l', 1).key() == l1_key}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# Symbol"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `Symbol` is a GTSAM class used to create semantically meaningful keys (`gtsam.Key`) for variables. It combines a character (`unsigned char`) and an index (`uint64_t`) into a single 64-bit integer key. This allows for easy identification of variable types and their indices, e.g., 'x' for poses and 'l' for landmarks, like `x0`, `x1`, `l0`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/Symbol.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "symbol_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"id": "symbol_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import Symbol"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_init_md"
|
||||
},
|
||||
"source": [
|
||||
"## Initialization\n",
|
||||
"\n",
|
||||
"A `Symbol` can be created by providing a character and an index. It can also be created by decoding an existing `gtsam.Key` (integer)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "symbol_create_code",
|
||||
"outputId": "c1d2e3f4-a5b6-7890-bcde-f12345678901"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol from char/index: x5\n",
|
||||
"Symbol from key 8646911284551352325: x5\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create Symbol 'x' with index 5\n",
|
||||
"sym1 = Symbol('x', 5)\n",
|
||||
"print(f\"Symbol from char/index: {sym1.string()}\")\n",
|
||||
"\n",
|
||||
"# Get the underlying integer key\n",
|
||||
"key1 = sym1.key()\n",
|
||||
"\n",
|
||||
"# Reconstruct Symbol from the key\n",
|
||||
"sym2 = Symbol(key1)\n",
|
||||
"print(f\"Symbol from key {key1}: {sym2.string()}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_props_md"
|
||||
},
|
||||
"source": [
|
||||
"## Properties and Usage\n",
|
||||
"\n",
|
||||
"You can access the character, index, and underlying integer key."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "symbol_access_code",
|
||||
"outputId": "d2e3f4a5-b6c7-8901-cdef-123456789012"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol: l10\n",
|
||||
" Char: 108\n",
|
||||
" Index: 10\n",
|
||||
" Key: 7782220156096217098\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"landmark_sym = Symbol('l', 10)\n",
|
||||
"\n",
|
||||
"print(f\"Symbol: {landmark_sym.string()}\")\n",
|
||||
"print(f\" Char: {landmark_sym.chr()}\")\n",
|
||||
"print(f\" Index: {landmark_sym.index()}\")\n",
|
||||
"print(f\" Key: {landmark_sym.key()}\")\n",
|
||||
"\n",
|
||||
"# Symbols are often used directly where Keys are expected in GTSAM functions,\n",
|
||||
"# as they implicitly convert.\n",
|
||||
"# e.g., values.insert(landmark_sym, gtsam.Point3(1,2,3))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "symbol_shorthand_md"
|
||||
},
|
||||
"source": [
|
||||
"## Shorthand Functions\n",
|
||||
"\n",
|
||||
"GTSAM provides convenient shorthand functions `gtsam.symbol_shorthand.X(j)`, `gtsam.symbol_shorthand.L(j)`, etc., which are equivalent to `gtsam.Symbol('x', j)`, `gtsam.Symbol('l', j)`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "symbol_shorthand_code",
|
||||
"outputId": "e3f4a5b6-c7d8-9012-def0-234567890123"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Symbol('x', 0).key() == gtsam.symbol_shorthand.X(0): True\n",
|
||||
"Symbol('l', 1).key() == gtsam.symbol_shorthand.L(1): True\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"x0_key = symbol_shorthand.X(0)\n",
|
||||
"l1_key = symbol_shorthand.L(1)\n",
|
||||
"\n",
|
||||
"print(f\"Symbol('x', 0).key() == gtsam.symbol_shorthand.X(0): {Symbol('x', 0).key() == x0_key}\")\n",
|
||||
"print(f\"Symbol('l', 1).key() == gtsam.symbol_shorthand.L(1): {Symbol('l', 1).key() == l1_key}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
|
@ -1,233 +1,237 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_intro_md"
|
||||
},
|
||||
"source": [
|
||||
"# VariableIndex"
|
||||
]
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_intro_md"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `VariableIndex` provides an efficient way to look up which factors in a `FactorGraph` involve a particular variable (Key). It stores, for each variable, a list of the indices of the factors that include that variable.\n",
|
||||
"\n",
|
||||
"This structure is often computed internally by GTSAM algorithms (like ordering methods or elimination) but can also be created explicitly if needed, for example, to improve performance when multiple operations need this information."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/VariableIndex.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vindex_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vindex_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import VariableIndex\n",
|
||||
"# Need a graph type for creation\n",
|
||||
"from gtsam import SymbolicFactorGraph\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a VariableIndex\n",
|
||||
"\n",
|
||||
"A `VariableIndex` is typically created from an existing `FactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vindex_create_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "abcdef01-2345-6789-abcd-ef0123456789"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"VariableIndex: \n",
|
||||
"nEntries = 13, nFactors = 7\n",
|
||||
"var l1: 3 4 \n",
|
||||
"var l2: 5 6 \n",
|
||||
"var x0: 0 1 3 \n",
|
||||
"var x1: 1 2 4 5 \n",
|
||||
"var x2: 2 6 \n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple SymbolicFactorGraph\n",
|
||||
"graph = SymbolicFactorGraph()\n",
|
||||
"graph.push_factor(X(0)) # Factor 0\n",
|
||||
"graph.push_factor(X(0), X(1)) # Factor 1\n",
|
||||
"graph.push_factor(X(1), X(2)) # Factor 2\n",
|
||||
"graph.push_factor(X(0), L(1)) # Factor 3\n",
|
||||
"graph.push_factor(X(1), L(1)) # Factor 4\n",
|
||||
"graph.push_factor(X(1), L(2)) # Factor 5\n",
|
||||
"graph.push_factor(X(2), L(2)) # Factor 6\n",
|
||||
"\n",
|
||||
"# Create VariableIndex from the graph\n",
|
||||
"variable_index = VariableIndex(graph)\n",
|
||||
"\n",
|
||||
"# Print the index\n",
|
||||
"variable_index.print(\"VariableIndex: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_access_md"
|
||||
},
|
||||
"source": [
|
||||
"## Accessing Information\n",
|
||||
"\n",
|
||||
"You can query the number of variables, factors, and entries, and look up the factors associated with a specific variable."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vindex_access_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "bcdef012-3456-789a-bcde-f0123456789a"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Number of variables (size): 5\n",
|
||||
"Number of factors (nFactors): 7\n",
|
||||
"Number of variable-factor entries (nEntries): 13\n",
|
||||
"\n",
|
||||
"Factors involving x1: [1, 2, 4, 5]\n",
|
||||
"Factors involving l1: [3, 4]\n",
|
||||
"\n",
|
||||
"Iterating through VariableIndex:\n",
|
||||
" Variable l1 involves factors: [3, 4]\n",
|
||||
" Variable l2 involves factors: [5, 6]\n",
|
||||
" Variable x0 involves factors: [0, 1, 3]\n",
|
||||
" Variable x1 involves factors: [1, 2, 4, 5]\n",
|
||||
" Variable x2 involves factors: [2, 6]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Number of variables (size): {variable_index.size()}\")\n",
|
||||
"print(f\"Number of factors (nFactors): {variable_index.nFactors()}\")\n",
|
||||
"print(f\"Number of variable-factor entries (nEntries): {variable_index.nEntries()}\")\n",
|
||||
"\n",
|
||||
"# Get factors involving a specific variable\n",
|
||||
"factors_x1 = variable_index[X(1)] # Returns a FactorIndices (FastVector<size_t>)\n",
|
||||
"print(f\"Factors involving x1: {factors_x1}\")\n",
|
||||
"\n",
|
||||
"# Use key directly\n",
|
||||
"factors_l1 = variable_index[L(1)]\n",
|
||||
"print(f\"Factors involving l1: {factors_l1}\")\n",
|
||||
"\n",
|
||||
"# Iterate through the index (items are pairs of Key, FactorIndices)\n",
|
||||
"print(\"Iterating through VariableIndex:\")\n",
|
||||
"for key, factor_indices in variable_index.items(): # Use .items() like a dict\n",
|
||||
" print(f\" Variable {gtsam.DefaultKeyFormatter(key)} involves factors: {factor_indices}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_use_md"
|
||||
},
|
||||
"source": [
|
||||
"## Usage in Algorithms\n",
|
||||
"\n",
|
||||
"`VariableIndex` is primarily used as input to other algorithms, particularly ordering methods like `Ordering.Colamd`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vindex_use_code",
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"outputId": "cdef0123-4567-89ab-cdef-0123456789ab"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"COLAMD Ordering from VariableIndex: \n",
|
||||
"Position 0: x0\n",
|
||||
"Position 1: l1\n",
|
||||
"Position 2: x1\n",
|
||||
"Position 3: l2\n",
|
||||
"Position 4: x2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Compute COLAMD ordering directly from the VariableIndex\n",
|
||||
"colamd_ordering = Ordering.Colamd(variable_index)\n",
|
||||
"colamd_ordering.print(\"COLAMD Ordering from VariableIndex: \")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
"source": [
|
||||
"# VariableIndex"
|
||||
]
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_desc_md"
|
||||
},
|
||||
"source": [
|
||||
"A `VariableIndex` provides an efficient way to look up which factors in a `FactorGraph` involve a particular variable (Key). It stores, for each variable, a list of the indices of the factors that include that variable.\n",
|
||||
"\n",
|
||||
"This structure is often computed internally by GTSAM algorithms (like ordering methods or elimination) but can also be created explicitly if needed, for example, to improve performance when multiple operations need this information."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_colab_md"
|
||||
},
|
||||
"source": [
|
||||
"<a href=\"https://colab.research.google.com/github/borglab/gtsam/blob/develop/gtsam/inference/doc/VariableIndex.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vindex_pip_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%pip install gtsam"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"id": "vindex_import_code"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gtsam\n",
|
||||
"from gtsam import VariableIndex, Ordering\n",
|
||||
"# Need a graph type for creation\n",
|
||||
"from gtsam import SymbolicFactorGraph\n",
|
||||
"from gtsam import symbol_shorthand\n",
|
||||
"\n",
|
||||
"X = symbol_shorthand.X\n",
|
||||
"L = symbol_shorthand.L"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_create_md"
|
||||
},
|
||||
"source": [
|
||||
"## Creating a VariableIndex\n",
|
||||
"\n",
|
||||
"A `VariableIndex` is typically created from an existing `FactorGraph`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "vindex_create_code",
|
||||
"outputId": "abcdef01-2345-6789-abcd-ef0123456789"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"VariableIndex: nEntries = 13, nFactors = 7\n",
|
||||
"var l1: 3 4\n",
|
||||
"var l2: 5 6\n",
|
||||
"var x0: 0 1 3\n",
|
||||
"var x1: 1 2 4 5\n",
|
||||
"var x2: 2 6\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Create a simple SymbolicFactorGraph\n",
|
||||
"graph = SymbolicFactorGraph()\n",
|
||||
"graph.push_factor(X(0)) # Factor 0\n",
|
||||
"graph.push_factor(X(0), X(1)) # Factor 1\n",
|
||||
"graph.push_factor(X(1), X(2)) # Factor 2\n",
|
||||
"graph.push_factor(X(0), L(1)) # Factor 3\n",
|
||||
"graph.push_factor(X(1), L(1)) # Factor 4\n",
|
||||
"graph.push_factor(X(1), L(2)) # Factor 5\n",
|
||||
"graph.push_factor(X(2), L(2)) # Factor 6\n",
|
||||
"\n",
|
||||
"# Create VariableIndex from the graph\n",
|
||||
"variable_index = VariableIndex(graph)\n",
|
||||
"\n",
|
||||
"# Print the index\n",
|
||||
"variable_index.print(\"VariableIndex: \")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_access_md"
|
||||
},
|
||||
"source": [
|
||||
"## Accessing Information\n",
|
||||
"\n",
|
||||
"You can query the number of variables, factors, and entries, and look up the factors associated with a specific variable."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "vindex_access_code",
|
||||
"outputId": "bcdef012-3456-789a-bcde-f0123456789a"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Number of variables (size): 5\n",
|
||||
"Number of factors (nFactors): 7\n",
|
||||
"Number of variable-factor entries (nEntries): 13\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ename": "TypeError",
|
||||
"evalue": "'gtsam.gtsam.VariableIndex' object is not subscriptable",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[1;32mIn[3], line 6\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mNumber of variable-factor entries (nEntries): \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mvariable_index\u001b[38;5;241m.\u001b[39mnEntries()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 5\u001b[0m \u001b[38;5;66;03m# Get factors involving a specific variable\u001b[39;00m\n\u001b[1;32m----> 6\u001b[0m factors_x1 \u001b[38;5;241m=\u001b[39m \u001b[43mvariable_index\u001b[49m\u001b[43m[\u001b[49m\u001b[43mX\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m \u001b[38;5;66;03m# Returns a FactorIndices (FastVector<size_t>)\u001b[39;00m\n\u001b[0;32m 7\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactors involving x1: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfactors_x1\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 9\u001b[0m \u001b[38;5;66;03m# Use key directly\u001b[39;00m\n",
|
||||
"\u001b[1;31mTypeError\u001b[0m: 'gtsam.gtsam.VariableIndex' object is not subscriptable"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Number of variables (size): {variable_index.size()}\")\n",
|
||||
"print(f\"Number of factors (nFactors): {variable_index.nFactors()}\")\n",
|
||||
"print(f\"Number of variable-factor entries (nEntries): {variable_index.nEntries()}\")\n",
|
||||
"\n",
|
||||
"# Get factors involving a specific variable\n",
|
||||
"factors_x1 = variable_index[X(1)] # Returns a FactorIndices (FastVector<size_t>)\n",
|
||||
"print(f\"Factors involving x1: {factors_x1}\")\n",
|
||||
"\n",
|
||||
"# Use key directly\n",
|
||||
"factors_l1 = variable_index[L(1)]\n",
|
||||
"print(f\"Factors involving l1: {factors_l1}\")\n",
|
||||
"\n",
|
||||
"# Iterate through the index (items are pairs of Key, FactorIndices)\n",
|
||||
"print(\"Iterating through VariableIndex:\")\n",
|
||||
"for key, factor_indices in variable_index.items(): # Use .items() like a dict\n",
|
||||
" print(f\" Variable {gtsam.DefaultKeyFormatter(key)} involves factors: {factor_indices}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vindex_use_md"
|
||||
},
|
||||
"source": [
|
||||
"## Usage in Algorithms\n",
|
||||
"\n",
|
||||
"`VariableIndex` is primarily used as input to other algorithms, particularly ordering methods like `Ordering.Colamd`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "vindex_use_code",
|
||||
"outputId": "cdef0123-4567-89ab-cdef-0123456789ab"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"COLAMD Ordering from VariableIndex: Position 0: l1, x0, x1, l2, x2\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Compute COLAMD ordering directly from the VariableIndex\n",
|
||||
"colamd_ordering = Ordering.Colamd(variable_index)\n",
|
||||
"colamd_ordering.print(\"COLAMD Ordering from VariableIndex: \")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "gtsam",
|
||||
"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.13.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue