From 020071719ed47cee93b3d81e055486644e7d9bdb Mon Sep 17 00:00:00 2001 From: senselessDev Date: Sun, 23 Jan 2022 13:39:21 +0100 Subject: [PATCH 1/5] expose GraphvizFormatting and test it in Python --- gtsam/discrete/discrete.i | 6 + gtsam/nonlinear/nonlinear.i | 19 ++- python/gtsam/tests/test_GraphvizFormatting.py | 139 ++++++++++++++++++ 3 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 python/gtsam/tests/test_GraphvizFormatting.py diff --git a/gtsam/discrete/discrete.i b/gtsam/discrete/discrete.i index e2310f434..1bee25379 100644 --- a/gtsam/discrete/discrete.i +++ b/gtsam/discrete/discrete.i @@ -222,6 +222,12 @@ class DotWriter { DotWriter(double figureWidthInches = 5, double figureHeightInches = 5, bool plotFactorPoints = true, bool connectKeysToFactor = true, bool binaryEdges = true); + + double figureWidthInches; + double figureHeightInches; + bool plotFactorPoints; + bool connectKeysToFactor; + bool binaryEdges; }; #include diff --git a/gtsam/nonlinear/nonlinear.i b/gtsam/nonlinear/nonlinear.i index 84c4939f4..b6ab086c4 100644 --- a/gtsam/nonlinear/nonlinear.i +++ b/gtsam/nonlinear/nonlinear.i @@ -133,6 +133,18 @@ class Ordering { void serialize() const; }; +#include +class GraphvizFormatting : gtsam::DotWriter { + GraphvizFormatting(); + + enum Axis { X, Y, Z, NEGX, NEGY, NEGZ }; + Axis paperHorizontalAxis; + Axis paperVerticalAxis; + + double scale; + bool mergeSimilarFactors; +}; + #include class NonlinearFactorGraph { NonlinearFactorGraph(); @@ -195,10 +207,13 @@ class NonlinearFactorGraph { string dot( const gtsam::Values& values, - const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter); + const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter, + const GraphvizFormatting& writer = GraphvizFormatting()); void saveGraph(const string& s, const gtsam::Values& values, const gtsam::KeyFormatter& keyFormatter = - gtsam::DefaultKeyFormatter) const; + gtsam::DefaultKeyFormatter, + const GraphvizFormatting& writer = + GraphvizFormatting()) const; }; #include diff --git a/python/gtsam/tests/test_GraphvizFormatting.py b/python/gtsam/tests/test_GraphvizFormatting.py new file mode 100644 index 000000000..bd675b988 --- /dev/null +++ b/python/gtsam/tests/test_GraphvizFormatting.py @@ -0,0 +1,139 @@ +""" +GTSAM Copyright 2010-2021, Georgia Tech Research Corporation, +Atlanta, Georgia 30332-0415 +All Rights Reserved + +See LICENSE for the license information + +Unit tests for Graphviz formatting of NonlinearFactorGraph. +Author: Frank Dellaert & senselessDev +""" + +# pylint: disable=no-member, invalid-name + +import unittest +import textwrap + +import numpy as np + +import gtsam +from gtsam.utils.test_case import GtsamTestCase + + +class TestGraphvizFormatting(GtsamTestCase): + """Tests for saving NonlinearFactorGraph to GraphViz format.""" + + def setUp(self): + self.graph = gtsam.NonlinearFactorGraph() + + odometry = gtsam.Pose2(2.0, 0.0, 0.0) + odometryNoise = gtsam.noiseModel.Diagonal.Sigmas( + np.array([0.2, 0.2, 0.1])) + self.graph.add(gtsam.BetweenFactorPose2(0, 1, odometry, odometryNoise)) + self.graph.add(gtsam.BetweenFactorPose2(1, 2, odometry, odometryNoise)) + + self.values = gtsam.Values() + self.values.insert_pose2(0, gtsam.Pose2(0., 0., 0.)) + self.values.insert_pose2(1, gtsam.Pose2(2., 0., 0.)) + self.values.insert_pose2(2, gtsam.Pose2(4., 0., 0.)) + + def test_default(self): + """Test with default GraphvizFormatting""" + expected_result = """\ + graph { + size="5,5"; + + var0[label="0", pos="0,0!"]; + var1[label="1", pos="0,2!"]; + var2[label="2", pos="0,4!"]; + + factor0[label="", shape=point]; + var0--factor0; + var1--factor0; + factor1[label="", shape=point]; + var1--factor1; + var2--factor1; + } + """ + + self.assertEqual(self.graph.dot(self.values), + textwrap.dedent(expected_result)) + + def test_swapped_axes(self): + """Test with user-defined GraphvizFormatting swapping x and y""" + expected_result = """\ + graph { + size="5,5"; + + var0[label="0", pos="0,0!"]; + var1[label="1", pos="2,0!"]; + var2[label="2", pos="4,0!"]; + + factor0[label="", shape=point]; + var0--factor0; + var1--factor0; + factor1[label="", shape=point]; + var1--factor1; + var2--factor1; + } + """ + + graphviz_formatting = gtsam.GraphvizFormatting() + graphviz_formatting.paperHorizontalAxis = gtsam.GraphvizFormatting.Axis.X + graphviz_formatting.paperVerticalAxis = gtsam.GraphvizFormatting.Axis.Y + self.assertEqual(self.graph.dot(self.values, + writer=graphviz_formatting), + textwrap.dedent(expected_result)) + + def test_factor_points(self): + """Test with user-defined GraphvizFormatting without factor points""" + expected_result = """\ + graph { + size="5,5"; + + var0[label="0", pos="0,0!"]; + var1[label="1", pos="0,2!"]; + var2[label="2", pos="0,4!"]; + + var0--var1; + var1--var2; + } + """ + + graphviz_formatting = gtsam.GraphvizFormatting() + graphviz_formatting.plotFactorPoints = False + + self.assertEqual(self.graph.dot(self.values, + writer=graphviz_formatting), + textwrap.dedent(expected_result)) + + def test_width_height(self): + """Test with user-defined GraphvizFormatting for width and height""" + expected_result = """\ + graph { + size="20,10"; + + var0[label="0", pos="0,0!"]; + var1[label="1", pos="0,2!"]; + var2[label="2", pos="0,4!"]; + + factor0[label="", shape=point]; + var0--factor0; + var1--factor0; + factor1[label="", shape=point]; + var1--factor1; + var2--factor1; + } + """ + + graphviz_formatting = gtsam.GraphvizFormatting() + graphviz_formatting.figureWidthInches = 20 + graphviz_formatting.figureHeightInches = 10 + + self.assertEqual(self.graph.dot(self.values, + writer=graphviz_formatting), + textwrap.dedent(expected_result)) + + +if __name__ == "__main__": + unittest.main() From 67ca0b9c4eb4fd59a05fb6fbe6b5cf29d7e62e4a Mon Sep 17 00:00:00 2001 From: senselessDev Date: Sun, 23 Jan 2022 14:19:26 +0100 Subject: [PATCH 2/5] add python test files to test target dependencies --- python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index b39e067b0..f42e330b2 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -181,5 +181,5 @@ add_custom_target( ${CMAKE_COMMAND} -E env # add package to python path so no need to install "PYTHONPATH=${GTSAM_PYTHON_BUILD_DIRECTORY}/$ENV{PYTHONPATH}" ${PYTHON_EXECUTABLE} -m unittest discover -v -s . - DEPENDS ${GTSAM_PYTHON_DEPENDENCIES} + DEPENDS ${GTSAM_PYTHON_DEPENDENCIES} ${GTSAM_PYTHON_TEST_FILES} WORKING_DIRECTORY "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam/tests") From fb0575720cddb639e2edc4caaeec4cd3d679a613 Mon Sep 17 00:00:00 2001 From: senselessDev Date: Sun, 23 Jan 2022 14:48:06 +0100 Subject: [PATCH 3/5] consider CMAKE_INSTALL_PREFIX for python-install target --- python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index f42e330b2..56411f96c 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -170,7 +170,7 @@ endif() # Add custom target so we can install with `make python-install` set(GTSAM_PYTHON_INSTALL_TARGET python-install) add_custom_target(${GTSAM_PYTHON_INSTALL_TARGET} - COMMAND ${PYTHON_EXECUTABLE} ${GTSAM_PYTHON_BUILD_DIRECTORY}/setup.py install + COMMAND ${PYTHON_EXECUTABLE} ${GTSAM_PYTHON_BUILD_DIRECTORY}/setup.py install --prefix ${CMAKE_INSTALL_PREFIX} DEPENDS ${GTSAM_PYTHON_DEPENDENCIES} WORKING_DIRECTORY ${GTSAM_PYTHON_BUILD_DIRECTORY}) From 79038b1b46221a6a3c5ff74cafbbfc9019c45d20 Mon Sep 17 00:00:00 2001 From: senselessDev Date: Mon, 24 Jan 2022 09:21:48 +0100 Subject: [PATCH 4/5] dont copy GT copyright --- python/gtsam/tests/test_GraphvizFormatting.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/python/gtsam/tests/test_GraphvizFormatting.py b/python/gtsam/tests/test_GraphvizFormatting.py index bd675b988..ecdc23b45 100644 --- a/python/gtsam/tests/test_GraphvizFormatting.py +++ b/python/gtsam/tests/test_GraphvizFormatting.py @@ -1,12 +1,8 @@ """ -GTSAM Copyright 2010-2021, Georgia Tech Research Corporation, -Atlanta, Georgia 30332-0415 -All Rights Reserved - See LICENSE for the license information Unit tests for Graphviz formatting of NonlinearFactorGraph. -Author: Frank Dellaert & senselessDev +Author: senselessDev (contact by mentioning on GitHub, e.g. in PR#1059) """ # pylint: disable=no-member, invalid-name From b35ed166758e54c72b9062dba2ca54cfd5865545 Mon Sep 17 00:00:00 2001 From: senselessDev Date: Mon, 24 Jan 2022 13:59:58 +0100 Subject: [PATCH 5/5] remove --prefix for setup.py --- python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 56411f96c..f42e330b2 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -170,7 +170,7 @@ endif() # Add custom target so we can install with `make python-install` set(GTSAM_PYTHON_INSTALL_TARGET python-install) add_custom_target(${GTSAM_PYTHON_INSTALL_TARGET} - COMMAND ${PYTHON_EXECUTABLE} ${GTSAM_PYTHON_BUILD_DIRECTORY}/setup.py install --prefix ${CMAKE_INSTALL_PREFIX} + COMMAND ${PYTHON_EXECUTABLE} ${GTSAM_PYTHON_BUILD_DIRECTORY}/setup.py install DEPENDS ${GTSAM_PYTHON_DEPENDENCIES} WORKING_DIRECTORY ${GTSAM_PYTHON_BUILD_DIRECTORY})