diff --git a/.cproject b/.cproject
index b76463977..1282db639 100644
--- a/.cproject
+++ b/.cproject
@@ -732,46 +732,6 @@
true
true
-
- make
- -j5
- testValues.run
- true
- true
- true
-
-
- make
- -j5
- testOrdering.run
- true
- true
- true
-
-
- make
- -j5
- testKey.run
- true
- true
- true
-
-
- make
- -j5
- testLinearContainerFactor.run
- true
- true
- true
-
-
- make
- -j6 -j8
- testWhiteNoiseFactor.run
- true
- true
- true
-
make
-j2
@@ -2015,6 +1975,134 @@
true
true
+
+ make
+ -j5
+ testCal3Bundler.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testCal3DS2.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testCalibratedCamera.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testEssentialMatrix.run
+ true
+ true
+ true
+
+
+ make
+ -j1 VERBOSE=1
+ testHomography2.run
+ true
+ false
+ true
+
+
+ make
+ -j5
+ testPinholeCamera.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testPoint2.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testPoint3.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testPose2.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testPose3.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testRot3M.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testSphere2.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testStereoCamera.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ timeCalibratedCamera.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ timePinholeCamera.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ timeStereoCamera.run
+ true
+ true
+ true
+
make
-j2
@@ -2095,70 +2183,6 @@
true
true
-
- make
- -j5
- testGeneralSFMFactor.run
- true
- true
- true
-
-
- make
- -j5
- testProjectionFactor.run
- true
- true
- true
-
-
- make
- -j5
- testGeneralSFMFactor_Cal3Bundler.run
- true
- true
- true
-
-
- make
- -j6 -j8
- testAntiFactor.run
- true
- true
- true
-
-
- make
- -j6 -j8
- testBetweenFactor.run
- true
- true
- true
-
-
- make
- -j5
- testDataset.run
- true
- true
- true
-
-
- make
- -j5
- testEssentialMatrixFactor.run
- true
- true
- true
-
-
- make
- -j5
- testRotateFactor.run
- true
- true
- true
-
make
-j2
@@ -2191,6 +2215,14 @@
true
true
+
+ make
+ -j5
+ testWrap.run
+ true
+ true
+ true
+
make
-j5
@@ -2263,6 +2295,22 @@
true
true
+
+ make
+ -j5
+ testMatrix.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testVector.run
+ true
+ true
+ true
+
make
-j5
@@ -2535,146 +2583,66 @@
true
true
-
+
make
-j5
- testStereoCamera.run
+ testAntiFactor.run
true
true
true
-
+
make
-j5
- testRot3M.run
+ testBetweenFactor.run
true
true
true
-
+
make
-j5
- testPoint3.run
+ testDataset.run
true
true
true
-
+
make
-j5
- testCalibratedCamera.run
+ testEssentialMatrixFactor.run
true
true
true
-
+
make
-j5
- timeStereoCamera.run
+ testGeneralSFMFactor_Cal3Bundler.run
true
true
true
-
- make
- -j1 VERBOSE=1
- testHomography2.run
- true
- false
- true
-
-
+
make
-j5
- testPoint2.run
+ testGeneralSFMFactor.run
true
true
true
-
+
make
-j5
- testPose2.run
+ testProjectionFactor.run
true
true
true
-
+
make
-j5
- testPose3.run
- true
- true
- true
-
-
- make
- -j5
- timeCalibratedCamera.run
- true
- true
- true
-
-
- make
- -j5
- testPinholeCamera.run
- true
- true
- true
-
-
- make
- -j5
- timePinholeCamera.run
- true
- true
- true
-
-
- make
- -j5
- testCal3DS2.run
- true
- true
- true
-
-
- make
- -j5
- testCal3Bundler.run
- true
- true
- true
-
-
- make
- -j5
- testSphere2.run
- true
- true
- true
-
-
- make
- -j5
- testEssentialMatrix.run
- true
- true
- true
-
-
- make
- -j5
- testVector.run
- true
- true
- true
-
-
- make
- -j5
- testMatrix.run
+ testRotateFactor.run
true
true
true
@@ -2831,6 +2799,70 @@
true
true
+
+ make
+ -j5
+ Pose2SLAMExample_lago.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ Pose2SLAMExample_g2o.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testLago.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testLinearContainerFactor.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testOrdering.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testValues.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ testWhiteNoiseFactor.run
+ true
+ true
+ true
+
+
+ make
+ -j5
+ timeLago.run
+ true
+ true
+ true
+
make
-j4
@@ -2958,14 +2990,6 @@
true
true
-
- make
- -j5
- testWrap.run
- true
- true
- true
-
make
-j5
diff --git a/.gitignore b/.gitignore
index 6d274af09..cf23a5147 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
/build*
/doc*
*.pyc
-*.DS_Store
\ No newline at end of file
+*.DS_Store
+/examples/Data/dubrovnik-3-7-pre-rewritten.txt
+/examples/Data/pose2example-rewritten.txt
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d03f11ede..933f2083e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 2.6)
# Set the version number for the library
set (GTSAM_VERSION_MAJOR 3)
-set (GTSAM_VERSION_MINOR 0)
+set (GTSAM_VERSION_MINOR 1)
set (GTSAM_VERSION_PATCH 0)
math (EXPR GTSAM_VERSION_NUMERIC "10000 * ${GTSAM_VERSION_MAJOR} + 100 * ${GTSAM_VERSION_MINOR} + ${GTSAM_VERSION_PATCH}")
set (GTSAM_VERSION_STRING "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}.${GTSAM_VERSION_PATCH}")
@@ -91,10 +91,10 @@ set(CPACK_GENERATOR "TGZ" CACHE STRING "CPack Default Binary Generator")
# If using Boost shared libs, disable auto linking
if(MSVC)
# Some libraries, at least Boost Program Options, rely on this to export DLL symbols
- add_definitions(-DBOOST_ALL_DYN_LINK)
# Disable autolinking
if(NOT Boost_USE_STATIC_LIBS)
add_definitions(-DBOOST_ALL_NO_LIB)
+ add_definitions(-DBOOST_ALL_DYN_LINK)
endif()
endif()
@@ -273,6 +273,13 @@ if(MSVC)
add_definitions(/wd4251 /wd4275 /wd4251 /wd4661 /wd4344) # Disable non-DLL-exported base class and other warnings
endif()
+# GCC 4.8+ complains about local typedefs which we use for shared_ptr etc.
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
+ add_definitions(-Wno-unused-local-typedefs)
+ endif()
+endif()
+
if(GTSAM_ENABLE_CONSISTENCY_CHECKS)
add_definitions(-DGTSAM_EXTRA_CONSISTENCY_CHECKS)
endif()
diff --git a/cmake/GtsamBuildTypes.cmake b/cmake/GtsamBuildTypes.cmake
index ee2bbd42a..9a0b297ab 100644
--- a/cmake/GtsamBuildTypes.cmake
+++ b/cmake/GtsamBuildTypes.cmake
@@ -56,7 +56,7 @@ endif()
# Clang on Mac uses a template depth that is less than standard and is too small
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if(NOT "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "5.0")
- add_definitions(-ftemplate-depth=1024)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth=1024")
endif()
endif()
diff --git a/cmake/GtsamMatlabWrap.cmake b/cmake/GtsamMatlabWrap.cmake
index 98bd7f469..5d018376b 100644
--- a/cmake/GtsamMatlabWrap.cmake
+++ b/cmake/GtsamMatlabWrap.cmake
@@ -93,7 +93,7 @@ function(wrap_library_internal interfaceHeader linkLibraries extraIncludeDirs ex
# Paths for generated files
set(generated_files_path "${PROJECT_BINARY_DIR}/wrap/${moduleName}")
- set(generated_cpp_file "${PROJECT_BINARY_DIR}/wrap/${moduleName}/${moduleName}_wrapper.cpp")
+ set(generated_cpp_file "${generated_files_path}/${moduleName}_wrapper.cpp")
set(compiled_mex_modules_root "${PROJECT_BINARY_DIR}/wrap/${moduleName}_mex")
message(STATUS "Building wrap module ${moduleName}")
@@ -108,24 +108,87 @@ function(wrap_library_internal interfaceHeader linkLibraries extraIncludeDirs ex
list(GET GTSAM_INCLUDE_DIR 0 installed_includes_path)
set(matlab_h_path "${installed_includes_path}/wrap")
endif()
-
- # Add -shared or -static suffix to targets
+
+ # If building a static mex module, add all cmake-linked libraries to the
+ # explicit link libraries list so that the next block of code can unpack
+ # any static libraries
+ set(automaticDependencies "")
+ foreach(lib ${moduleName} ${linkLibraries})
+ #message("MODULE NAME: ${moduleName}")
+ if(TARGET "${lib}")
+ get_target_property(dependentLibraries ${lib} INTERFACE_LINK_LIBRARIES)
+ # message("DEPENDENT LIBRARIES: ${dependentLibraries}")
+ if(dependentLibraries)
+ list(APPEND automaticDependencies ${dependentLibraries})
+ endif()
+ endif()
+ endforeach()
+
+ ## CHRIS: Temporary fix. On my system the get_target_property above returned Not-found for gtsam module
+ ## This needs to be fixed!!
+ if(UNIX AND NOT APPLE)
+ list(APPEND automaticDependencies ${Boost_SERIALIZATION_LIBRARY_RELEASE} ${Boost_FILESYSTEM_LIBRARY_RELEASE}
+ ${Boost_SYSTEM_LIBRARY_RELEASE} ${Boost_THREAD_LIBRARY_RELEASE} ${Boost_DATE_TIME_LIBRARY_RELEASE}
+ ${Boost_REGEX_LIBRARY_RELEASE})
+ if(Boost_TIMER_LIBRARY_RELEASE AND NOT GTSAM_DISABLE_NEW_TIMERS) # Only present in Boost >= 1.48.0
+ list(APPEND automaticDependencies ${Boost_TIMER_LIBRARY_RELEASE} ${Boost_CHRONO_LIBRARY_RELEASE})
+ if(GTSAM_MEX_BUILD_STATIC_MODULE)
+ #list(APPEND automaticDependencies -Wl,--no-as-needed -lrt)
+ endif()
+ endif()
+ endif()
+
+ #message("AUTOMATIC DEPENDENCIES: ${automaticDependencies}")
+ ## CHRIS: End temporary fix
+
+ # Separate dependencies
set(correctedOtherLibraries "")
set(otherLibraryTargets "")
set(otherLibraryNontargets "")
- foreach(lib ${moduleName} ${linkLibraries})
- if(TARGET ${lib})
- list(APPEND correctedOtherLibraries ${lib})
- list(APPEND otherLibraryTargets ${lib})
- elseif(TARGET ${lib}-shared) # Prefer the shared library if we have both shared and static)
- list(APPEND correctedOtherLibraries ${lib}-shared)
- list(APPEND otherLibraryTargets ${lib}-shared)
- elseif(TARGET ${lib}-static)
- list(APPEND correctedOtherLibraries ${lib}-static)
- list(APPEND otherLibraryTargets ${lib}-static)
+ set(otherSourcesAndObjects "")
+ foreach(lib ${moduleName} ${linkLibraries} ${automaticDependencies})
+ if(TARGET "${lib}")
+ if(GTSAM_MEX_BUILD_STATIC_MODULE)
+ get_target_property(target_sources ${lib} SOURCES)
+ list(APPEND otherSourcesAndObjects ${target_sources})
+ else()
+ list(APPEND correctedOtherLibraries ${lib})
+ list(APPEND otherLibraryTargets ${lib})
+ endif()
else()
- list(APPEND correctedOtherLibraries ${lib})
- list(APPEND otherLibraryNontargets ${lib})
+ get_filename_component(file_extension "${lib}" EXT)
+ get_filename_component(lib_name "${lib}" NAME_WE)
+ if(file_extension STREQUAL ".a" AND GTSAM_MEX_BUILD_STATIC_MODULE)
+ # For building a static MEX module, unpack the static library
+ # and compile its object files into our module
+ file(MAKE_DIRECTORY "${generated_files_path}/${lib_name}_objects")
+ execute_process(COMMAND ar -x "${lib}"
+ WORKING_DIRECTORY "${generated_files_path}/${lib_name}_objects"
+ RESULT_VARIABLE ar_result)
+ if(NOT ar_result EQUAL 0)
+ message(FATAL_ERROR "Failed extracting ${lib}")
+ endif()
+
+ # Get list of object files
+ execute_process(COMMAND ar -t "${lib}"
+ OUTPUT_VARIABLE object_files
+ RESULT_VARIABLE ar_result)
+ if(NOT ar_result EQUAL 0)
+ message(FATAL_ERROR "Failed listing ${lib}")
+ endif()
+
+ # Add directory to object files
+ string(REPLACE "\n" ";" object_files_list "${object_files}")
+ foreach(object_file ${object_files_list})
+ get_filename_component(file_extension "${object_file}" EXT)
+ if(file_extension STREQUAL ".o")
+ list(APPEND otherSourcesAndObjects "${generated_files_path}/${lib_name}_objects/${object_file}")
+ endif()
+ endforeach()
+ else()
+ list(APPEND correctedOtherLibraries ${lib})
+ list(APPEND otherLibraryNontargets ${lib})
+ endif()
endif()
endforeach()
@@ -144,7 +207,7 @@ function(wrap_library_internal interfaceHeader linkLibraries extraIncludeDirs ex
file(MAKE_DIRECTORY "${generated_files_path}")
add_custom_command(
OUTPUT ${generated_cpp_file}
- DEPENDS ${interfaceHeader} wrap ${module_library_target} ${otherLibraryTargets}
+ DEPENDS ${interfaceHeader} wrap ${module_library_target} ${otherLibraryTargets} ${otherSourcesAndObjects}
COMMAND
wrap
${modulePath}
@@ -157,7 +220,7 @@ function(wrap_library_internal interfaceHeader linkLibraries extraIncludeDirs ex
# Set up building of mex module
string(REPLACE ";" " " extraMexFlagsSpaced "${extraMexFlags}")
string(REPLACE ";" " " mexFlagsSpaced "${GTSAM_BUILD_MEX_BINARY_FLAGS}")
- add_library(${moduleName}_wrapper MODULE ${generated_cpp_file} ${interfaceHeader})
+ add_library(${moduleName}_wrapper MODULE ${generated_cpp_file} ${interfaceHeader} ${otherSourcesAndObjects})
target_link_libraries(${moduleName}_wrapper ${correctedOtherLibraries})
set_target_properties(${moduleName}_wrapper PROPERTIES
OUTPUT_NAME "${moduleName}_wrapper"
diff --git a/examples/Data/dubrovnik-3-7-pre-rewritten.txt b/examples/Data/dubrovnik-3-7-pre-rewritten.txt
deleted file mode 100644
index 12c9f4db4..000000000
--- a/examples/Data/dubrovnik-3-7-pre-rewritten.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-3 7 19
-
-0 0 -385.989990234375 387.1199951171875
-1 0 -38.439998626708984375 492.1199951171875
-2 0 -667.91998291015625 123.1100006103515625
-0 1 383.8800048828125 -15.299989700317382812
-1 1 559.75 -106.15000152587890625
-0 2 591.54998779296875 136.44000244140625
-1 2 863.8599853515625 -23.469970703125
-2 2 494.720001220703125 112.51999664306640625
-0 3 592.5 125.75
-1 3 861.08001708984375 -35.219970703125
-2 3 498.540008544921875 101.55999755859375
-0 4 348.720001220703125 558.3800048828125
-1 4 776.030029296875 483.529998779296875
-2 4 7.7800288200378417969 326.350006103515625
-0 5 14.010009765625 96.420013427734375
-1 5 207.1300048828125 118.3600006103515625
-0 6 202.7599945068359375 340.989990234375
-1 6 543.18011474609375 294.80999755859375
-2 6 -58.419979095458984375 110.8300018310546875
-
-0.29656188120312942935
--0.035318354384285870207
-0.31252101755032046793
-0.47230274932665988752
--0.3572340863744113415
--2.0517704282499575896
-1430.031982421875
--7.5572756941255647689e-08
-3.2377570134516087119e-14
-
-0.28532097381985194184
--0.27699838370789808817
-0.048601169984112867206
--1.2598695987143850861
--0.049063798188844320869
--1.9586867140445654023
-1432.137451171875
--7.3171918302250560373e-08
-3.1759419042137054801e-14
-
-0.057491325683772541433
-0.34853090049579965592
-0.47985129303736057116
-8.1963904289063389541
-6.5146840788718787252
--3.8392804395897406344
-1572.047119140625
--1.5962623223231275915e-08
--1.6507904730136101212e-14
-
--11.317351620610928364
-3.3594874875767186673
--42.755222607849105998
-
-4.2648515634753199066
--8.4629358700849355301
--22.252086323427270997
-
-10.996977688149536689
--9.2123370180278048025
--29.206739014051372294
-
-10.935342607054865383
--9.4338917557810741954
--29.112263909175499776
-
-15.714024935401759819
-1.3745079651566265433
--59.286834979937104606
-
--1.3624227800805182031
--4.1979357415396094666
--21.034430148188398846
-
-6.7690173115899296974
--4.7352452433700786827
--53.605307875695892506
-
diff --git a/examples/Data/noisyToyGraph.txt b/examples/Data/noisyToyGraph.txt
new file mode 100644
index 000000000..747f8da1c
--- /dev/null
+++ b/examples/Data/noisyToyGraph.txt
@@ -0,0 +1,9 @@
+VERTEX_SE2 0 0.000000 0.000000 0.000000
+VERTEX_SE2 1 0.774115 1.183389 1.576173
+VERTEX_SE2 2 -0.262420 2.047059 -3.127594
+VERTEX_SE2 3 -1.605649 0.993891 -1.561134
+EDGE_SE2 0 1 0.774115 1.183389 1.576173 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 1 2 0.869231 1.031877 1.579418 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 2 3 1.357840 1.034262 1.566460 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 2 0 0.303492 1.865011 -3.113898 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 0 3 -0.928526 0.993695 -1.563542 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
diff --git a/examples/Data/optimizedNoisyToyGraph.txt b/examples/Data/optimizedNoisyToyGraph.txt
new file mode 100644
index 000000000..217a76fb1
--- /dev/null
+++ b/examples/Data/optimizedNoisyToyGraph.txt
@@ -0,0 +1,9 @@
+VERTEX_SE2 0 0.000000 -0.000000 0.000000
+VERTEX_SE2 1 0.955797 1.137643 1.543041
+VERTEX_SE2 2 0.129867 1.989651 3.201259
+VERTEX_SE2 3 -1.047715 0.933789 4.743682
+EDGE_SE2 0 1 0.774115 1.183389 1.576173 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 1 2 0.869231 1.031877 1.579418 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 2 3 1.357840 1.034262 1.566460 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 2 0 0.303492 1.865011 -3.113898 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 0 3 -0.928526 0.993695 -1.563542 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
diff --git a/examples/Data/orientationsNoisyToyGraph.txt b/examples/Data/orientationsNoisyToyGraph.txt
new file mode 100644
index 000000000..ada372a8e
--- /dev/null
+++ b/examples/Data/orientationsNoisyToyGraph.txt
@@ -0,0 +1,9 @@
+VERTEX_SE2 0 0.000000 0.000000 0.000000
+VERTEX_SE2 1 0.000000 0.000000 1.565449
+VERTEX_SE2 2 0.000000 0.000000 3.134143
+VERTEX_SE2 3 0.000000 0.000000 4.710123
+EDGE_SE2 0 1 0.774115 1.183389 1.576173 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 1 2 0.869231 1.031877 1.579418 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 2 3 1.357840 1.034262 1.566460 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 2 0 0.303492 1.865011 -3.113898 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
+EDGE_SE2 0 3 -0.928526 0.993695 -1.563542 1.000000 0.000000 0.000000 1.000000 0.000000 1.000000
diff --git a/examples/Data/pose2example.txt b/examples/Data/pose2example.txt
new file mode 100644
index 000000000..c6c69c881
--- /dev/null
+++ b/examples/Data/pose2example.txt
@@ -0,0 +1,23 @@
+VERTEX_SE2 0 0.000000 0.000000 0.000000
+VERTEX_SE2 1 1.030390 0.011350 -0.081596
+VERTEX_SE2 2 2.036137 -0.129733 -0.301887
+VERTEX_SE2 3 3.015097 -0.442395 -0.345514
+VERTEX_SE2 4 3.343949 0.506678 1.214715
+VERTEX_SE2 5 3.684491 1.464049 1.183785
+VERTEX_SE2 6 4.064626 2.414783 1.176333
+VERTEX_SE2 7 4.429778 3.300180 1.259169
+VERTEX_SE2 8 4.128877 2.321481 -1.825391
+VERTEX_SE2 9 3.884653 1.327509 -1.953016
+VERTEX_SE2 10 3.531067 0.388263 -2.148934
+EDGE_SE2 0 1 1.030390 0.011350 -0.081596 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 1 2 1.013900 -0.058639 -0.220291 44.721360 -0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 2 3 1.027650 -0.007456 -0.043627 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 3 4 -0.012016 1.004360 1.560229 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 4 5 1.016030 0.014565 -0.030930 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 5 6 1.023890 0.006808 -0.007452 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 6 7 0.957734 0.003159 0.082836 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 7 8 -1.023820 -0.013668 -3.084560 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 8 9 1.023440 0.013984 -0.127624 44.721360 -0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 9 10 1.003350 0.022250 -0.195918 44.721360 0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 5 9 0.033943 0.032439 3.073637 44.721360 -0.000000 0.000000 44.721360 0.000000 30.901699
+EDGE_SE2 3 10 0.044020 0.988477 -1.553511 44.721360 -0.000000 0.000000 44.721360 0.000000 30.901699
\ No newline at end of file
diff --git a/examples/Pose2SLAMExample_g2o.cpp b/examples/Pose2SLAMExample_g2o.cpp
new file mode 100644
index 000000000..3a4ee9cff
--- /dev/null
+++ b/examples/Pose2SLAMExample_g2o.cpp
@@ -0,0 +1,62 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file Pose2SLAMExample_g2o.cpp
+ * @brief A 2D Pose SLAM example that reads input from g2o, converts it to a factor graph and does the
+ * optimization. Output is written on a file, in g2o format
+ * Syntax for the script is ./Pose2SLAMExample_g2o input.g2o output.g2o
+ * @date May 15, 2014
+ * @author Luca Carlone
+ */
+
+#include
+#include
+#include
+#include
+
+using namespace std;
+using namespace gtsam;
+
+int main(const int argc, const char *argv[]) {
+
+ // Read graph from file
+ string g2oFile;
+ if (argc < 2)
+ g2oFile = "../../examples/Data/noisyToyGraph.txt";
+ else
+ g2oFile = argv[1];
+
+ NonlinearFactorGraph::shared_ptr graph;
+ Values::shared_ptr initial;
+ boost::tie(graph, initial) = readG2o(g2oFile);
+
+ // Add prior on the pose having index (key) = 0
+ NonlinearFactorGraph graphWithPrior = *graph;
+ noiseModel::Diagonal::shared_ptr priorModel = //
+ noiseModel::Diagonal::Variances((Vector(3) << 1e-6, 1e-6, 1e-8));
+ graphWithPrior.add(PriorFactor(0, Pose2(), priorModel));
+
+ std::cout << "Optimizing the factor graph" << std::endl;
+ GaussNewtonOptimizer optimizer(graphWithPrior, *initial);
+ Values result = optimizer.optimize();
+ std::cout << "Optimization complete" << std::endl;
+
+ if (argc < 3) {
+ result.print("result");
+ } else {
+ const string outputFile = argv[2];
+ std::cout << "Writing results to file: " << outputFile << std::endl;
+ writeG2o(*graph, result, outputFile);
+ std::cout << "done! " << std::endl;
+ }
+ return 0;
+}
diff --git a/examples/Pose2SLAMExample_lago.cpp b/examples/Pose2SLAMExample_lago.cpp
new file mode 100644
index 000000000..1f5d80d7b
--- /dev/null
+++ b/examples/Pose2SLAMExample_lago.cpp
@@ -0,0 +1,64 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file Pose2SLAMExample_lago.cpp
+ * @brief A 2D Pose SLAM example that reads input from g2o, and solve the Pose2 problem
+ * using LAGO (Linear Approximation for Graph Optimization). See class lago.h
+ * Output is written on a file, in g2o format
+ * Syntax for the script is ./Pose2SLAMExample_lago input.g2o output.g2o
+ * @date May 15, 2014
+ * @author Luca Carlone
+ */
+
+#include
+#include
+#include
+#include
+
+using namespace std;
+using namespace gtsam;
+
+int main(const int argc, const char *argv[]) {
+
+ // Read graph from file
+ string g2oFile;
+ if (argc < 2)
+ g2oFile = "../../examples/Data/noisyToyGraph.txt";
+ else
+ g2oFile = argv[1];
+
+ NonlinearFactorGraph::shared_ptr graph;
+ Values::shared_ptr initial;
+ boost::tie(graph, initial) = readG2o(g2oFile);
+
+ // Add prior on the pose having index (key) = 0
+ NonlinearFactorGraph graphWithPrior = *graph;
+ noiseModel::Diagonal::shared_ptr priorModel = //
+ noiseModel::Diagonal::Variances((Vector(3) << 1e-6, 1e-6, 1e-8));
+ graphWithPrior.add(PriorFactor(0, Pose2(), priorModel));
+ graphWithPrior.print();
+
+ std::cout << "Computing LAGO estimate" << std::endl;
+ Values estimateLago = lago::initialize(graphWithPrior);
+ std::cout << "done!" << std::endl;
+
+ if (argc < 3) {
+ estimateLago.print("estimateLago");
+ } else {
+ const string outputFile = argv[2];
+ std::cout << "Writing results to file: " << outputFile << std::endl;
+ writeG2o(*graph, estimateLago, outputFile);
+ std::cout << "done! " << std::endl;
+ }
+
+ return 0;
+}
diff --git a/examples/SFMExample_SmartFactor.cpp b/examples/SFMExample_SmartFactor.cpp
new file mode 100644
index 000000000..904919d42
--- /dev/null
+++ b/examples/SFMExample_SmartFactor.cpp
@@ -0,0 +1,168 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file SFMExample_SmartFactor.cpp
+ * @brief A structure-from-motion problem on a simulated dataset, using smart projection factor
+ * @author Duy-Nguyen Ta
+ * @author Jing Dong
+ */
+
+/**
+ * A structure-from-motion example with landmarks
+ * - The landmarks form a 10 meter cube
+ * - The robot rotates around the landmarks, always facing towards the cube
+ */
+
+// For loading the data
+#include "SFMdata.h"
+
+// Camera observations of landmarks (i.e. pixel coordinates) will be stored as Point2 (x, y).
+#include
+
+// Each variable in the system (poses and landmarks) must be identified with a unique key.
+// We can either use simple integer keys (1, 2, 3, ...) or symbols (X1, X2, L1).
+// Here we will use Symbols
+#include
+
+// In GTSAM, measurement functions are represented as 'factors'.
+// The factor we used here is SmartProjectionPoseFactor. Every smart factor represent a single landmark,
+// The SmartProjectionPoseFactor only optimize the pose of camera, not the calibration,
+// The calibration should be known.
+#include
+
+// Also, we will initialize the robot at some location using a Prior factor.
+#include
+
+// When the factors are created, we will add them to a Factor Graph. As the factors we are using
+// are nonlinear factors, we will need a Nonlinear Factor Graph.
+#include
+
+// Finally, once all of the factors have been added to our factor graph, we will want to
+// solve/optimize to graph to find the best (Maximum A Posteriori) set of variable values.
+// GTSAM includes several nonlinear optimizers to perform this step. Here we will use a
+// trust-region method known as Powell's Degleg
+#include
+
+// The nonlinear solvers within GTSAM are iterative solvers, meaning they linearize the
+// nonlinear functions around an initial linearization point, then solve the linear system
+// to update the linearization point. This happens repeatedly until the solver converges
+// to a consistent set of variable values. This requires us to specify an initial guess
+// for each variable, held in a Values container.
+#include
+
+#include
+
+using namespace std;
+using namespace gtsam;
+
+// Make the typename short so it looks much cleaner
+typedef gtsam::SmartProjectionPoseFactor
+ SmartFactor;
+
+/* ************************************************************************* */
+int main(int argc, char* argv[]) {
+
+ // Define the camera calibration parameters
+ Cal3_S2::shared_ptr K(new Cal3_S2(50.0, 50.0, 0.0, 50.0, 50.0));
+
+ // Define the camera observation noise model
+ noiseModel::Isotropic::shared_ptr measurementNoise = noiseModel::Isotropic::Sigma(2, 1.0); // one pixel in u and v
+
+ // Create the set of ground-truth landmarks
+ vector points = createPoints();
+
+ // Create the set of ground-truth poses
+ vector poses = createPoses();
+
+ // Create a factor graph
+ NonlinearFactorGraph graph;
+
+ // A vector saved all Smart factors (for get landmark position after optimization)
+ vector smartfactors_ptr;
+
+ // Simulated measurements from each camera pose, adding them to the factor graph
+ for (size_t i = 0; i < points.size(); ++i) {
+
+ // every landmark represent a single landmark, we use shared pointer to init the factor, and then insert measurements.
+ SmartFactor::shared_ptr smartfactor(new SmartFactor());
+
+ for (size_t j = 0; j < poses.size(); ++j) {
+
+ // generate the 2D measurement
+ SimpleCamera camera(poses[j], *K);
+ Point2 measurement = camera.project(points[i]);
+
+ // call add() function to add measurment into a single factor, here we need to add:
+ // 1. the 2D measurement
+ // 2. the corresponding camera's key
+ // 3. camera noise model
+ // 4. camera calibration
+ smartfactor->add(measurement, Symbol('x', j), measurementNoise, K);
+ }
+
+ // save smartfactors to get landmark position
+ smartfactors_ptr.push_back(smartfactor);
+
+ // insert the smart factor in the graph
+ graph.push_back(smartfactor);
+ }
+
+ // Add a prior on pose x0. This indirectly specifies where the origin is.
+ noiseModel::Diagonal::shared_ptr poseNoise = noiseModel::Diagonal::Sigmas((Vector(6) << Vector3::Constant(0.3), Vector3::Constant(0.1))); // 30cm std on x,y,z 0.1 rad on roll,pitch,yaw
+ graph.push_back(PriorFactor(Symbol('x', 0), poses[0], poseNoise)); // add directly to graph
+
+ // Because the structure-from-motion problem has a scale ambiguity, the problem is still under-constrained
+ // Here we add a prior on the second pose x1, so this will fix the scale by indicating the distance between x0 and x1.
+ // Because these two are fixed, the rest poses will be alse fixed.
+ graph.push_back(PriorFactor(Symbol('x', 1), poses[1], poseNoise)); // add directly to graph
+
+ graph.print("Factor Graph:\n");
+
+ // Create the data structure to hold the initial estimate to the solution
+ // Intentionally initialize the variables off from the ground truth
+ Values initialEstimate;
+ for (size_t i = 0; i < poses.size(); ++i)
+ initialEstimate.insert(Symbol('x', i), poses[i].compose(Pose3(Rot3::rodriguez(-0.1, 0.2, 0.25), Point3(0.05, -0.10, 0.20))));
+ initialEstimate.print("Initial Estimates:\n");
+
+ // Optimize the graph and print results
+ Values result = DoglegOptimizer(graph, initialEstimate).optimize();
+ result.print("Final results:\n");
+
+
+ // Notice: Smart factor represent the 3D point as a factor, not a variable.
+ // The 3D position of the landmark is not explicitly calculated by the optimizer.
+ // If you do want to output the landmark's 3D position, you should use the internal function point()
+ // of the smart factor to get the 3D point.
+ Values landmark_result;
+ for (size_t i = 0; i < points.size(); ++i) {
+
+ // The output of point() is in boost::optional, since sometimes
+ // the triangulation opterations inside smart factor will encounter degeneracy.
+ // Check the manual of boost::optional for more details for the usages.
+ boost::optional point;
+
+ // here we use the saved smart factors to call, pass in all optimized pose to calculate landmark positions
+ point = smartfactors_ptr.at(i)->point(result);
+
+ // ignore if boost::optional return NULL
+ if (point)
+ landmark_result.insert(Symbol('l', i), *point);
+ }
+
+ landmark_result.print("Landmark results:\n");
+
+
+ return 0;
+}
+/* ************************************************************************* */
+
diff --git a/examples/StereoVOExample.cpp b/examples/StereoVOExample.cpp
new file mode 100644
index 000000000..8a04d13a3
--- /dev/null
+++ b/examples/StereoVOExample.cpp
@@ -0,0 +1,75 @@
+/* ----------------------------------------------------------------------------
+
+ * GTSAM Copyright 2010, Georgia Tech Research Corporation,
+ * Atlanta, Georgia 30332-0415
+ * All Rights Reserved
+ * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+ * See LICENSE for the license information
+
+ * -------------------------------------------------------------------------- */
+
+/**
+ * @file SteroVOExample.cpp
+ * @brief A stereo visual odometry example
+ * @date May 25, 2014
+ * @author Stephen Camp
+ */
+
+/**
+ * A 3D stereo visual odometry example
+ * - robot starts at origin
+ * -moves forward 1 meter
+ * -takes stereo readings on three landmarks
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+
+using namespace std;
+using namespace gtsam;
+
+int main(int argc, char** argv){
+ //create graph object, add first pose at origin with key '1'
+ NonlinearFactorGraph graph;
+ Pose3 first_pose = Pose3();
+ graph.push_back(NonlinearEquality(1, first_pose));
+
+ //create factor noise model with 3 sigmas of value 1
+ const noiseModel::Isotropic::shared_ptr model = noiseModel::Isotropic::Sigma(3,1);
+ //create stereo camera calibration object with .2m between cameras
+ const Cal3_S2Stereo::shared_ptr K(new Cal3_S2Stereo(1000, 1000, 0, 320, 240, 0.2));
+ //create and add stereo factors between first pose (key value 1) and the three landmarks
+ graph.push_back(GenericStereoFactor(StereoPoint2(520, 480, 440), model, 1, 3, K));
+ graph.push_back(GenericStereoFactor(StereoPoint2(120, 80, 440), model, 1, 4, K));
+ graph.push_back(GenericStereoFactor(StereoPoint2(320, 280, 140), model, 1, 5, K));
+ //create and add stereo factors between second pose and the three landmarks
+ graph.push_back(GenericStereoFactor(StereoPoint2(570, 520, 490), model, 2, 3, K));
+ graph.push_back(GenericStereoFactor(StereoPoint2(70, 20, 490), model, 2, 4, K));
+ graph.push_back(GenericStereoFactor(StereoPoint2(320, 270, 115), model, 2, 5, K));
+ //create Values object to contain initial estimates of camera poses and landmark locations
+ Values initial_estimate = Values();
+ //create and add iniital estimates
+ initial_estimate.insert(1, first_pose);
+ initial_estimate.insert(2, Pose3(Rot3(), Point3(0.1, -0.1, 1.1)));
+ initial_estimate.insert(3, Point3(1, 1, 5));
+ initial_estimate.insert(4, Point3(-1, 1, 5));
+ initial_estimate.insert(5, Point3(0, -0.5, 5));
+ //create Levenberg-Marquardt optimizer for resulting factor graph, optimize
+ LevenbergMarquardtOptimizer optimizer = LevenbergMarquardtOptimizer(graph, initial_estimate);
+ Values result = optimizer.optimize();
+
+ result.print("Final result:\n");
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/StereoVOExample_large.cpp b/examples/StereoVOExample_large.cpp
new file mode 100644
index 000000000..b9ab663d9
--- /dev/null
+++ b/examples/StereoVOExample_large.cpp
@@ -0,0 +1,114 @@
+/* ----------------------------------------------------------------------------
+
+* GTSAM Copyright 2010, Georgia Tech Research Corporation,
+* Atlanta, Georgia 30332-0415
+* All Rights Reserved
+* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
+
+* See LICENSE for the license information
+
+* -------------------------------------------------------------------------- */
+
+/**
+* @file SteroVOExample.cpp
+* @brief A stereo visual odometry example
+* @date May 25, 2014
+* @author Stephen Camp
+*/
+
+
+/**
+ * A 3D stereo visual odometry example
+ * - robot starts at origin
+ * -moves forward, taking periodic stereo measurements
+ * -takes stereo readings of many landmarks
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+using namespace std;
+using namespace gtsam;
+
+int main(int argc, char** argv){
+
+ Values initial_estimate;
+ NonlinearFactorGraph graph;
+ const noiseModel::Isotropic::shared_ptr model = noiseModel::Isotropic::Sigma(3,1);
+
+ string calibration_loc = findExampleDataFile("VO_calibration.txt");
+ string pose_loc = findExampleDataFile("VO_camera_poses_large.txt");
+ string factor_loc = findExampleDataFile("VO_stereo_factors_large.txt");
+
+ //read camera calibration info from file
+ // focal lengths fx, fy, skew s, principal point u0, v0, baseline b
+ double fx, fy, s, u0, v0, b;
+ ifstream calibration_file(calibration_loc.c_str());
+ cout << "Reading calibration info" << endl;
+ calibration_file >> fx >> fy >> s >> u0 >> v0 >> b;
+
+ //create stereo camera calibration object
+ const Cal3_S2Stereo::shared_ptr K(new Cal3_S2Stereo(fx,fy,s,u0,v0,b));
+
+ ifstream pose_file(pose_loc.c_str());
+ cout << "Reading camera poses" << endl;
+ int pose_id;
+ MatrixRowMajor m(4,4);
+ //read camera pose parameters and use to make initial estimates of camera poses
+ while (pose_file >> pose_id) {
+ for (int i = 0; i < 16; i++) {
+ pose_file >> m.data()[i];
+ }
+ initial_estimate.insert(Symbol('x', pose_id), Pose3(m));
+ }
+
+ // camera and landmark keys
+ size_t x, l;
+
+ // pixel coordinates uL, uR, v (same for left/right images due to rectification)
+ // landmark coordinates X, Y, Z in camera frame, resulting from triangulation
+ double uL, uR, v, X, Y, Z;
+ ifstream factor_file(factor_loc.c_str());
+ cout << "Reading stereo factors" << endl;
+ //read stereo measurement details from file and use to create and add GenericStereoFactor objects to the graph representation
+ while (factor_file >> x >> l >> uL >> uR >> v >> X >> Y >> Z) {
+ graph.push_back(
+ GenericStereoFactor(StereoPoint2(uL, uR, v), model,
+ Symbol('x', x), Symbol('l', l), K));
+ //if the landmark variable included in this factor has not yet been added to the initial variable value estimate, add it
+ if (!initial_estimate.exists(Symbol('l', l))) {
+ Pose3 camPose = initial_estimate.at(Symbol('x', x));
+ //transform_from() transforms the input Point3 from the camera pose space, camPose, to the global space
+ Point3 worldPoint = camPose.transform_from(Point3(X, Y, Z));
+ initial_estimate.insert(Symbol('l', l), worldPoint);
+ }
+ }
+
+ Pose3 first_pose = initial_estimate.at(Symbol('x',1));
+ //constrain the first pose such that it cannot change from its original value during optimization
+ // NOTE: NonlinearEquality forces the optimizer to use QR rather than Cholesky
+ // QR is much slower than Cholesky, but numerically more stable
+ graph.push_back(NonlinearEquality(Symbol('x',1),first_pose));
+
+ cout << "Optimizing" << endl;
+ //create Levenberg-Marquardt optimizer to optimize the factor graph
+ LevenbergMarquardtOptimizer optimizer = LevenbergMarquardtOptimizer(graph, initial_estimate);
+ Values result = optimizer.optimize();
+
+ cout << "Final result sample:" << endl;
+ Values pose_values = result.filter();
+ pose_values.print("Final camera poses:\n");
+
+ return 0;
+}
diff --git a/gtsam.h b/gtsam.h
index 2d181d350..b7178d534 100644
--- a/gtsam.h
+++ b/gtsam.h
@@ -2249,6 +2249,13 @@ pair load2D(string filename,
pair load2D(string filename);
pair load2D_robust(string filename,
gtsam::noiseModel::Base* model);
+void save2D(const gtsam::NonlinearFactorGraph& graph,
+ const gtsam::Values& config, gtsam::noiseModel::Diagonal* model,
+ string filename);
+
+pair readG2o(string filename);
+void writeG2o(const gtsam::NonlinearFactorGraph& graph,
+ const gtsam::Values& estimate, string filename);
//*************************************************************************
// Navigation
@@ -2363,6 +2370,12 @@ virtual class CombinedImuFactor : gtsam::NonlinearFactor {
namespace utilities {
#include
+ gtsam::KeyList createKeyList(Vector I);
+ gtsam::KeyList createKeyList(string s, Vector I);
+ gtsam::KeyVector createKeyVector(Vector I);
+ gtsam::KeyVector createKeyVector(string s, Vector I);
+ gtsam::KeySet createKeySet(Vector I);
+ gtsam::KeySet createKeySet(string s, Vector I);
Matrix extractPoint2(const gtsam::Values& values);
Matrix extractPoint3(const gtsam::Values& values);
Matrix extractPose2(const gtsam::Values& values);
@@ -2375,6 +2388,8 @@ namespace utilities {
void insertProjectionFactors(gtsam::NonlinearFactorGraph& graph, size_t i, Vector J, Matrix Z, const gtsam::noiseModel::Base* model, const gtsam::Cal3_S2* K);
void insertProjectionFactors(gtsam::NonlinearFactorGraph& graph, size_t i, Vector J, Matrix Z, const gtsam::noiseModel::Base* model, const gtsam::Cal3_S2* K, const gtsam::Pose3& body_P_sensor);
Matrix reprojectionErrors(const gtsam::NonlinearFactorGraph& graph, const gtsam::Values& values);
+ gtsam::Values localToWorld(const gtsam::Values& local, const gtsam::Pose2& base);
+ gtsam::Values localToWorld(const gtsam::Values& local, const gtsam::Pose2& base, const gtsam::KeyVector& keys);
} //\namespace utilities
diff --git a/gtsam/3rdparty/CCOLAMD/Source/ccolamd.c b/gtsam/3rdparty/CCOLAMD/Source/ccolamd.c
index 36ffa1ada..1c35ffa41 100644
--- a/gtsam/3rdparty/CCOLAMD/Source/ccolamd.c
+++ b/gtsam/3rdparty/CCOLAMD/Source/ccolamd.c
@@ -617,11 +617,12 @@
#include "ccolamd.h"
-#include
#include
#include
#ifdef MATLAB_MEX_FILE
+#include
+typedef uint16_t char16_t;
#include "mex.h"
#include "matrix.h"
#endif
diff --git a/gtsam/3rdparty/CCOLAMD/Source/ccolamd_global.c b/gtsam/3rdparty/CCOLAMD/Source/ccolamd_global.c
index d43985126..e470804a6 100644
--- a/gtsam/3rdparty/CCOLAMD/Source/ccolamd_global.c
+++ b/gtsam/3rdparty/CCOLAMD/Source/ccolamd_global.c
@@ -13,6 +13,9 @@
#ifndef NPRINT
#ifdef MATLAB_MEX_FILE
+#include
+#include
+typedef uint16_t char16_t;
#include "mex.h"
int (*ccolamd_printf) (const char *, ...) = mexPrintf ;
#else
diff --git a/gtsam/3rdparty/CMakeLists.txt b/gtsam/3rdparty/CMakeLists.txt
index 430ae9515..718179866 100644
--- a/gtsam/3rdparty/CMakeLists.txt
+++ b/gtsam/3rdparty/CMakeLists.txt
@@ -16,13 +16,30 @@ if(NOT GTSAM_USE_SYSTEM_EIGEN)
endif()
endforeach(eigen_dir)
+ # do the same for the unsupported eigen folder
+ file(GLOB_RECURSE unsupported_eigen_headers "${CMAKE_CURRENT_SOURCE_DIR}/Eigen/unsupported/Eigen/*.h")
+
+ file(GLOB unsupported_eigen_dir_headers_all "Eigen/unsupported/Eigen/*")
+ foreach(unsupported_eigen_dir ${unsupported_eigen_dir_headers_all})
+ get_filename_component(filename ${unsupported_eigen_dir} NAME)
+ if (NOT ((${filename} MATCHES "CMakeLists.txt") OR (${filename} MATCHES "src") OR (${filename} MATCHES ".svn")))
+ list(APPEND unsupported_eigen_headers "${CMAKE_CURRENT_SOURCE_DIR}/Eigen/unsupported/Eigen/${filename}")
+ install(FILES Eigen/unsupported/Eigen/${filename} DESTINATION include/gtsam/3rdparty/Eigen/unsupported/Eigen)
+ endif()
+ endforeach(unsupported_eigen_dir)
+
# Add to project source
set(eigen_headers ${eigen_headers} PARENT_SCOPE)
+ # set(unsupported_eigen_headers ${unsupported_eigen_headers} PARENT_SCOPE)
# install Eigen - only the headers in our 3rdparty directory
install(DIRECTORY Eigen/Eigen
DESTINATION include/gtsam/3rdparty/Eigen
FILES_MATCHING PATTERN "*.h")
+
+ install(DIRECTORY Eigen/unsupported/Eigen
+ DESTINATION include/gtsam/3rdparty/Eigen/unsupported/
+ FILES_MATCHING PATTERN "*.h")
endif()
############ NOTE: When updating GeographicLib be sure to disable building their examples
diff --git a/gtsam/base/Matrix.cpp b/gtsam/base/Matrix.cpp
index d9ab7d71c..f51197cf2 100644
--- a/gtsam/base/Matrix.cpp
+++ b/gtsam/base/Matrix.cpp
@@ -591,41 +591,17 @@ Matrix3 skewSymmetric(double wx, double wy, double wz)
}
/* ************************************************************************* */
-/** Numerical Recipes in C wrappers
- * create Numerical Recipes in C structure
- * pointers are subtracted by one to provide base 1 access
- */
-/* ************************************************************************* */
-// FIXME: assumes row major, rather than column major
-//double** createNRC(Matrix& A) {
-// const size_t m=A.rows();
-// double** a = new double* [m];
-// for(size_t i = 0; i < m; i++)
-// a[i] = &A(i,0)-1;
-// return a;
-//}
-
-/* ******************************************
- *
- * Modified from Justin's codebase
- *
- * Idea came from other public domain code. Takes a S.P.D. matrix
- * and computes the LL^t decomposition. returns L, which is lower
- * triangular. Note this is the opposite convention from Matlab,
- * which calculates Q'Q where Q is upper triangular.
- *
- * ******************************************/
Matrix LLt(const Matrix& A)
{
- Matrix L = zeros(A.rows(), A.rows());
- Eigen::LLT llt;
- llt.compute(A);
+ Eigen::LLT llt(A);
return llt.matrixL();
}
+/* ************************************************************************* */
Matrix RtR(const Matrix &A)
{
- return LLt(A).transpose();
+ Eigen::LLT llt(A);
+ return llt.matrixU();
}
/*
@@ -633,13 +609,10 @@ Matrix RtR(const Matrix &A)
*/
Matrix cholesky_inverse(const Matrix &A)
{
- // FIXME: replace with real algorithm
- return A.inverse();
-
-// Matrix L = LLt(A);
-// Matrix inv(eye(A.rows()));
-// inplace_solve (L, inv, BNU::lower_tag ());
-// return BNU::prod(trans(inv), inv);
+ Eigen::LLT llt(A);
+ Matrix inv = eye(A.rows());
+ llt.matrixU().solveInPlace(inv);
+ return inv*inv.transpose();
}
/* ************************************************************************* */
@@ -648,9 +621,9 @@ Matrix cholesky_inverse(const Matrix &A)
// inv(B) * inv(B)' == A
// inv(B' * B) == A
Matrix inverse_square_root(const Matrix& A) {
- Matrix R = RtR(A);
+ Eigen::LLT llt(A);
Matrix inv = eye(A.rows());
- R.triangularView().solveInPlace(inv);
+ llt.matrixU().solveInPlace(inv);
return inv.transpose();
}
diff --git a/gtsam/base/SymmetricBlockMatrix.h b/gtsam/base/SymmetricBlockMatrix.h
index 45edbf6af..5dcc79a68 100644
--- a/gtsam/base/SymmetricBlockMatrix.h
+++ b/gtsam/base/SymmetricBlockMatrix.h
@@ -236,6 +236,10 @@ namespace gtsam {
friend class boost::serialization::access;
template
void serialize(ARCHIVE & ar, const unsigned int version) {
+ // Fill in the lower triangle part of the matrix, so boost::serialization won't
+ // complain about uninitialized data with an input_stream_error exception
+ // http://www.boost.org/doc/libs/1_37_0/libs/serialization/doc/exceptions.html#stream_error
+ matrix_.triangularView() = matrix_.triangularView().transpose();
ar & BOOST_SERIALIZATION_NVP(matrix_);
ar & BOOST_SERIALIZATION_NVP(variableColOffsets_);
ar & BOOST_SERIALIZATION_NVP(blockStart_);
diff --git a/gtsam/base/tests/testMatrix.cpp b/gtsam/base/tests/testMatrix.cpp
index 9d84e5ab7..4e857b143 100644
--- a/gtsam/base/tests/testMatrix.cpp
+++ b/gtsam/base/tests/testMatrix.cpp
@@ -1022,22 +1022,32 @@ TEST( matrix, inverse_square_root )
/* *********************************************************************** */
// M was generated as the covariance of a set of random numbers. L that
// we are checking against was generated via chol(M)' on octave
+namespace cholesky {
+Matrix M = (Matrix(5, 5) << 0.0874197, -0.0030860, 0.0116969, 0.0081463,
+ 0.0048741, -0.0030860, 0.0872727, 0.0183073, 0.0125325, -0.0037363,
+ 0.0116969, 0.0183073, 0.0966217, 0.0103894, -0.0021113, 0.0081463,
+ 0.0125325, 0.0103894, 0.0747324, 0.0036415, 0.0048741, -0.0037363,
+ -0.0021113, 0.0036415, 0.0909464);
+
+Matrix expected = (Matrix(5, 5) <<
+ 0.295668226226627, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000,
+ -0.010437374483502, 0.295235094820875, 0.000000000000000, 0.000000000000000, 0.000000000000000,
+ 0.039560896175007, 0.063407813693827, 0.301721866387571, 0.000000000000000, 0.000000000000000,
+ 0.027552165831157, 0.043423266737274, 0.021695600982708, 0.267613525371710, 0.000000000000000,
+ 0.016485031422565, -0.012072546984405, -0.006621889326331, 0.014405837566082, 0.300462176944247);
+}
TEST( matrix, LLt )
{
- Matrix M = (Matrix(5, 5) << 0.0874197, -0.0030860, 0.0116969, 0.0081463,
- 0.0048741, -0.0030860, 0.0872727, 0.0183073, 0.0125325, -0.0037363,
- 0.0116969, 0.0183073, 0.0966217, 0.0103894, -0.0021113, 0.0081463,
- 0.0125325, 0.0103894, 0.0747324, 0.0036415, 0.0048741, -0.0037363,
- -0.0021113, 0.0036415, 0.0909464);
+ EQUALITY(cholesky::expected, LLt(cholesky::M));
+}
+TEST( matrix, RtR )
+{
+ EQUALITY(cholesky::expected.transpose(), RtR(cholesky::M));
+}
- Matrix expected = (Matrix(5, 5) <<
- 0.295668226226627, 0.000000000000000, 0.000000000000000, 0.000000000000000, 0.000000000000000,
- -0.010437374483502, 0.295235094820875, 0.000000000000000, 0.000000000000000, 0.000000000000000,
- 0.039560896175007, 0.063407813693827, 0.301721866387571, 0.000000000000000, 0.000000000000000,
- 0.027552165831157, 0.043423266737274, 0.021695600982708, 0.267613525371710, 0.000000000000000,
- 0.016485031422565, -0.012072546984405, -0.006621889326331, 0.014405837566082, 0.300462176944247);
-
- EQUALITY(expected, LLt(M));
+TEST( matrix, cholesky_inverse )
+{
+ EQUALITY(cholesky::M.inverse(), cholesky_inverse(cholesky::M));
}
/* ************************************************************************* */
diff --git a/gtsam/geometry/Pose2.cpp b/gtsam/geometry/Pose2.cpp
index 30a4434fe..85307e322 100644
--- a/gtsam/geometry/Pose2.cpp
+++ b/gtsam/geometry/Pose2.cpp
@@ -178,6 +178,7 @@ Pose2 Pose2::between(const Pose2& p2, boost::optional H1,
// Calculate delta translation = unrotate(R1, dt);
Point2 dt = p2.t() - t_;
double x = dt.x(), y = dt.y();
+ // t = R1' * (t2-t1)
Point2 t(c1 * x + s1 * y, -s1 * x + c1 * y);
// FD: This is just -AdjointMap(between(p2,p1)) inlined and re-using above
diff --git a/gtsam_unstable/geometry/TriangulationFactor.h b/gtsam/geometry/TriangulationFactor.h
similarity index 100%
rename from gtsam_unstable/geometry/TriangulationFactor.h
rename to gtsam/geometry/TriangulationFactor.h
diff --git a/gtsam/geometry/Unit3.cpp b/gtsam/geometry/Unit3.cpp
index 88359e0f8..db4c8da83 100644
--- a/gtsam/geometry/Unit3.cpp
+++ b/gtsam/geometry/Unit3.cpp
@@ -151,9 +151,9 @@ Vector Unit3::localCoordinates(const Unit3& y) const {
double dot = p.dot(q);
// Check for special cases
- if (std::abs(dot - 1.0) < 1e-20)
+ if (std::abs(dot - 1.0) < 1e-16)
return (Vector(2) << 0, 0);
- else if (std::abs(dot + 1.0) < 1e-20)
+ else if (std::abs(dot + 1.0) < 1e-16)
return (Vector(2) << M_PI, 0);
else {
// no special case
diff --git a/gtsam/geometry/tests/testPose2.cpp b/gtsam/geometry/tests/testPose2.cpp
index a5646f647..42b548f5a 100644
--- a/gtsam/geometry/tests/testPose2.cpp
+++ b/gtsam/geometry/tests/testPose2.cpp
@@ -398,7 +398,7 @@ TEST( Pose2, matrix )
TEST( Pose2, compose_matrix )
{
Pose2 gT1(M_PI/2.0, Point2(1,2)); // robot at (1,2) looking towards y
- Pose2 _1T2(M_PI, Point2(-1,4)); // local robot at (-1,4) loooking at negative x
+ Pose2 _1T2(M_PI, Point2(-1,4)); // local robot at (-1,4) looking at negative x
Matrix gM1(matrix(gT1)),_1M2(matrix(_1T2));
EXPECT(assert_equal(gM1*_1M2,matrix(gT1.compose(_1T2)))); // RIGHT DOES NOT
}
@@ -412,7 +412,7 @@ TEST( Pose2, between )
//
// *--0--*--*
Pose2 gT1(M_PI/2.0, Point2(1,2)); // robot at (1,2) looking towards y
- Pose2 gT2(M_PI, Point2(-1,4)); // robot at (-1,4) loooking at negative x
+ Pose2 gT2(M_PI, Point2(-1,4)); // robot at (-1,4) looking at negative x
Matrix actualH1,actualH2;
Pose2 expected(M_PI/2.0, Point2(2,2));
diff --git a/gtsam_unstable/geometry/tests/testTriangulation.cpp b/gtsam/geometry/tests/testTriangulation.cpp
similarity index 99%
rename from gtsam_unstable/geometry/tests/testTriangulation.cpp
rename to gtsam/geometry/tests/testTriangulation.cpp
index 8d45311f1..fb05bcf9f 100644
--- a/gtsam_unstable/geometry/tests/testTriangulation.cpp
+++ b/gtsam/geometry/tests/testTriangulation.cpp
@@ -16,7 +16,7 @@
* Author: cbeall3
*/
-#include
+#include
#include
#include
diff --git a/gtsam/geometry/tests/testSphere2.cpp b/gtsam/geometry/tests/testUnit3.cpp
similarity index 100%
rename from gtsam/geometry/tests/testSphere2.cpp
rename to gtsam/geometry/tests/testUnit3.cpp
diff --git a/gtsam_unstable/geometry/triangulation.cpp b/gtsam/geometry/triangulation.cpp
similarity index 98%
rename from gtsam_unstable/geometry/triangulation.cpp
rename to gtsam/geometry/triangulation.cpp
index 3017fdf7a..9e1575801 100644
--- a/gtsam_unstable/geometry/triangulation.cpp
+++ b/gtsam/geometry/triangulation.cpp
@@ -16,7 +16,7 @@
* @author Chris Beall
*/
-#include
+#include
#include
#include
diff --git a/gtsam_unstable/geometry/triangulation.h b/gtsam/geometry/triangulation.h
similarity index 97%
rename from gtsam_unstable/geometry/triangulation.h
rename to gtsam/geometry/triangulation.h
index f767514c1..6899c616a 100644
--- a/gtsam_unstable/geometry/triangulation.h
+++ b/gtsam/geometry/triangulation.h
@@ -18,8 +18,8 @@
#pragma once
-#include
-#include
+
+#include
#include
#include
#include
@@ -52,7 +52,7 @@ public:
* @param rank_tol SVD rank tolerance
* @return Triangulated Point3
*/
-GTSAM_UNSTABLE_EXPORT Point3 triangulateDLT(
+GTSAM_EXPORT Point3 triangulateDLT(
const std::vector& projection_matrices,
const std::vector& measurements, double rank_tol);
@@ -120,7 +120,7 @@ std::pair triangulationGraph(
* @param landmarkKey to refer to landmark
* @return refined Point3
*/
-GTSAM_UNSTABLE_EXPORT Point3 optimize(const NonlinearFactorGraph& graph,
+GTSAM_EXPORT Point3 optimize(const NonlinearFactorGraph& graph,
const Values& values, Key landmarkKey);
/**
diff --git a/gtsam/inference/VariableIndex-inl.h b/gtsam/inference/VariableIndex-inl.h
index a88ea5d57..82eb85c76 100644
--- a/gtsam/inference/VariableIndex-inl.h
+++ b/gtsam/inference/VariableIndex-inl.h
@@ -23,43 +23,35 @@ namespace gtsam {
/* ************************************************************************* */
template
-void VariableIndex::augment(const FG& factors, boost::optional&> newFactorIndices)
-{
+void VariableIndex::augment(const FG& factors,
+ boost::optional&> newFactorIndices) {
gttic(VariableIndex_augment);
// Augment index for each factor
- for(size_t i = 0; i < factors.size(); ++i)
- {
- if(factors[i])
- {
+ for (size_t i = 0; i < factors.size(); ++i) {
+ if (factors[i]) {
const size_t globalI =
- newFactorIndices ?
- (*newFactorIndices)[i] :
- nFactors_;
- BOOST_FOREACH(const Key key, *factors[i])
- {
+ newFactorIndices ? (*newFactorIndices)[i] : nFactors_;
+ BOOST_FOREACH(const Key key, *factors[i]) {
index_[key].push_back(globalI);
- ++ nEntries_;
+ ++nEntries_;
}
}
// Increment factor count even if factors are null, to keep indices consistent
- if(newFactorIndices)
- {
- if((*newFactorIndices)[i] >= nFactors_)
+ if (newFactorIndices) {
+ if ((*newFactorIndices)[i] >= nFactors_)
nFactors_ = (*newFactorIndices)[i] + 1;
- }
- else
- {
- ++ nFactors_;
+ } else {
+ ++nFactors_;
}
}
}
/* ************************************************************************* */
template
-void VariableIndex::remove(ITERATOR firstFactor, ITERATOR lastFactor, const FG& factors)
-{
+void VariableIndex::remove(ITERATOR firstFactor, ITERATOR lastFactor,
+ const FG& factors) {
gttic(VariableIndex_remove);
// NOTE: We intentionally do not decrement nFactors_ because the factor
@@ -68,17 +60,20 @@ void VariableIndex::remove(ITERATOR firstFactor, ITERATOR lastFactor, const FG&
// one greater than the highest-numbered factor referenced in a VariableIndex.
ITERATOR factorIndex = firstFactor;
size_t i = 0;
- for( ; factorIndex != lastFactor; ++factorIndex, ++i) {
- if(i >= factors.size())
- throw std::invalid_argument("Internal error, requested inconsistent number of factor indices and factors in VariableIndex::remove");
- if(factors[i]) {
+ for (; factorIndex != lastFactor; ++factorIndex, ++i) {
+ if (i >= factors.size())
+ throw std::invalid_argument(
+ "Internal error, requested inconsistent number of factor indices and factors in VariableIndex::remove");
+ if (factors[i]) {
BOOST_FOREACH(Key j, *factors[i]) {
Factors& factorEntries = internalAt(j);
- Factors::iterator entry = std::find(factorEntries.begin(), factorEntries.end(), *factorIndex);
- if(entry == factorEntries.end())
- throw std::invalid_argument("Internal error, indices and factors passed into VariableIndex::remove are not consistent with the existing variable index");
+ Factors::iterator entry = std::find(factorEntries.begin(),
+ factorEntries.end(), *factorIndex);
+ if (entry == factorEntries.end())
+ throw std::invalid_argument(
+ "Internal error, indices and factors passed into VariableIndex::remove are not consistent with the existing variable index");
factorEntries.erase(entry);
- -- nEntries_;
+ --nEntries_;
}
}
}
@@ -87,10 +82,11 @@ void VariableIndex::remove(ITERATOR firstFactor, ITERATOR lastFactor, const FG&
/* ************************************************************************* */
template
void VariableIndex::removeUnusedVariables(ITERATOR firstKey, ITERATOR lastKey) {
- for(ITERATOR key = firstKey; key != lastKey; ++key) {
+ for (ITERATOR key = firstKey; key != lastKey; ++key) {
KeyMap::iterator entry = index_.find(*key);
- if(!entry->second.empty())
- throw std::invalid_argument("Asking to remove variables from the variable index that are not unused");
+ if (!entry->second.empty())
+ throw std::invalid_argument(
+ "Asking to remove variables from the variable index that are not unused");
index_.erase(entry);
}
}
diff --git a/gtsam/inference/graph-inl.h b/gtsam/inference/graph-inl.h
index 48ee88255..90c7b32c9 100644
--- a/gtsam/inference/graph-inl.h
+++ b/gtsam/inference/graph-inl.h
@@ -22,7 +22,7 @@
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunneeded-internal-declaration"
+//#pragma GCC diagnostic ignored "-Wunneeded-internal-declaration"
#endif
#include
#ifdef __GNUC__
@@ -73,34 +73,41 @@ SDGraph toBoostGraph(const G& graph) {
SDGraph g;
typedef typename boost::graph_traits >::vertex_descriptor BoostVertex;
std::map key2vertex;
- BoostVertex v1, v2;
typename G::const_iterator itFactor;
+ // Loop over the factors
for(itFactor=graph.begin(); itFactor!=graph.end(); itFactor++) {
- if ((*itFactor)->keys().size() > 2)
- throw(std::invalid_argument("toBoostGraph: only support factors with at most two keys"));
- if ((*itFactor)->keys().size() == 1)
+ // Ignore factors that are not binary
+ if ((*itFactor)->keys().size() != 2)
continue;
+ // Cast the factor to the user-specified factor type F
boost::shared_ptr factor = boost::dynamic_pointer_cast(*itFactor);
+ // Ignore factors that are not of type F
if (!factor) continue;
- KEY key1 = factor->key1();
- KEY key2 = factor->key2();
+ // Retrieve the 2 keys (nodes) the factor (edge) is incident on
+ KEY key1 = factor->keys()[0];
+ KEY key2 = factor->keys()[1];
+ BoostVertex v1, v2;
+
+ // If key1 is a new key, add it to the key2vertex map, else get the corresponding vertex id
if (key2vertex.find(key1) == key2vertex.end()) {
v1 = add_vertex(key1, g);
- key2vertex.insert(make_pair(key1, v1));
+ key2vertex.insert(std::pair(key1, v1));
} else
v1 = key2vertex[key1];
+ // If key2 is a new key, add it to the key2vertex map, else get the corresponding vertex id
if (key2vertex.find(key2) == key2vertex.end()) {
v2 = add_vertex(key2, g);
- key2vertex.insert(make_pair(key2, v2));
+ key2vertex.insert(std::pair(key2, v2));
} else
v2 = key2vertex[key2];
+ // Add an edge with weight 1.0
boost::property edge_property(1.0); // assume constant edge weight here
boost::add_edge(v1, v2, edge_property, g);
}
@@ -222,12 +229,11 @@ boost::shared_ptr composePoses(const G& graph, const PredecessorMap
return config;
}
-/* ************************************************************************* */
-
/* ************************************************************************* */
template
PredecessorMap findMinimumSpanningTree(const G& fg) {
+ // Convert to a graph that boost understands
SDGraph g = gtsam::toBoostGraph(fg);
// find minimum spanning tree
@@ -237,13 +243,12 @@ PredecessorMap findMinimumSpanningTree(const G& fg) {
// convert edge to string pairs
PredecessorMap tree;
typename SDGraph::vertex_iterator itVertex = boost::vertices(g).first;
- typename std::vector::Vertex>::iterator vi;
- for (vi = p_map.begin(); vi != p_map.end(); itVertex++, vi++) {
+ BOOST_FOREACH(const typename SDGraph::Vertex& vi, p_map){
KEY key = boost::get(boost::vertex_name, g, *itVertex);
- KEY parent = boost::get(boost::vertex_name, g, *vi);
+ KEY parent = boost::get(boost::vertex_name, g, vi);
tree.insert(key, parent);
+ itVertex++;
}
-
return tree;
}
diff --git a/gtsam/linear/GaussianFactor.h b/gtsam/linear/GaussianFactor.h
index 543822ce0..8c8e2cb2b 100644
--- a/gtsam/linear/GaussianFactor.h
+++ b/gtsam/linear/GaussianFactor.h
@@ -130,6 +130,9 @@ namespace gtsam {
/// A'*b for Jacobian, eta for Hessian
virtual VectorValues gradientAtZero() const = 0;
+ /// A'*b for Jacobian, eta for Hessian (raw memory version)
+ virtual void gradientAtZero(double* d) const = 0;
+
private:
/** Serialization function */
friend class boost::serialization::access;
diff --git a/gtsam/linear/GaussianFactorGraph.cpp b/gtsam/linear/GaussianFactorGraph.cpp
index b56270a55..d6836783b 100644
--- a/gtsam/linear/GaussianFactorGraph.cpp
+++ b/gtsam/linear/GaussianFactorGraph.cpp
@@ -70,11 +70,18 @@ namespace gtsam {
vector dims_accumulated;
dims_accumulated.resize(dims.size()+1,0);
dims_accumulated[0]=0;
- for (int i=1; i DVector;
+ typedef Eigen::Map DMap;
+
+ // Loop over all variables in the factor
+ for (DenseIndex pos = 0; pos < (DenseIndex)size(); ++pos) {
+ Key j = keys_[pos];
+ // Get the diagonal block, and insert its diagonal
+ DVector dj = -info_(pos,size()).knownOffDiagonal();
+ DMap(d + 9 * j) += dj;
+ }
+}
+
/* ************************************************************************* */
std::pair, boost::shared_ptr >
EliminateCholesky(const GaussianFactorGraph& factors, const Ordering& keys)
diff --git a/gtsam/linear/HessianFactor.h b/gtsam/linear/HessianFactor.h
index faf2ccd33..62d84925c 100644
--- a/gtsam/linear/HessianFactor.h
+++ b/gtsam/linear/HessianFactor.h
@@ -387,6 +387,8 @@ namespace gtsam {
/// eta for Hessian
VectorValues gradientAtZero() const;
+ virtual void gradientAtZero(double* d) const;
+
/**
* Densely partially eliminate with Cholesky factorization. JacobianFactors are
* left-multiplied with their transpose to form the Hessian using the conversion constructor
diff --git a/gtsam/linear/JacobianFactor.cpp b/gtsam/linear/JacobianFactor.cpp
index c080f75cb..a63bbf473 100644
--- a/gtsam/linear/JacobianFactor.cpp
+++ b/gtsam/linear/JacobianFactor.cpp
@@ -573,6 +573,11 @@ VectorValues JacobianFactor::gradientAtZero() const {
return g;
}
+/* ************************************************************************* */
+void JacobianFactor::gradientAtZero(double* d) const {
+ //throw std::runtime_error("gradientAtZero not implemented for Jacobian factor");
+}
+
/* ************************************************************************* */
pair JacobianFactor::jacobian() const {
pair result = jacobianUnweighted();
diff --git a/gtsam/linear/JacobianFactor.h b/gtsam/linear/JacobianFactor.h
index 5a567c2c7..b90012822 100644
--- a/gtsam/linear/JacobianFactor.h
+++ b/gtsam/linear/JacobianFactor.h
@@ -286,6 +286,9 @@ namespace gtsam {
/// A'*b for Jacobian
VectorValues gradientAtZero() const;
+ /* ************************************************************************* */
+ virtual void gradientAtZero(double* d) const;
+
/** Return a whitened version of the factor, i.e. with unit diagonal noise model. */
JacobianFactor whiten() const;
diff --git a/gtsam/linear/NoiseModel.cpp b/gtsam/linear/NoiseModel.cpp
index 641b47640..fcaec3afa 100644
--- a/gtsam/linear/NoiseModel.cpp
+++ b/gtsam/linear/NoiseModel.cpp
@@ -47,22 +47,73 @@ void updateAb(MATRIX& Ab, int j, const Vector& a, const Vector& rd) {
Ab.middleCols(j+1,n-j) -= a * rd.segment(j+1, n-j).transpose();
}
+/* ************************************************************************* */
+// check *above the diagonal* for non-zero entries
+boost::optional checkIfDiagonal(const Matrix M) {
+ size_t m = M.rows(), n = M.cols();
+ // check all non-diagonal entries
+ bool full = false;
+ size_t i, j;
+ for (i = 0; i < m; i++)
+ if (!full)
+ for (j = i + 1; j < n; j++)
+ if (fabs(M(i, j)) > 1e-9) {
+ full = true;
+ break;
+ }
+ if (full) {
+ return boost::none;
+ } else {
+ Vector diagonal(n);
+ for (j = 0; j < n; j++)
+ diagonal(j) = M(j, j);
+ return diagonal;
+ }
+}
/* ************************************************************************* */
-Gaussian::shared_ptr Gaussian::Covariance(const Matrix& covariance, bool smart) {
- size_t m = covariance.rows(), n = covariance.cols();
- if (m != n) throw invalid_argument("Gaussian::Covariance: covariance not square");
- if (smart) {
- // check all non-diagonal entries
- size_t i,j;
- for (i = 0; i < m; i++)
- for (j = 0; j < n; j++)
- if (i != j && fabs(covariance(i, j)) > 1e-9) goto full;
- Vector variances(n);
- for (j = 0; j < n; j++) variances(j) = covariance(j,j);
- return Diagonal::Variances(variances,true);
+Gaussian::shared_ptr Gaussian::SqrtInformation(const Matrix& R, bool smart) {
+ size_t m = R.rows(), n = R.cols();
+ if (m != n)
+ throw invalid_argument("Gaussian::SqrtInformation: R not square");
+ boost::optional diagonal = boost::none;
+ if (smart)
+ diagonal = checkIfDiagonal(R);
+ if (diagonal)
+ return Diagonal::Sigmas(reciprocal(*diagonal), true);
+ else
+ return shared_ptr(new Gaussian(R.rows(), R));
+}
+
+/* ************************************************************************* */
+Gaussian::shared_ptr Gaussian::Information(const Matrix& M, bool smart) {
+ size_t m = M.rows(), n = M.cols();
+ if (m != n)
+ throw invalid_argument("Gaussian::Information: R not square");
+ boost::optional diagonal = boost::none;
+ if (smart)
+ diagonal = checkIfDiagonal(M);
+ if (diagonal)
+ return Diagonal::Precisions(*diagonal, true);
+ else {
+ Matrix R = RtR(M);
+ return shared_ptr(new Gaussian(R.rows(), R));
}
- full: return shared_ptr(new Gaussian(n, inverse_square_root(covariance)));
+}
+
+/* ************************************************************************* */
+Gaussian::shared_ptr Gaussian::Covariance(const Matrix& covariance,
+ bool smart) {
+ size_t m = covariance.rows(), n = covariance.cols();
+ if (m != n)
+ throw invalid_argument("Gaussian::Covariance: covariance not square");
+ boost::optional variances = boost::none;
+ if (smart)
+ variances = checkIfDiagonal(covariance);
+ if (variances)
+ return Diagonal::Variances(*variances, true);
+ else
+ return shared_ptr(new Gaussian(n, inverse_square_root(covariance)));
}
/* ************************************************************************* */
@@ -166,7 +217,7 @@ void Gaussian::WhitenSystem(Matrix& A1, Matrix& A2, Matrix& A3, Vector& b) const
// Diagonal
/* ************************************************************************* */
Diagonal::Diagonal() :
- Gaussian(1)//, sigmas_(ones(1)), invsigmas_(ones(1)), precisions_(ones(1))
+ Gaussian(1) // TODO: Frank asks: really sure about this?
{
}
@@ -180,8 +231,8 @@ Diagonal::Diagonal(const Vector& sigmas) :
Diagonal::shared_ptr Diagonal::Variances(const Vector& variances, bool smart) {
if (smart) {
// check whether all the same entry
- DenseIndex j, n = variances.size();
- for (j = 1; j < n; j++)
+ size_t n = variances.size();
+ for (size_t j = 1; j < n; j++)
if (variances(j) != variances(0)) goto full;
return Isotropic::Variance(n, variances(0), true);
}
@@ -191,12 +242,18 @@ Diagonal::shared_ptr Diagonal::Variances(const Vector& variances, bool smart) {
/* ************************************************************************* */
Diagonal::shared_ptr Diagonal::Sigmas(const Vector& sigmas, bool smart) {
if (smart) {
+ size_t n = sigmas.size();
+ if (n==0) goto full;
// look for zeros to make a constraint
- for (size_t i=0; i< (size_t) sigmas.size(); ++i)
- if (sigmas(i)<1e-8)
+ for (size_t j=0; j< n; ++j)
+ if (sigmas(j)<1e-8)
return Constrained::MixedSigmas(sigmas);
+ // check whether all the same entry
+ for (size_t j = 1; j < n; j++)
+ if (sigmas(j) != sigmas(0)) goto full;
+ return Isotropic::Sigma(n, sigmas(0), true);
}
- return Diagonal::shared_ptr(new Diagonal(sigmas));
+ full: return Diagonal::shared_ptr(new Diagonal(sigmas));
}
/* ************************************************************************* */
diff --git a/gtsam/linear/NoiseModel.h b/gtsam/linear/NoiseModel.h
index a87f426fa..0351cfabd 100644
--- a/gtsam/linear/NoiseModel.h
+++ b/gtsam/linear/NoiseModel.h
@@ -159,10 +159,17 @@ namespace gtsam {
/**
* A Gaussian noise model created by specifying a square root information matrix.
+ * @param R The (upper-triangular) square root information matrix
+ * @param smart check if can be simplified to derived class
*/
- static shared_ptr SqrtInformation(const Matrix& R) {
- return shared_ptr(new Gaussian(R.rows(),R));
- }
+ static shared_ptr SqrtInformation(const Matrix& R, bool smart = true);
+
+ /**
+ * A Gaussian noise model created by specifying an information matrix.
+ * @param M The information matrix
+ * @param smart check if can be simplified to derived class
+ */
+ static shared_ptr Information(const Matrix& M, bool smart = true);
/**
* A Gaussian noise model created by specifying a covariance matrix.
@@ -864,6 +871,9 @@ namespace gtsam {
ar & boost::serialization::make_nvp("noise_", const_cast(noise_));
}
};
+
+ // Helper function
+ GTSAM_EXPORT boost::optional checkIfDiagonal(const Matrix M);
} // namespace noiseModel
diff --git a/gtsam/linear/tests/testNoiseModel.cpp b/gtsam/linear/tests/testNoiseModel.cpp
index 9d87a163b..df0f8a774 100644
--- a/gtsam/linear/tests/testNoiseModel.cpp
+++ b/gtsam/linear/tests/testNoiseModel.cpp
@@ -267,6 +267,35 @@ TEST(NoiseModel, QRNan )
EXPECT(assert_equal(expectedAb,Ab));
}
+/* ************************************************************************* */
+TEST(NoiseModel, SmartSqrtInformation )
+{
+ bool smart = true;
+ gtsam::SharedGaussian expected = Unit::Create(3);
+ gtsam::SharedGaussian actual = Gaussian::SqrtInformation(eye(3), smart);
+ EXPECT(assert_equal(*expected,*actual));
+}
+
+/* ************************************************************************* */
+TEST(NoiseModel, SmartSqrtInformation2 )
+{
+ bool smart = true;
+ gtsam::SharedGaussian expected = Unit::Isotropic::Sigma(3,2);
+ gtsam::SharedGaussian actual = Gaussian::SqrtInformation(0.5*eye(3), smart);
+ EXPECT(assert_equal(*expected,*actual));
+}
+
+/* ************************************************************************* */
+TEST(NoiseModel, SmartInformation )
+{
+ bool smart = true;
+ gtsam::SharedGaussian expected = Unit::Isotropic::Variance(3,2);
+ Matrix M = 0.5*eye(3);
+ EXPECT(checkIfDiagonal(M));
+ gtsam::SharedGaussian actual = Gaussian::Information(M, smart);
+ EXPECT(assert_equal(*expected,*actual));
+}
+
/* ************************************************************************* */
TEST(NoiseModel, SmartCovariance )
{
diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h
index 559568c84..06e79324d 100644
--- a/gtsam/navigation/CombinedImuFactor.h
+++ b/gtsam/navigation/CombinedImuFactor.h
@@ -34,6 +34,11 @@ namespace gtsam {
*
* @addtogroup SLAM
*
+ * If you are using the factor, please cite:
+ * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating conditionally
+ * independent sets in factor graphs: a unifying perspective based on smart factors,
+ * Int. Conf. on Robotics and Automation (ICRA), 2014.
+ *
* REFERENCES:
* [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", Volume 2, 2008.
* [2] T. Lupton and S.Sukkarieh, "Visual-Inertial-Aided Navigation for High-Dynamic Motion in Built
diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h
index b1e20297e..f5b5293f8 100644
--- a/gtsam/navigation/ImuFactor.h
+++ b/gtsam/navigation/ImuFactor.h
@@ -33,7 +33,13 @@ namespace gtsam {
/**
*
* @addtogroup SLAM
- * * REFERENCES:
+ *
+ * If you are using the factor, please cite:
+ * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating conditionally
+ * independent sets in factor graphs: a unifying perspective based on smart factors,
+ * Int. Conf. on Robotics and Automation (ICRA), 2014.
+ *
+ ** REFERENCES:
* [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", Volume 2, 2008.
* [2] T. Lupton and S.Sukkarieh, "Visual-Inertial-Aided Navigation for High-Dynamic Motion in Built
* Environments Without Initial Conditions", TRO, 28(1):61-76, 2012.
diff --git a/gtsam/navigation/MagFactor.h b/gtsam/navigation/MagFactor.h
index 79677c0c6..44f543bc9 100644
--- a/gtsam/navigation/MagFactor.h
+++ b/gtsam/navigation/MagFactor.h
@@ -54,8 +54,11 @@ public:
static Point3 unrotate(const Rot2& R, const Point3& p,
boost::optional HR = boost::none) {
Point3 q = Rot3::yaw(R.theta()).unrotate(p, HR);
- if (HR)
- *HR = HR->col(2);
+ if (HR) {
+ // assign to temporary first to avoid error in Win-Debug mode
+ Matrix H = HR->col(2);
+ *HR = H;
+ }
return q;
}
diff --git a/gtsam/nonlinear/DoglegOptimizerImpl.h b/gtsam/nonlinear/DoglegOptimizerImpl.h
index a6c04681b..dea4113f7 100644
--- a/gtsam/nonlinear/DoglegOptimizerImpl.h
+++ b/gtsam/nonlinear/DoglegOptimizerImpl.h
@@ -191,7 +191,7 @@ typename DoglegOptimizerImpl::IterationResult DoglegOptimizerImpl::Iterate(
if(mode == ONE_STEP_PER_ITERATION || mode == SEARCH_REDUCE_ONLY)
stay = false; // If not searching, just return with the new Delta
else if(mode == SEARCH_EACH_ITERATION) {
- if(newDelta == Delta || lastAction == DECREASED_DELTA)
+ if(fabs(newDelta - Delta) < 1e-15 || lastAction == DECREASED_DELTA)
stay = false; // Searching, but Newton's solution is within trust region so keep the same trust region
else {
stay = true; // Searching and increased Delta, so try again to increase Delta
diff --git a/gtsam/nonlinear/LevenbergMarquardtOptimizer.cpp b/gtsam/nonlinear/LevenbergMarquardtOptimizer.cpp
index 6be69e710..08961db86 100644
--- a/gtsam/nonlinear/LevenbergMarquardtOptimizer.cpp
+++ b/gtsam/nonlinear/LevenbergMarquardtOptimizer.cpp
@@ -138,7 +138,7 @@ void LevenbergMarquardtOptimizer::decreaseLambda(double stepQuality) {
}
/* ************************************************************************* */
-GaussianFactorGraph LevenbergMarquardtOptimizer::buildDampedSystem(
+GaussianFactorGraph::shared_ptr LevenbergMarquardtOptimizer::buildDampedSystem(
const GaussianFactorGraph& linear) {
gttic(damp);
@@ -159,7 +159,8 @@ GaussianFactorGraph LevenbergMarquardtOptimizer::buildDampedSystem(
// for each of the variables, add a prior
double sigma = 1.0 / std::sqrt(state_.lambda);
- GaussianFactorGraph damped = linear;
+ GaussianFactorGraph::shared_ptr dampedPtr = linear.cloneToPtr();
+ GaussianFactorGraph &damped = (*dampedPtr);
damped.reserve(damped.size() + state_.values.size());
if (params_.diagonalDamping) {
BOOST_FOREACH(const VectorValues::KeyValuePair& key_vector, state_.hessianDiagonal) {
@@ -188,7 +189,20 @@ GaussianFactorGraph LevenbergMarquardtOptimizer::buildDampedSystem(
}
}
gttoc(damp);
- return damped;
+ return dampedPtr;
+}
+
+/* ************************************************************************* */
+// Log current error/lambda to file
+inline void LevenbergMarquardtOptimizer::writeLogFile(double currentError){
+ if (!params_.logFile.empty()) {
+ ofstream os(params_.logFile.c_str(), ios::app);
+ boost::posix_time::ptime currentTime = boost::posix_time::microsec_clock::universal_time();
+ os << /*inner iterations*/ state_.totalNumberInnerIterations << ","
+ << 1e-6 * (currentTime - state_.startTime).total_microseconds() << ","
+ << /*current error*/ currentError << "," << state_.lambda << ","
+ << /*outer iterations*/ state_.iterations << endl;
+ }
}
/* ************************************************************************* */
@@ -205,6 +219,9 @@ void LevenbergMarquardtOptimizer::iterate() {
cout << "linearizing = " << endl;
GaussianFactorGraph::shared_ptr linear = linearize();
+ if(state_.totalNumberInnerIterations==0) // write initial error
+ writeLogFile(state_.error);
+
// Keep increasing lambda until we make make progress
while (true) {
@@ -212,21 +229,8 @@ void LevenbergMarquardtOptimizer::iterate() {
cout << "trying lambda = " << state_.lambda << endl;
// Build damped system for this lambda (adds prior factors that make it like gradient descent)
- GaussianFactorGraph dampedSystem = buildDampedSystem(*linear);
-
- // Log current error/lambda to file
- if (!params_.logFile.empty()) {
- ofstream os(params_.logFile.c_str(), ios::app);
-
- boost::posix_time::ptime currentTime =
- boost::posix_time::microsec_clock::universal_time();
-
- os << state_.totalNumberInnerIterations << ","
- << 1e-6 * (currentTime - state_.startTime).total_microseconds() << ","
- << state_.error << "," << state_.lambda << endl;
- }
-
- ++state_.totalNumberInnerIterations;
+ GaussianFactorGraph::shared_ptr dampedSystemPtr = buildDampedSystem(*linear);
+ GaussianFactorGraph &dampedSystem = (*dampedSystemPtr);
// Try solving
double modelFidelity = 0.0;
@@ -240,7 +244,7 @@ void LevenbergMarquardtOptimizer::iterate() {
try {
delta = solve(dampedSystem, state_.values, params_);
systemSolvedSuccessfully = true;
- } catch (IndeterminantLinearSystemException& e) {
+ } catch (IndeterminantLinearSystemException) {
systemSolvedSuccessfully = false;
}
@@ -256,6 +260,9 @@ void LevenbergMarquardtOptimizer::iterate() {
double newlinearizedError = linear->error(delta);
double linearizedCostChange = state_.error - newlinearizedError;
+ if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
+ cout << "newlinearizedError = " << newlinearizedError <<
+ " linearizedCostChange = " << linearizedCostChange << endl;
if (linearizedCostChange >= 0) { // step is valid
// update values
@@ -266,50 +273,62 @@ void LevenbergMarquardtOptimizer::iterate() {
// compute new error
gttic(compute_error);
if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
- cout << "calculating error" << endl;
+ cout << "calculating error:" << endl;
newError = graph_.error(newValues);
gttoc(compute_error);
+ if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
+ cout << "old error (" << state_.error
+ << ") new (tentative) error (" << newError << ")" << endl;
+
// cost change in the original, nonlinear system (old - new)
double costChange = state_.error - newError;
- if (linearizedCostChange > 1e-15) { // the error has to decrease to satify this condition
+ if (linearizedCostChange > 1e-20) { // the (linear) error has to decrease to satisfy this condition
// fidelity of linearized model VS original system between
modelFidelity = costChange / linearizedCostChange;
// if we decrease the error in the nonlinear system and modelFidelity is above threshold
step_is_successful = modelFidelity > params_.minModelFidelity;
- } else {
- step_is_successful = true; // linearizedCostChange close to zero
- }
+ if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
+ cout << "modelFidelity: " << modelFidelity << endl;
+ } // else we consider the step non successful and we either increase lambda or stop if error change is small
double minAbsoluteTolerance = params_.relativeErrorTol * state_.error;
// if the change is small we terminate
- if (fabs(costChange) < minAbsoluteTolerance)
+ if (fabs(costChange) < minAbsoluteTolerance){
+ if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
+ cout << "fabs(costChange)="<= LevenbergMarquardtParams::TRYLAMBDA)
- cout << "increasing lambda: old error (" << state_.error
- << ") new error (" << newError << ")" << endl;
+ cout << "increasing lambda" << endl;
increaseLambda();
+ writeLogFile(state_.error);
// check if lambda is too big
if (state_.lambda >= params_.lambdaUpperBound) {
if (nloVerbosity >= NonlinearOptimizerParams::TERMINATION)
- cout
- << "Warning: Levenberg-Marquardt giving up because cannot decrease error with maximum lambda"
- << endl;
+ cout << "Warning: Levenberg-Marquardt giving up because "
+ "cannot decrease error with maximum lambda" << endl;
break;
}
} else { // the change in the cost is very small and it is not worth trying bigger lambdas
+ writeLogFile(state_.error);
+ if (lmVerbosity >= LevenbergMarquardtParams::TRYLAMBDA)
+ cout << "Levenberg-Marquardt: stopping as relative cost reduction is small" << endl;
break;
}
} // end while
diff --git a/gtsam/nonlinear/LevenbergMarquardtOptimizer.h b/gtsam/nonlinear/LevenbergMarquardtOptimizer.h
index 47952f9e4..f365fc524 100644
--- a/gtsam/nonlinear/LevenbergMarquardtOptimizer.h
+++ b/gtsam/nonlinear/LevenbergMarquardtOptimizer.h
@@ -113,7 +113,6 @@ public:
inline void setDiagonalDamping(bool flag) {
diagonalDamping = flag;
}
-
inline void setUseFixedLambdaFactor(bool flag) {
useFixedLambdaFactor_ = flag;
}
@@ -255,9 +254,11 @@ public:
}
/** Build a damped system for a specific lambda */
- GaussianFactorGraph buildDampedSystem(const GaussianFactorGraph& linear);
+ GaussianFactorGraph::shared_ptr buildDampedSystem(const GaussianFactorGraph& linear);
friend class ::NonlinearOptimizerMoreOptimizationTest;
+ void writeLogFile(double currentError);
+
/// @}
protected:
diff --git a/gtsam_unstable/slam/ImplicitSchurFactor.h b/gtsam/slam/ImplicitSchurFactor.h
similarity index 75%
rename from gtsam_unstable/slam/ImplicitSchurFactor.h
rename to gtsam/slam/ImplicitSchurFactor.h
index 234d13f1f..c0f339b30 100644
--- a/gtsam_unstable/slam/ImplicitSchurFactor.h
+++ b/gtsam/slam/ImplicitSchurFactor.h
@@ -24,7 +24,6 @@ class ImplicitSchurFactor: public GaussianFactor {
public:
typedef ImplicitSchurFactor This; ///< Typedef to this class
- typedef JacobianFactor Base; ///< Typedef to base class
typedef boost::shared_ptr shared_ptr; ///< shared_ptr to this class
protected:
@@ -87,7 +86,8 @@ public:
}
/// print
- void print(const std::string& s, const KeyFormatter& formatter) const {
+ void print(const std::string& s = "",
+ const KeyFormatter& keyFormatter = DefaultKeyFormatter) const {
std::cout << " ImplicitSchurFactor " << std::endl;
Factor::print(s);
std::cout << " PointCovariance_ \n" << PointCovariance_ << std::endl;
@@ -188,19 +188,24 @@ public:
/// Return the block diagonal of the Hessian for this factor
virtual std::map hessianBlockDiagonal() const {
std::map blocks;
+ // F'*(I - E*P*E')*F
for (size_t pos = 0; pos < size(); ++pos) {
Key j = keys_[pos];
- const Matrix2D& Fj = Fblocks_[pos].second;
// F'*F - F'*E*P*E'*F (9*2)*(2*9) - (9*2)*(2*3)*(3*3)*(3*2)*(2*9)
- Eigen::Matrix FtE = Fj.transpose()
- * E_.block<2, 3>(2 * pos, 0);
- blocks[j] = Fj.transpose() * Fj
- - FtE * PointCovariance_ * FtE.transpose();
+ const Matrix2D& Fj = Fblocks_[pos].second;
+ // Eigen::Matrix FtE = Fj.transpose()
+ // * E_.block<2, 3>(2 * pos, 0);
+ // blocks[j] = Fj.transpose() * Fj
+ // - FtE * PointCovariance_ * FtE.transpose();
+
+ const Matrix23& Ej = E_.block<2, 3>(2 * pos, 0);
+ blocks[j] = Fj.transpose() * (Fj - Ej * PointCovariance_ * Ej.transpose() * Fj);
+
// F'*(I - E*P*E')*F, TODO: this should work, but it does not :-(
-// static const Eigen::Matrix I2 = eye(2);
-// Eigen::Matrix Q = //
-// I2 - E_.block<2, 3>(2 * pos, 0) * PointCovariance_ * E_.block<2, 3>(2 * pos, 0).transpose();
-// blocks[j] = Fj.transpose() * Q * Fj;
+ // static const Eigen::Matrix I2 = eye(2);
+ // Eigen::Matrix Q = //
+ // I2 - E_.block<2, 3>(2 * pos, 0) * PointCovariance_ * E_.block<2, 3>(2 * pos, 0).transpose();
+ // blocks[j] = Fj.transpose() * Q * Fj;
}
return blocks;
}
@@ -235,25 +240,33 @@ public:
typedef std::vector Error2s;
/**
- * @brief Calculate corrected error Q*e = (I - E*P*E')*e
+ * @brief Calculate corrected error Q*(e-2*b) = (I - E*P*E')*(e-2*b)
*/
- void projectError(const Error2s& e1, Error2s& e2) const {
+ void projectError2(const Error2s& e1, Error2s& e2) const {
- // d1 = E.transpose() * e1 = (3*2m)*2m
+ // d1 = E.transpose() * (e1-2*b) = (3*2m)*2m
Vector3 d1;
d1.setZero();
for (size_t k = 0; k < size(); k++)
- d1 += E_.block < 2, 3 > (2 * k, 0).transpose() * e1[k];
+ d1 += E_.block < 2, 3 > (2 * k, 0).transpose() * (e1[k] - 2 * b_.segment < 2 > (k * 2));
// d2 = E.transpose() * e1 = (3*2m)*2m
Vector3 d2 = PointCovariance_ * d1;
// e3 = alpha*(e1 - E*d2) = 1*[2m-(2m*3)*3]
for (size_t k = 0; k < size(); k++)
- e2[k] = e1[k] - E_.block < 2, 3 > (2 * k, 0) * d2;
+ e2[k] = e1[k] - 2 * b_.segment < 2 > (k * 2) - E_.block < 2, 3 > (2 * k, 0) * d2;
}
- /// needed to be GaussianFactor - (I - E*P*E')*(F*x - b)
+ /*
+ * This definition matches the linearized error in the Hessian Factor:
+ * LinError(x) = x'*H*x - 2*x'*eta + f
+ * with:
+ * H = F' * (I-E'*P*E) * F = F' * Q * F
+ * eta = F' * (I-E'*P*E) * b = F' * Q * b
+ * f = nonlinear error
+ * (x'*H*x - 2*x'*eta + f) = x'*F'*Q*F*x - 2*x'*F'*Q *b + f = x'*F'*Q*(F*x - 2*b) + f
+ */
virtual double error(const VectorValues& x) const {
// resize does not do malloc if correct size
@@ -262,15 +275,56 @@ public:
// e1 = F * x - b = (2m*dm)*dm
for (size_t k = 0; k < size(); ++k)
- e1[k] = Fblocks_[k].second * x.at(keys_[k]) - b_.segment < 2 > (k * 2);
- projectError(e1, e2);
+ e1[k] = Fblocks_[k].second * x.at(keys_[k]);
+ projectError2(e1, e2);
double result = 0;
for (size_t k = 0; k < size(); ++k)
- result += dot(e2[k], e2[k]);
- return 0.5 * result;
+ result += dot(e1[k], e2[k]);
+
+ double f = b_.squaredNorm();
+ return 0.5 * (result + f);
}
+ /// needed to be GaussianFactor - (I - E*P*E')*(F*x - b)
+ // This is wrong and does not match the definition in Hessian
+ // virtual double error(const VectorValues& x) const {
+ //
+ // // resize does not do malloc if correct size
+ // e1.resize(size());
+ // e2.resize(size());
+ //
+ // // e1 = F * x - b = (2m*dm)*dm
+ // for (size_t k = 0; k < size(); ++k)
+ // e1[k] = Fblocks_[k].second * x.at(keys_[k]) - b_.segment < 2 > (k * 2);
+ // projectError(e1, e2);
+ //
+ // double result = 0;
+ // for (size_t k = 0; k < size(); ++k)
+ // result += dot(e2[k], e2[k]);
+ //
+ // std::cout << "implicitFactor::error result " << result << std::endl;
+ // return 0.5 * result;
+ // }
+ /**
+ * @brief Calculate corrected error Q*e = (I - E*P*E')*e
+ */
+ void projectError(const Error2s& e1, Error2s& e2) const {
+
+ // d1 = E.transpose() * e1 = (3*2m)*2m
+ Vector3 d1;
+ d1.setZero();
+ for (size_t k = 0; k < size(); k++)
+ d1 += E_.block < 2, 3 > (2 * k, 0).transpose() * e1[k];
+
+ // d2 = E.transpose() * e1 = (3*2m)*2m
+ Vector3 d2 = PointCovariance_ * d1;
+
+ // e3 = alpha*(e1 - E*d2) = 1*[2m-(2m*3)*3]
+ for (size_t k = 0; k < size(); k++)
+ e2[k] = e1[k] - E_.block < 2, 3 > (2 * k, 0) * d2;
+ }
+
/// Scratch space for multiplyHessianAdd
mutable Error2s e1, e2;
@@ -377,6 +431,28 @@ public:
return g;
}
+ /**
+ * Calculate gradient, which is -F'Q*b, see paper - RAW MEMORY ACCESS
+ */
+ void gradientAtZero(double* d) const {
+
+ // Use eigen magic to access raw memory
+ typedef Eigen::Matrix DVector;
+ typedef Eigen::Map DMap;
+
+ // calculate Q*b
+ e1.resize(size());
+ e2.resize(size());
+ for (size_t k = 0; k < size(); k++)
+ e1[k] = b_.segment < 2 > (2 * k);
+ projectError(e1, e2);
+
+ for (size_t k = 0; k < size(); ++k) { // for each camera in the factor
+ Key j = keys_[k];
+ DMap(d + D * j) += -Fblocks_[k].second.transpose() * e2[k];
+ }
+ }
+
};
// ImplicitSchurFactor
diff --git a/gtsam_unstable/slam/JacobianFactorQ.h b/gtsam/slam/JacobianFactorQ.h
similarity index 71%
rename from gtsam_unstable/slam/JacobianFactorQ.h
rename to gtsam/slam/JacobianFactorQ.h
index fdbe0e231..f4dfb9422 100644
--- a/gtsam_unstable/slam/JacobianFactorQ.h
+++ b/gtsam/slam/JacobianFactorQ.h
@@ -23,6 +23,19 @@ public:
JacobianFactorQ() {
}
+ /// Empty constructor with keys
+ JacobianFactorQ(const FastVector& keys,
+ const SharedDiagonal& model = SharedDiagonal()) : JacobianSchurFactor() {
+ Matrix zeroMatrix = Matrix::Zero(0,D);
+ Vector zeroVector = Vector::Zero(0);
+ typedef std::pair KeyMatrix;
+ std::vector QF;
+ QF.reserve(keys.size());
+ BOOST_FOREACH(const Key& key, keys)
+ QF.push_back(KeyMatrix(key, zeroMatrix));
+ JacobianFactor::fillTerms(QF, zeroVector, model);
+ }
+
/// Constructor
JacobianFactorQ(const std::vector& Fblocks,
const Matrix& E, const Matrix3& P, const Vector& b,
diff --git a/gtsam_unstable/slam/JacobianFactorQR.h b/gtsam/slam/JacobianFactorQR.h
similarity index 96%
rename from gtsam_unstable/slam/JacobianFactorQR.h
rename to gtsam/slam/JacobianFactorQR.h
index 2d2d5b7a4..a928106a8 100644
--- a/gtsam_unstable/slam/JacobianFactorQR.h
+++ b/gtsam/slam/JacobianFactorQR.h
@@ -6,7 +6,7 @@
*/
#pragma once
-#include
+#include
namespace gtsam {
/**
diff --git a/gtsam_unstable/slam/JacobianFactorSVD.h b/gtsam/slam/JacobianFactorSVD.h
similarity index 69%
rename from gtsam_unstable/slam/JacobianFactorSVD.h
rename to gtsam/slam/JacobianFactorSVD.h
index 752a9f48e..e28185038 100644
--- a/gtsam_unstable/slam/JacobianFactorSVD.h
+++ b/gtsam/slam/JacobianFactorSVD.h
@@ -5,7 +5,7 @@
*/
#pragma once
-#include "gtsam_unstable/slam/JacobianSchurFactor.h"
+#include "gtsam/slam/JacobianSchurFactor.h"
namespace gtsam {
/**
@@ -18,10 +18,23 @@ public:
typedef Eigen::Matrix Matrix2D;
typedef std::pair KeyMatrix2D;
+ typedef std::pair KeyMatrix;
/// Default constructor
JacobianFactorSVD() {}
+ /// Empty constructor with keys
+ JacobianFactorSVD(const FastVector& keys,
+ const SharedDiagonal& model = SharedDiagonal()) : JacobianSchurFactor() {
+ Matrix zeroMatrix = Matrix::Zero(0,D);
+ Vector zeroVector = Vector::Zero(0);
+ std::vector QF;
+ QF.reserve(keys.size());
+ BOOST_FOREACH(const Key& key, keys)
+ QF.push_back(KeyMatrix(key, zeroMatrix));
+ JacobianFactor::fillTerms(QF, zeroVector, model);
+ }
+
/// Constructor
JacobianFactorSVD(const std::vector& Fblocks, const Matrix& Enull, const Vector& b,
const SharedDiagonal& model = SharedDiagonal()) : JacobianSchurFactor() {
@@ -32,7 +45,6 @@ public:
// BOOST_FOREACH(const KeyMatrix2D& it, Fblocks)
// QF.push_back(KeyMatrix(it.first, Q.block(0, 2 * j++, m2, 2) * it.second));
// JacobianFactor factor(QF, Q * b);
- typedef std::pair KeyMatrix;
std::vector QF;
QF.reserve(numKeys);
BOOST_FOREACH(const KeyMatrix2D& it, Fblocks)
diff --git a/gtsam_unstable/slam/JacobianSchurFactor.h b/gtsam/slam/JacobianSchurFactor.h
similarity index 100%
rename from gtsam_unstable/slam/JacobianSchurFactor.h
rename to gtsam/slam/JacobianSchurFactor.h
diff --git a/gtsam_unstable/slam/RegularHessianFactor.h b/gtsam/slam/RegularHessianFactor.h
similarity index 100%
rename from gtsam_unstable/slam/RegularHessianFactor.h
rename to gtsam/slam/RegularHessianFactor.h
diff --git a/gtsam_unstable/slam/SmartFactorBase.h b/gtsam/slam/SmartFactorBase.h
similarity index 79%
rename from gtsam_unstable/slam/SmartFactorBase.h
rename to gtsam/slam/SmartFactorBase.h
index d24a878bb..1235fc6fb 100644
--- a/gtsam_unstable/slam/SmartFactorBase.h
+++ b/gtsam/slam/SmartFactorBase.h
@@ -20,6 +20,7 @@
#pragma once
#include "JacobianFactorQ.h"
+#include "JacobianFactorSVD.h"
#include "ImplicitSchurFactor.h"
#include "RegularHessianFactor.h"
@@ -135,12 +136,12 @@ public:
}
/** return the measurements */
- const Vector& measured() const {
+ const std::vector& measured() const {
return measured_;
}
/** return the noise model */
- const SharedNoiseModel& noise() const {
+ const std::vector& noise() const {
return noise_;
}
@@ -192,7 +193,7 @@ public:
b[2 * i + 1] = e.y();
} catch (CheiralityException& e) {
std::cout << "Cheirality exception " << std::endl;
- exit (EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
i += 1;
}
@@ -222,7 +223,7 @@ public:
* this->noise_.at(i)->distance(reprojectionError.vector());
} catch (CheiralityException&) {
std::cout << "Cheirality exception " << std::endl;
- exit (EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
i += 1;
}
@@ -244,7 +245,7 @@ public:
cameras[i].project(point, boost::none, Ei);
} catch (CheiralityException& e) {
std::cout << "Cheirality exception " << std::endl;
- exit (EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
this->noise_.at(i)->WhitenSystem(Ei, b);
E.block<2, 3>(2 * i, 0) = Ei;
@@ -274,7 +275,7 @@ public:
-(cameras[i].project(point, Fi, Ei, Hcali) - this->measured_.at(i)).vector();
} catch (CheiralityException&) {
std::cout << "Cheirality exception " << std::endl;
- exit (EXIT_FAILURE);
+ exit(EXIT_FAILURE);
}
this->noise_.at(i)->WhitenSystem(Fi, Ei, Hcali, bi);
@@ -302,15 +303,18 @@ public:
// Point covariance inv(E'*E)
Matrix3 EtE = E.transpose() * E;
- Matrix3 DMatrix = eye(E.cols()); // damping matrix
if (diagonalDamping) { // diagonal of the hessian
- DMatrix(0, 0) = EtE(0, 0);
- DMatrix(1, 1) = EtE(1, 1);
- DMatrix(2, 2) = EtE(2, 2);
+ EtE(0, 0) += lambda * EtE(0, 0);
+ EtE(1, 1) += lambda * EtE(1, 1);
+ EtE(2, 2) += lambda * EtE(2, 2);
+ }else{
+ EtE(0, 0) += lambda;
+ EtE(1, 1) += lambda;
+ EtE(2, 2) += lambda;
}
- PointCov.noalias() = (EtE + lambda * DMatrix).inverse();
+ PointCov.noalias() = (EtE).inverse();
return f;
}
@@ -322,7 +326,7 @@ public:
const double lambda = 0.0) const {
size_t numKeys = this->keys_.size();
- std::vector < KeyMatrix2D > Fblocks;
+ std::vector Fblocks;
double f = computeJacobians(Fblocks, E, PointCov, b, cameras, point,
lambda);
F = zeros(2 * numKeys, D * numKeys);
@@ -345,10 +349,10 @@ public:
diagonalDamping); // diagonalDamping should have no effect (only on PointCov)
// Do SVD on A
- Eigen::JacobiSVD < Matrix > svd(E, Eigen::ComputeFullU);
+ Eigen::JacobiSVD svd(E, Eigen::ComputeFullU);
Vector s = svd.singularValues();
// Enull = zeros(2 * numKeys, 2 * numKeys - 3);
- int numKeys = this->keys_.size();
+ size_t numKeys = this->keys_.size();
Enull = svd.matrixU().block(0, 3, 2 * numKeys, 2 * numKeys - 3); // last 2m-3 columns
return f;
@@ -361,7 +365,7 @@ public:
const Cameras& cameras, const Point3& point) const {
int numKeys = this->keys_.size();
- std::vector < KeyMatrix2D > Fblocks;
+ std::vector Fblocks;
double f = computeJacobiansSVD(Fblocks, Enull, b, cameras, point);
F.resize(2 * numKeys, D * numKeys);
F.setZero();
@@ -380,14 +384,14 @@ public:
int numKeys = this->keys_.size();
- std::vector < KeyMatrix2D > Fblocks;
+ std::vector Fblocks;
Matrix E;
Matrix3 PointCov;
Vector b;
double f = computeJacobians(Fblocks, E, PointCov, b, cameras, point, lambda,
diagonalDamping);
-//#define HESSIAN_BLOCKS
+//#define HESSIAN_BLOCKS // slower, as internally the Hessian factor will transform the blocks into SymmetricBlockMatrix
#ifdef HESSIAN_BLOCKS
// Create structures for Hessian Factors
std::vector < Matrix > Gs(numKeys * (numKeys + 1) / 2);
@@ -400,46 +404,23 @@ public:
//std::vector < Vector > gs2(gs.begin(), gs.end());
return boost::make_shared < RegularHessianFactor
- > (this->keys_, Gs, gs, f);
-#else
+ > (this->keys_, Gs, gs, f);
+#else // we create directly a SymmetricBlockMatrix
size_t n1 = D * numKeys + 1;
std::vector dims(numKeys + 1); // this also includes the b term
std::fill(dims.begin(), dims.end() - 1, D);
dims.back() = 1;
- SymmetricBlockMatrix augmentedHessian(dims, Matrix::Zero(n1, n1)); // for 10 cameras, size should be (10*D+1 x 10*D+1)
+ SymmetricBlockMatrix augmentedHessian(dims, Matrix::Zero(n1, n1)); // for 10 cameras, size should be (10*D+1 x 10*D+1)
sparseSchurComplement(Fblocks, E, PointCov, b, augmentedHessian); // augmentedHessian.matrix().block (i1,i2) = ...
- augmentedHessian(numKeys,numKeys)(0,0) = f;
- return boost::make_shared >(
- this->keys_, augmentedHessian);
-
+ augmentedHessian(numKeys, numKeys)(0, 0) = f;
+ return boost::make_shared >(this->keys_,
+ augmentedHessian);
#endif
}
// ****************************************************************************************************
- boost::shared_ptr > updateAugmentedHessian(
- const Cameras& cameras, const Point3& point, const double lambda,
- bool diagonalDamping, SymmetricBlockMatrix& augmentedHessian) const {
-
- int numKeys = this->keys_.size();
-
- std::vector < KeyMatrix2D > Fblocks;
- Matrix E;
- Matrix3 PointCov;
- Vector b;
- double f = computeJacobians(Fblocks, E, PointCov, b, cameras, point, lambda,
- diagonalDamping);
-
- std::vector dims(numKeys + 1); // this also includes the b term
- std::fill(dims.begin(), dims.end() - 1, D);
- dims.back() = 1;
-
- updateSparseSchurComplement(Fblocks, E, PointCov, b, augmentedHessian); // augmentedHessian.matrix().block (i1,i2) = ...
- std::cout << "f "<< f <& Fblocks, const Matrix& E,
const Matrix& PointCov, const Vector& b,
/*output ->*/std::vector& Gs, std::vector& gs) const {
@@ -466,7 +447,7 @@ public:
int GsCount2 = 0;
for (DenseIndex i1 = 0; i1 < numKeys; i1++) { // for each camera
DenseIndex i1D = i1 * D;
- gs.at(i1) = gs_vector.segment < D > (i1D);
+ gs.at(i1) = gs_vector.segment(i1D);
for (DenseIndex i2 = 0; i2 < numKeys; i2++) {
if (i2 >= i1) {
Gs.at(GsCount2) = H.block(i1D, i2 * D);
@@ -476,53 +457,6 @@ public:
}
}
- // ****************************************************************************************************
- void updateSparseSchurComplement(const std::vector& Fblocks,
- const Matrix& E, const Matrix& P /*Point Covariance*/, const Vector& b,
- /*output ->*/SymmetricBlockMatrix& augmentedHessian) const {
- // Schur complement trick
- // Gs = F' * F - F' * E * P * E' * F
- // gs = F' * (b - E * P * E' * b)
-
- // a single point is observed in numKeys cameras
- size_t numKeys = this->keys_.size(); // cameras observing current point
- size_t aug_numKeys = augmentedHessian.rows() - 1; // all cameras in the group
-
- // Blockwise Schur complement
- for (size_t i1 = 0; i1 < numKeys; i1++) { // for each camera
-
- const Matrix2D& Fi1 = Fblocks.at(i1).second;
- const Matrix23 Ei1_P = E.block<2, 3>(2 * i1, 0) * P;
-
- // D = (Dx2) * (2)
- // (augmentedHessian.matrix()).block (i1,numKeys+1) = Fi1.transpose() * b.segment < 2 > (2 * i1); // F' * b
- size_t aug_i1 = this->keys_[i1];
- std::cout << "i1 "<< i1 < (2 * i1) // F' * b
- - Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // D = (Dx2) * (2x3) * (3*2m) * (2m x 1)
-
- // (DxD) = (Dx2) * ( (2xD) - (2x3) * (3x2) * (2xD) )
- std::cout << "filled 1 " <(2 * i1, 0).transpose() * Fi1);
-
- // upper triangular part of the hessian
- for (size_t i2 = i1+1; i2 < numKeys; i2++) { // for each camera
- const Matrix2D& Fi2 = Fblocks.at(i2).second;
- size_t aug_i2 = this->keys_[i2];
- std::cout << "i2 "<< i2 <(2 * i2, 0).transpose() * Fi2);
- }
- } // end of for over cameras
- }
-
// ****************************************************************************************************
void sparseSchurComplement(const std::vector& Fblocks,
const Matrix& E, const Matrix& P /*Point Covariance*/, const Vector& b,
@@ -542,20 +476,20 @@ public:
// D = (Dx2) * (2)
// (augmentedHessian.matrix()).block (i1,numKeys+1) = Fi1.transpose() * b.segment < 2 > (2 * i1); // F' * b
- augmentedHessian(i1,numKeys) = Fi1.transpose() * b.segment < 2 > (2 * i1) // F' * b
- - Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // D = (Dx2) * (2x3) * (3*2m) * (2m x 1)
+ augmentedHessian(i1, numKeys) = Fi1.transpose() * b.segment<2>(2 * i1) // F' * b
+ - Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // D = (Dx2) * (2x3) * (3*2m) * (2m x 1)
// (DxD) = (Dx2) * ( (2xD) - (2x3) * (3x2) * (2xD) )
- augmentedHessian(i1,i1) =
- Fi1.transpose() * (Fi1 - Ei1_P * E.block<2, 3>(2 * i1, 0).transpose() * Fi1);
+ augmentedHessian(i1, i1) = Fi1.transpose()
+ * (Fi1 - Ei1_P * E.block<2, 3>(2 * i1, 0).transpose() * Fi1);
// upper triangular part of the hessian
- for (size_t i2 = i1+1; i2 < numKeys; i2++) { // for each camera
+ for (size_t i2 = i1 + 1; i2 < numKeys; i2++) { // for each camera
const Matrix2D& Fi2 = Fblocks.at(i2).second;
// (DxD) = (Dx2) * ( (2x2) * (2xD) )
- augmentedHessian(i1,i2) =
- -Fi1.transpose() * (Ei1_P * E.block<2, 3>(2 * i2, 0).transpose() * Fi2);
+ augmentedHessian(i1, i2) = -Fi1.transpose()
+ * (Ei1_P * E.block<2, 3>(2 * i2, 0).transpose() * Fi2);
}
} // end of for over cameras
}
@@ -586,24 +520,109 @@ public:
{ // for i1 = i2
// D = (Dx2) * (2)
- gs.at(i1) = Fi1.transpose() * b.segment < 2 > (2 * i1); // F' * b
- // D = (Dx2) * (2x3) * (3*2m) * (2m x 1)
- gs.at(i1) -= Fi1.transpose() * (Ei1_P * (E.transpose() * b));
+ gs.at(i1) = Fi1.transpose() * b.segment<2>(2 * i1) // F' * b
+ -Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // D = (Dx2) * (2x3) * (3*2m) * (2m x 1)
// (DxD) = (Dx2) * ( (2xD) - (2x3) * (3x2) * (2xD) )
- Gs.at(GsIndex) = Fi1.transpose() * (Fi1 - Ei1_P * E.block<2, 3>(2 * i1, 0).transpose() * Fi1);
+ Gs.at(GsIndex) = Fi1.transpose()
+ * (Fi1 - Ei1_P * E.block<2, 3>(2 * i1, 0).transpose() * Fi1);
GsIndex++;
}
// upper triangular part of the hessian
- for (size_t i2 = i1+1; i2 < numKeys; i2++) { // for each camera
+ for (size_t i2 = i1 + 1; i2 < numKeys; i2++) { // for each camera
const Matrix2D& Fi2 = Fblocks.at(i2).second;
// (DxD) = (Dx2) * ( (2x2) * (2xD) )
- Gs.at(GsIndex) = -Fi1.transpose() * (Ei1_P * E.block<2, 3>(2 * i2, 0).transpose() * Fi2);
+ Gs.at(GsIndex) = -Fi1.transpose()
+ * (Ei1_P * E.block<2, 3>(2 * i2, 0).transpose() * Fi2);
GsIndex++;
}
} // end of for over cameras
}
+
+ // ****************************************************************************************************
+ void updateAugmentedHessian(const Cameras& cameras, const Point3& point,
+ const double lambda, bool diagonalDamping,
+ SymmetricBlockMatrix& augmentedHessian,
+ const FastVector allKeys) const {
+
+ // int numKeys = this->keys_.size();
+
+ std::vector Fblocks;
+ Matrix E;
+ Matrix3 PointCov;
+ Vector b;
+ double f = computeJacobians(Fblocks, E, PointCov, b, cameras, point, lambda,
+ diagonalDamping);
+
+ updateSparseSchurComplement(Fblocks, E, PointCov, b, f, allKeys, augmentedHessian); // augmentedHessian.matrix().block (i1,i2) = ...
+ }
+
+ // ****************************************************************************************************
+ void updateSparseSchurComplement(const std::vector& Fblocks,
+ const Matrix& E, const Matrix& P /*Point Covariance*/, const Vector& b,
+ const double f, const FastVector allKeys,
+ /*output ->*/SymmetricBlockMatrix& augmentedHessian) const {
+ // Schur complement trick
+ // Gs = F' * F - F' * E * P * E' * F
+ // gs = F' * (b - E * P * E' * b)
+
+ MatrixDD matrixBlock;
+ typedef SymmetricBlockMatrix::Block Block; ///< A block from the Hessian matrix
+
+ FastMap KeySlotMap;
+ for (size_t slot=0; slot < allKeys.size(); slot++)
+ KeySlotMap.insert(std::make_pair(allKeys[slot],slot));
+
+ // a single point is observed in numKeys cameras
+ size_t numKeys = this->keys_.size(); // cameras observing current point
+ size_t aug_numKeys = (augmentedHessian.rows() - 1) / D; // all cameras in the group
+
+ // Blockwise Schur complement
+ for (size_t i1 = 0; i1 < numKeys; i1++) { // for each camera in the current factor
+
+ const Matrix2D& Fi1 = Fblocks.at(i1).second;
+ const Matrix23 Ei1_P = E.block<2, 3>(2 * i1, 0) * P;
+
+ // D = (Dx2) * (2)
+ // allKeys are the list of all camera keys in the group, e.g, (1,3,4,5,7)
+ // we should map those to a slot in the local (grouped) hessian (0,1,2,3,4)
+ // Key cameraKey_i1 = this->keys_[i1];
+ DenseIndex aug_i1 = KeySlotMap[this->keys_[i1]];
+
+ // information vector - store previous vector
+ // vectorBlock = augmentedHessian(aug_i1, aug_numKeys).knownOffDiagonal();
+ // add contribution of current factor
+ augmentedHessian(aug_i1, aug_numKeys) = augmentedHessian(aug_i1, aug_numKeys).knownOffDiagonal()
+ + Fi1.transpose() * b.segment<2>(2 * i1) // F' * b
+ - Fi1.transpose() * (Ei1_P * (E.transpose() * b)); // D = (Dx2) * (2x3) * (3*2m) * (2m x 1)
+
+ // (DxD) = (Dx2) * ( (2xD) - (2x3) * (3x2) * (2xD) )
+ // main block diagonal - store previous block
+ matrixBlock = augmentedHessian(aug_i1, aug_i1);
+ // add contribution of current factor
+ augmentedHessian(aug_i1, aug_i1) = matrixBlock +
+ ( Fi1.transpose() * (Fi1 - Ei1_P * E.block<2, 3>(2 * i1, 0).transpose() * Fi1) );
+
+ // upper triangular part of the hessian
+ for (size_t i2 = i1 + 1; i2 < numKeys; i2++) { // for each camera
+ const Matrix2D& Fi2 = Fblocks.at(i2).second;
+
+ //Key cameraKey_i2 = this->keys_[i2];
+ DenseIndex aug_i2 = KeySlotMap[this->keys_[i2]];
+
+ // (DxD) = (Dx2) * ( (2x2) * (2xD) )
+ // off diagonal block - store previous block
+ // matrixBlock = augmentedHessian(aug_i1, aug_i2).knownOffDiagonal();
+ // add contribution of current factor
+ augmentedHessian(aug_i1, aug_i2) = augmentedHessian(aug_i1, aug_i2).knownOffDiagonal()
+ - Fi1.transpose() * (Ei1_P * E.block<2, 3>(2 * i2, 0).transpose() * Fi2);
+ }
+ } // end of for over cameras
+
+ augmentedHessian(aug_numKeys, aug_numKeys)(0, 0) += f;
+ }
+
// ****************************************************************************************************
boost::shared_ptr > createImplicitSchurFactor(
const Cameras& cameras, const Point3& point, double lambda = 0.0,
@@ -620,13 +639,24 @@ public:
boost::shared_ptr > createJacobianQFactor(
const Cameras& cameras, const Point3& point, double lambda = 0.0,
bool diagonalDamping = false) const {
- std::vector < KeyMatrix2D > Fblocks;
+ std::vector Fblocks;
Matrix E;
Matrix3 PointCov;
Vector b;
computeJacobians(Fblocks, E, PointCov, b, cameras, point, lambda,
diagonalDamping);
- return boost::make_shared < JacobianFactorQ > (Fblocks, E, PointCov, b);
+ return boost::make_shared >(Fblocks, E, PointCov, b);
+ }
+
+ // ****************************************************************************************************
+ boost::shared_ptr createJacobianSVDFactor(
+ const Cameras& cameras, const Point3& point, double lambda = 0.0) const {
+ size_t numKeys = this->keys_.size();
+ std::vector < KeyMatrix2D > Fblocks;
+ Vector b;
+ Matrix Enull(2*numKeys, 2*numKeys-3);
+ computeJacobiansSVD(Fblocks, Enull, b, cameras, point, lambda);
+ return boost::make_shared< JacobianFactorSVD<6> >(Fblocks, Enull, b);
}
private:
diff --git a/gtsam_unstable/slam/SmartProjectionFactor.h b/gtsam/slam/SmartProjectionFactor.h
similarity index 91%
rename from gtsam_unstable/slam/SmartProjectionFactor.h
rename to gtsam/slam/SmartProjectionFactor.h
index 59a841813..043528fea 100644
--- a/gtsam_unstable/slam/SmartProjectionFactor.h
+++ b/gtsam/slam/SmartProjectionFactor.h
@@ -21,11 +21,10 @@
#include "SmartFactorBase.h"
-#include
+#include
#include
#include
#include
-#include
#include
#include
@@ -54,6 +53,10 @@ public:
double f;
};
+enum LinearizationMode {
+ HESSIAN, JACOBIAN_SVD, JACOBIAN_Q
+};
+
/**
* SmartProjectionFactor: triangulates point
* TODO: why LANDMARK parameter?
@@ -91,6 +94,13 @@ protected:
/// shorthand for base class type
typedef SmartFactorBase Base;
+ double landmarkDistanceThreshold_; // if the landmark is triangulated at a
+ // distance larger than that the factor is considered degenerate
+
+ double dynamicOutlierRejectionThreshold_; // if this is nonnegative the factor will check if the
+ // average reprojection error is smaller than this threshold after triangulation,
+ // and the factor is disregarded if the error is large
+
/// shorthand for this class
typedef SmartProjectionFactor This;
@@ -115,12 +125,15 @@ public:
SmartProjectionFactor(const double rankTol, const double linThreshold,
const bool manageDegeneracy, const bool enableEPI,
boost::optional body_P_sensor = boost::none,
- SmartFactorStatePtr state = SmartFactorStatePtr(
- new SmartProjectionFactorState())) :
+ double landmarkDistanceThreshold = 1e10,
+ double dynamicOutlierRejectionThreshold = -1,
+ SmartFactorStatePtr state = SmartFactorStatePtr(new SmartProjectionFactorState())) :
Base(body_P_sensor), rankTolerance_(rankTol), retriangulationThreshold_(
1e-5), manageDegeneracy_(manageDegeneracy), enableEPI_(enableEPI), linearizationThreshold_(
linThreshold), degenerate_(false), cheiralityException_(false), throwCheirality_(
- false), verboseCheirality_(false), state_(state) {
+ false), verboseCheirality_(false), state_(state),
+ landmarkDistanceThreshold_(landmarkDistanceThreshold),
+ dynamicOutlierRejectionThreshold_(dynamicOutlierRejectionThreshold) {
}
/** Virtual destructor */
@@ -234,6 +247,31 @@ public:
rankTolerance_, enableEPI_);
degenerate_ = false;
cheiralityException_ = false;
+
+ // Check landmark distance and reprojection errors to avoid outliers
+ double totalReprojError = 0.0;
+ size_t i=0;
+ BOOST_FOREACH(const Camera& camera, cameras) {
+ Point3 cameraTranslation = camera.pose().translation();
+ // we discard smart factors corresponding to points that are far away
+ if(cameraTranslation.distance(point_) > landmarkDistanceThreshold_){
+ degenerate_ = true;
+ break;
+ }
+ const Point2& zi = this->measured_.at(i);
+ try {
+ Point2 reprojectionError(camera.project(point_) - zi);
+ totalReprojError += reprojectionError.vector().norm();
+ } catch (CheiralityException) {
+ cheiralityException_ = true;
+ }
+ i += 1;
+ }
+ // we discard smart factors that have large reprojection error
+ if(dynamicOutlierRejectionThreshold_ > 0 &&
+ totalReprojError/m > dynamicOutlierRejectionThreshold_)
+ degenerate_ = true;
+
} catch (TriangulationUnderconstrainedException&) {
// if TriangulationUnderconstrainedException can be
// 1) There is a single pose for triangulation - this should not happen because we checked the number of poses before
@@ -385,7 +423,7 @@ public:
if (triangulateForLinearize(cameras))
return Base::createJacobianQFactor(cameras, point_, lambda);
else
- return boost::shared_ptr >();
+ return boost::make_shared< JacobianFactorQ >(this->keys_);
}
/// Create a factor, takes values
@@ -397,7 +435,16 @@ public:
if (nonDegenerate)
return createJacobianQFactor(myCameras, lambda);
else
- return boost::shared_ptr >();
+ return boost::make_shared< JacobianFactorQ >(this->keys_);
+ }
+
+ /// different (faster) way to compute Jacobian factor
+ boost::shared_ptr< JacobianFactor > createJacobianSVDFactor(const Cameras& cameras,
+ double lambda) const {
+ if (triangulateForLinearize(cameras))
+ return Base::createJacobianSVDFactor(cameras, point_, lambda);
+ else
+ return boost::make_shared< JacobianFactorSVD >(this->keys_);
}
/// Returns true if nonDegenerate
diff --git a/gtsam_unstable/slam/SmartProjectionPoseFactor.h b/gtsam/slam/SmartProjectionPoseFactor.h
similarity index 69%
rename from gtsam_unstable/slam/SmartProjectionPoseFactor.h
rename to gtsam/slam/SmartProjectionPoseFactor.h
index 879e5ab80..273102bda 100644
--- a/gtsam_unstable/slam/SmartProjectionPoseFactor.h
+++ b/gtsam/slam/SmartProjectionPoseFactor.h
@@ -22,6 +22,16 @@
#include "SmartProjectionFactor.h"
namespace gtsam {
+/**
+ *
+ * @addtogroup SLAM
+ *
+ * If you are using the factor, please cite:
+ * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating conditionally
+ * independent sets in factor graphs: a unifying perspective based on smart factors,
+ * Int. Conf. on Robotics and Automation (ICRA), 2014.
+ *
+ */
/**
* The calibration is known here. The factor only constraints poses (variable dimension is 6)
@@ -31,7 +41,8 @@ template
class SmartProjectionPoseFactor: public SmartProjectionFactor {
protected:
- // Known calibration
+ LinearizationMode linearizeTo_; ///< How to linearize the factor (HESSIAN, JACOBIAN_SVD, JACOBIAN_Q)
+
std::vector > K_all_; ///< shared pointer to calibration object (one for each camera)
public:
@@ -56,8 +67,11 @@ public:
*/
SmartProjectionPoseFactor(const double rankTol = 1,
const double linThreshold = -1, const bool manageDegeneracy = false,
- const bool enableEPI = false, boost::optional body_P_sensor = boost::none) :
- Base(rankTol, linThreshold, manageDegeneracy, enableEPI, body_P_sensor) {}
+ const bool enableEPI = false, boost::optional body_P_sensor = boost::none,
+ LinearizationMode linearizeTo = HESSIAN, double landmarkDistanceThreshold = 1e10,
+ double dynamicOutlierRejectionThreshold = -1) :
+ Base(rankTol, linThreshold, manageDegeneracy, enableEPI, body_P_sensor,
+ landmarkDistanceThreshold, dynamicOutlierRejectionThreshold), linearizeTo_(linearizeTo) {}
/** Virtual destructor */
virtual ~SmartProjectionPoseFactor() {}
@@ -65,7 +79,7 @@ public:
/**
* add a new measurement and pose key
* @param measured is the 2m dimensional location of the projection of a single landmark in the m view (the measurement)
- * @param poseKey is the index corresponding to the camera observing the same landmark
+ * @param poseKey is key corresponding to the camera observing the same landmark
* @param noise_i is the measurement noise
* @param K_i is the (known) camera calibration
*/
@@ -77,8 +91,11 @@ public:
}
/**
- * add a new measurements and pose keys
- * Variant of the previous one in which we include a set of measurements
+ * Variant of the previous one in which we include a set of measurements
+ * @param measurements vector of the 2m dimensional location of the projection of a single landmark in the m view (the measurement)
+ * @param poseKeys vector of keys corresponding to the camera observing the same landmark
+ * @param noises vector of measurement noises
+ * @param Ks vector of calibration objects
*/
void add(std::vector measurements, std::vector poseKeys,
std::vector noises,
@@ -90,8 +107,11 @@ public:
}
/**
- * add a new measurements and pose keys
* Variant of the previous one in which we include a set of measurements with the same noise and calibration
+ * @param mmeasurements vector of the 2m dimensional location of the projection of a single landmark in the m view (the measurement)
+ * @param poseKeys vector of keys corresponding to the camera observing the same landmark
+ * @param noise measurement noise (same for all measurements)
+ * @param K the (known) camera calibration (same for all measurements)
*/
void add(std::vector measurements, std::vector poseKeys,
const SharedNoiseModel noise, const boost::shared_ptr K) {
@@ -126,7 +146,12 @@ public:
return 6 * this->keys_.size();
}
- // Collect all cameras
+ /**
+ * Collect all cameras involved in this factor
+ * @param values Values structure which must contain camera poses corresponding
+ * to keys involved in this factor
+ * @return vector of Values
+ */
typename Base::Cameras cameras(const Values& values) const {
typename Base::Cameras cameras;
size_t i=0;
@@ -139,11 +164,24 @@ public:
}
/**
- * linearize returns a Hessian factor contraining the poses
+ * Linearize to Gaussian Factor
+ * @param values Values structure which must contain camera poses for this factor
+ * @return
*/
virtual boost::shared_ptr linearize(
const Values& values) const {
- return this->createHessianFactor(cameras(values));
+ // depending on flag set on construction we may linearize to different linear factors
+ switch(linearizeTo_){
+ case JACOBIAN_SVD :
+ return this->createJacobianSVDFactor(cameras(values), 0.0);
+ break;
+ case JACOBIAN_Q :
+ return this->createJacobianQFactor(cameras(values), 0.0);
+ break;
+ default:
+ return this->createHessianFactor(cameras(values));
+ break;
+ }
}
/**
@@ -158,7 +196,7 @@ public:
}
/** return the calibration object */
- inline const boost::shared_ptr calibration() const {
+ inline const std::vector > calibration() const {
return K_all_;
}
diff --git a/gtsam/slam/dataset.cpp b/gtsam/slam/dataset.cpp
index 4a7166f38..b3c9b9557 100644
--- a/gtsam/slam/dataset.cpp
+++ b/gtsam/slam/dataset.cpp
@@ -16,18 +16,18 @@
* @brief utility functions for loading datasets
*/
-#include
-#include
-#include
-
-#include
-
-#include
-#include