diff --git a/.github/scripts/python.sh b/.github/scripts/python.sh index d026aa123..08b8084a0 100644 --- a/.github/scripts/python.sh +++ b/.github/scripts/python.sh @@ -9,14 +9,13 @@ set -x -e # install TBB with _debug.so files function install_tbb() { + echo install_tbb if [ "$(uname)" == "Linux" ]; then sudo apt-get -y install libtbb-dev elif [ "$(uname)" == "Darwin" ]; then brew install tbb - fi - } if [ -z ${PYTHON_VERSION+x} ]; then @@ -37,19 +36,18 @@ function install_dependencies() export PATH=$PATH:$($PYTHON -c "import site; print(site.USER_BASE)")/bin - [ "${GTSAM_WITH_TBB:-OFF}" = "ON" ] && install_tbb - - $PYTHON -m pip install -r $GITHUB_WORKSPACE/python/requirements.txt + if [ "${GTSAM_WITH_TBB:-OFF}" == "ON" ]; then + install_tbb + fi } function build() { - mkdir $GITHUB_WORKSPACE/build - cd $GITHUB_WORKSPACE/build - + export CMAKE_GENERATOR=Ninja BUILD_PYBIND="ON" - - cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + cmake $GITHUB_WORKSPACE \ + -B build \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ -DGTSAM_BUILD_TESTS=OFF \ -DGTSAM_BUILD_UNSTABLE=${GTSAM_BUILD_UNSTABLE:-ON} \ -DGTSAM_USE_QUATERNIONS=OFF \ @@ -65,16 +63,16 @@ function build() # Set to 2 cores so that Actions does not error out during resource provisioning. - make -j2 install + cmake --build build -j2 - cd $GITHUB_WORKSPACE/build/python - $PYTHON -m pip install --user . + $PYTHON -m pip install --user build/python } function test() { cd $GITHUB_WORKSPACE/python/gtsam/tests $PYTHON -m unittest discover -v + cd $GITHUB_WORKSPACE } # select between build or test diff --git a/.github/scripts/unix.sh b/.github/scripts/unix.sh index 557255474..09fcd788b 100644 --- a/.github/scripts/unix.sh +++ b/.github/scripts/unix.sh @@ -5,33 +5,30 @@ # Specifically Linux and macOS. ########################################################## +set -e # Make sure any error makes the script to return an error code +set -x # echo + # install TBB with _debug.so files function install_tbb() { + echo install_tbb if [ "$(uname)" == "Linux" ]; then sudo apt-get -y install libtbb-dev elif [ "$(uname)" == "Darwin" ]; then brew install tbb - fi - } # common tasks before either build or test function configure() { - set -e # Make sure any error makes the script to return an error code - set -x # echo + # delete old build + rm -rf build - SOURCE_DIR=$GITHUB_WORKSPACE - BUILD_DIR=$GITHUB_WORKSPACE/build - - #env - rm -fr $BUILD_DIR || true - mkdir $BUILD_DIR && cd $BUILD_DIR - - [ "${GTSAM_WITH_TBB:-OFF}" = "ON" ] && install_tbb + if [ "${GTSAM_WITH_TBB:-OFF}" == "ON" ]; then + install_tbb + fi if [ ! -z "$GCC_VERSION" ]; then export CC=gcc-$GCC_VERSION @@ -40,7 +37,9 @@ function configure() # GTSAM_BUILD_WITH_MARCH_NATIVE=OFF: to avoid crashes in builder VMs # CMAKE_CXX_FLAGS="-w": Suppress warnings to avoid IO latency in CI logs - cmake $SOURCE_DIR \ + export CMAKE_GENERATOR=Ninja + cmake $GITHUB_WORKSPACE \ + -B build \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Debug} \ -DCMAKE_CXX_FLAGS="-w" \ -DGTSAM_BUILD_TESTS=${GTSAM_BUILD_TESTS:-OFF} \ @@ -62,9 +61,9 @@ function configure() function finish () { # Print ccache stats - [ -x "$(command -v ccache)" ] && ccache -s - - cd $SOURCE_DIR + if [ -x "$(command -v ccache)" ]; then + ccache -s + fi } # compile the code with the intent of populating the cache @@ -77,12 +76,12 @@ function build () if [ "$(uname)" == "Linux" ]; then if (($(nproc) > 2)); then - make -j4 + cmake --build build -j4 else - make -j2 + cmake --build build -j2 fi elif [ "$(uname)" == "Darwin" ]; then - make -j$(sysctl -n hw.physicalcpu) + cmake --build build -j$(sysctl -n hw.physicalcpu) fi finish @@ -99,12 +98,12 @@ function test () # Actual testing if [ "$(uname)" == "Linux" ]; then if (($(nproc) > 2)); then - make -j$(nproc) check + cmake --build build -j$(nproc) --target check else - make -j2 check + cmake --build build -j2 --target check fi elif [ "$(uname)" == "Darwin" ]; then - make -j$(sysctl -n hw.physicalcpu) check + cmake --build build -j$(sysctl -n hw.physicalcpu) --target check fi finish @@ -118,4 +117,4 @@ case $1 in -t) test ;; -esac \ No newline at end of file +esac diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index e4937ce06..5de09c63f 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -73,7 +73,7 @@ jobs: fi sudo apt-get -y update - sudo apt-get -y install cmake build-essential pkg-config libpython3-dev python3-numpy libicu-dev + sudo apt-get -y install cmake build-essential pkg-config libpython3-dev python3-numpy libicu-dev ninja-build if [ "${{ matrix.compiler }}" = "gcc" ]; then sudo apt-get install -y g++-${{ matrix.version }} g++-${{ matrix.version }}-multilib diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index ca4645a77..91bc4e80a 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -75,7 +75,7 @@ jobs: fi sudo apt-get -y update - sudo apt-get -y install cmake build-essential pkg-config libpython3-dev python3-numpy libboost-all-dev + sudo apt-get -y install cmake build-essential pkg-config libpython3-dev python3-numpy libboost-all-dev ninja-build if [ "${{ matrix.compiler }}" = "gcc" ]; then sudo apt-get install -y g++-${{ matrix.version }} g++-${{ matrix.version }}-multilib @@ -109,10 +109,13 @@ jobs: with: swap-size-gb: 6 - - name: Install Dependencies + - name: Install System Dependencies run: | bash .github/scripts/python.sh -d + - name: Install Python Dependencies + run: python$PYTHON_VERSION -m pip install -r python/dev_requirements.txt + - name: Build run: | bash .github/scripts/python.sh -b diff --git a/.github/workflows/build-special.yml b/.github/workflows/build-special.yml index 72466ffd6..164646e3e 100644 --- a/.github/workflows/build-special.yml +++ b/.github/workflows/build-special.yml @@ -103,7 +103,7 @@ jobs: sudo add-apt-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy main" fi - sudo apt-get -y install cmake build-essential pkg-config libpython3-dev python3-numpy libicu-dev + sudo apt-get -y install cmake build-essential pkg-config libpython3-dev python3-numpy libicu-dev ninja-build if [ "${{ matrix.compiler }}" = "gcc" ]; then sudo apt-get install -y g++-${{ matrix.version }} g++-${{ matrix.version }}-multilib diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index cbe0c10f1..f0568394f 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -34,6 +34,7 @@ jobs: Debug, Release ] + build_unstable: [ON] include: - name: windows-2019-cl @@ -93,32 +94,66 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Setup msbuild + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x${{ matrix.platform }} + - name: Configuration + shell: bash run: | + export CMAKE_GENERATOR=Ninja cmake -E remove_directory build - cmake -B build -S . -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF -DBOOST_ROOT="${env:BOOST_ROOT}" -DBOOST_INCLUDEDIR="${env:BOOST_ROOT}\boost\include" -DBOOST_LIBRARYDIR="${env:BOOST_ROOT}\lib" + cmake -B build \ + -S . \ + -DGTSAM_BUILD_EXAMPLES_ALWAYS=OFF \ + -DGTSAM_ALLOW_DEPRECATED_SINCE_V43=OFF \ + -DBOOST_ROOT="${BOOST_ROOT}" \ + -DBOOST_INCLUDEDIR="${BOOST_ROOT}\boost\include" \ + -DBOOST_LIBRARYDIR="${BOOST_ROOT}\lib" - name: Build + shell: bash run: | # Since Visual Studio is a multi-generator, we need to use --config # https://stackoverflow.com/a/24470998/1236990 - cmake --build build -j 4 --config ${{ matrix.build_type }} --target gtsam - cmake --build build -j 4 --config ${{ matrix.build_type }} --target gtsam_unstable - cmake --build build -j 4 --config ${{ matrix.build_type }} --target wrap + cmake --build build -j4 --config ${{ matrix.build_type }} --target gtsam + cmake --build build -j4 --config ${{ matrix.build_type }} --target gtsam_unstable + - name: Test + shell: bash + run: | # Run GTSAM tests - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.base - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.basis - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.discrete - #cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.geometry - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.inference - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.linear - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.navigation - #cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.nonlinear - #cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.sam - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.sfm - #cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.slam - cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.symbolic + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.base + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.basis + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.discrete + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.geometry + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.inference + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.linear + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.navigation + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.sam + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.sfm + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.symbolic + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.hybrid + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.nonlinear + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.slam # Run GTSAM_UNSTABLE tests - #cmake --build build -j 4 --config ${{ matrix.build_type }} --target check.base_unstable + cmake --build build -j4 --config ${{ matrix.build_type }} --target check.base_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.geometry_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.linear_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.discrete_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.dynamics_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.nonlinear_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.slam_unstable + # Compile. Fail with exception + # cmake --build build -j4 --config ${{ matrix.build_type }} --target check.partition + + # Run all tests + # cmake --build build -j1 --config ${{ matrix.build_type }} --target check diff --git a/CMakeLists.txt b/CMakeLists.txt index ebe27443a..16848a721 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,5 @@ cmake_minimum_required(VERSION 3.0) -# new feature to Cmake Version > 2.8.12 -# Mac ONLY. Define Relative Path on Mac OS -if(NOT DEFINED CMAKE_MACOSX_RPATH) - set(CMAKE_MACOSX_RPATH 0) -endif() - # Set the version number for the library set (GTSAM_VERSION_MAJOR 4) set (GTSAM_VERSION_MINOR 3) @@ -13,18 +7,16 @@ set (GTSAM_VERSION_PATCH 0) set (GTSAM_PRERELEASE_VERSION "a0") math (EXPR GTSAM_VERSION_NUMERIC "10000 * ${GTSAM_VERSION_MAJOR} + 100 * ${GTSAM_VERSION_MINOR} + ${GTSAM_VERSION_PATCH}") -if (${GTSAM_VERSION_PATCH} EQUAL 0) - set (GTSAM_VERSION_STRING "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}${GTSAM_PRERELEASE_VERSION}") +if ("${GTSAM_PRERELEASE_VERSION}" STREQUAL "") + set (GTSAM_VERSION_STRING "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}.${GTSAM_VERSION_PATCH}") else() - set (GTSAM_VERSION_STRING "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}.${GTSAM_VERSION_PATCH}${GTSAM_PRERELEASE_VERSION}") + set (GTSAM_VERSION_STRING "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}${GTSAM_PRERELEASE_VERSION}") endif() project(GTSAM LANGUAGES CXX C VERSION "${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}.${GTSAM_VERSION_PATCH}") -message(STATUS "GTSAM Version: ${GTSAM_VERSION_STRING}") - set (CMAKE_PROJECT_VERSION_MAJOR ${GTSAM_VERSION_MAJOR}) set (CMAKE_PROJECT_VERSION_MINOR ${GTSAM_VERSION_MINOR}) set (CMAKE_PROJECT_VERSION_PATCH ${GTSAM_VERSION_PATCH}) @@ -32,17 +24,18 @@ set (CMAKE_PROJECT_VERSION_PATCH ${GTSAM_VERSION_PATCH}) ############################################################################### # Gather information, perform checks, set defaults +if(MSVC) + set(MSVC_LINKER_FLAGS "/FORCE:MULTIPLE") + set(CMAKE_EXE_LINKER_FLAGS ${MSVC_LINKER_FLAGS}) + set(CMAKE_MODULE_LINKER_FLAGS ${MSVC_LINKER_FLAGS}) + set(CMAKE_SHARED_LINKER_FLAGS ${MSVC_LINKER_FLAGS}) + set(CMAKE_STATIC_LINKER_FLAGS ${MSVC_LINKER_FLAGS}) +endif() + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(GtsamMakeConfigFile) include(GNUInstallDirs) -# Load build type flags and default to Debug mode -include(GtsamBuildTypes) - -# Use macros for creating tests/timing scripts -include(GtsamTesting) -include(GtsamPrinting) - # guard against in-source builds if(${GTSAM_SOURCE_DIR} STREQUAL ${GTSAM_BINARY_DIR}) message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ") @@ -50,6 +43,13 @@ endif() include(cmake/HandleGeneralOptions.cmake) # CMake build options +# Load build type flags and default to Debug mode +include(GtsamBuildTypes) + +# Use macros for creating tests/timing scripts +include(GtsamTesting) +include(GtsamPrinting) + ############### Decide on BOOST ###################################### # Enable or disable serialization with GTSAM_ENABLE_BOOST_SERIALIZATION option(GTSAM_ENABLE_BOOST_SERIALIZATION "Enable Boost serialization" ON) diff --git a/INSTALL.md b/INSTALL.md index f148e3718..10bee196c 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -182,7 +182,7 @@ Here are some tips to get the best possible performance out of GTSAM. optimization by 30-50%. Please note that this may not be true for very small problems where the overhead of dispatching work to multiple threads outweighs the benefit. We recommend that you benchmark your problem with/without TBB. -3. Add `-march=native` to `GTSAM_CMAKE_CXX_FLAGS`. A performance gain of +3. Use `GTSAM_BUILD_WITH_MARCH_NATIVE`. A performance gain of 25-30% can be expected on modern processors. Note that this affects the portability of your executable. It may not run when copied to another system with older/different processor architecture. diff --git a/README.md b/README.md index 1ccde9738..40b84dff8 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **As of January 2023, the `develop` branch is officially in "Pre 4.3" mode. We envision several API-breaking changes as we switch to C++17 and away from boost.** -In addition, features deprecated in 4.2 will be removed. Please use the last [4.2a8 release](https://github.com/borglab/gtsam/releases/tag/4.2a8) if you need those features. However, most are easily converted and can be tracked down (in 4.2) by disabling the cmake flag `GTSAM_ALLOW_DEPRECATED_SINCE_V42`. +In addition, features deprecated in 4.2 will be removed. Please use the stable [4.2 release](https://github.com/borglab/gtsam/releases/tag/4.2) if you need those features. However, most are easily converted and can be tracked down (in 4.2) by disabling the cmake flag `GTSAM_ALLOW_DEPRECATED_SINCE_V42`. ## What is GTSAM? diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in index cc2a7df8f..36906d090 100644 --- a/cmake/Config.cmake.in +++ b/cmake/Config.cmake.in @@ -21,6 +21,10 @@ else() find_dependency(Boost @BOOST_FIND_MINIMUM_VERSION@ COMPONENTS @BOOST_FIND_MINIMUM_COMPONENTS@) endif() +if(@GTSAM_USE_TBB@) + find_dependency(TBB 4.4 COMPONENTS tbb tbbmalloc) +endif() + if(@GTSAM_USE_SYSTEM_EIGEN@) find_dependency(Eigen3 REQUIRED) endif() diff --git a/cmake/FindTBB.cmake b/cmake/FindTBB.cmake deleted file mode 100644 index 0ecd4ca0e..000000000 --- a/cmake/FindTBB.cmake +++ /dev/null @@ -1,323 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2015 Justus Calvin -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# -# FindTBB -# ------- -# -# Find TBB include directories and libraries. -# -# Usage: -# -# find_package(TBB [major[.minor]] [EXACT] -# [QUIET] [REQUIRED] -# [[COMPONENTS] [components...]] -# [OPTIONAL_COMPONENTS components...]) -# -# where the allowed components are tbbmalloc and tbb_preview. Users may modify -# the behavior of this module with the following variables: -# -# * TBB_ROOT_DIR - The base directory the of TBB installation. -# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. -# * TBB_LIBRARY - The directory that contains the TBB library files. -# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. -# These libraries, if specified, override the -# corresponding library search results, where -# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, -# tbb_preview, or tbb_preview_debug. -# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will -# be used instead of the release version. -# -# Users may modify the behavior of this module with the following environment -# variables: -# -# * TBB_INSTALL_DIR -# * TBBROOT -# * LIBRARY_PATH -# -# This module will set the following variables: -# -# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or -# don’t want to use TBB. -# * TBB__FOUND - If False, optional part of TBB sytem is -# not available. -# * TBB_VERSION - The full version string -# * TBB_VERSION_MAJOR - The major version -# * TBB_VERSION_MINOR - The minor version -# * TBB_INTERFACE_VERSION - The interface version number defined in -# tbb/tbb_stddef.h. -# * TBB__LIBRARY_RELEASE - The path of the TBB release version of -# , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or -# tbb_preview_debug. -# * TBB__LIBRARY_DEGUG - The path of the TBB release version of -# , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or -# tbb_preview_debug. -# -# The following varibles should be used to build and link with TBB: -# -# * TBB_INCLUDE_DIRS - The include directory for TBB. -# * TBB_LIBRARIES - The libraries to link against to use TBB. -# * TBB_LIBRARIES_RELEASE - The release libraries to link against to use TBB. -# * TBB_LIBRARIES_DEBUG - The debug libraries to link against to use TBB. -# * TBB_DEFINITIONS - Definitions to use when compiling code that uses -# TBB. -# * TBB_DEFINITIONS_RELEASE - Definitions to use when compiling release code that -# uses TBB. -# * TBB_DEFINITIONS_DEBUG - Definitions to use when compiling debug code that -# uses TBB. -# -# This module will also create the "tbb" target that may be used when building -# executables and libraries. - -include(FindPackageHandleStandardArgs) - -if(NOT TBB_FOUND) - - ################################## - # Check the build type - ################################## - - if(NOT DEFINED TBB_USE_DEBUG_BUILD) - # Set build type to RELEASE by default for optimization. - set(TBB_BUILD_TYPE RELEASE) - elseif(TBB_USE_DEBUG_BUILD) - set(TBB_BUILD_TYPE DEBUG) - else() - set(TBB_BUILD_TYPE RELEASE) - endif() - - ################################## - # Set the TBB search directories - ################################## - - # Define search paths based on user input and environment variables - set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) - - # Define the search directories based on the current platform - if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" - "C:/Program Files (x86)/Intel/TBB") - - # Set the target architecture - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(TBB_ARCHITECTURE "intel64") - else() - set(TBB_ARCHITECTURE "ia32") - endif() - - # Set the TBB search library path search suffix based on the version of VC - if(WINDOWS_STORE) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11_ui") - elseif(MSVC14) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc14") - elseif(MSVC12) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc12") - elseif(MSVC11) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc11") - elseif(MSVC10) - set(TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc10") - endif() - - # Add the library path search suffix for the VC independent version of TBB - list(APPEND TBB_LIB_PATH_SUFFIX "lib/${TBB_ARCHITECTURE}/vc_mt") - - elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # OS X - set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb" - "/usr/local/opt/tbb") - - # TODO: Check to see which C++ library is being used by the compiler. - if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) - # The default C++ library on OS X 10.9 and later is libc++ - set(TBB_LIB_PATH_SUFFIX "lib/libc++" "lib") - else() - set(TBB_LIB_PATH_SUFFIX "lib") - endif() - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - # Linux - set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") - - # TODO: Check compiler version to see the suffix should be /gcc4.1 or - # /gcc4.1. For now, assume that the compiler is more recent than - # gcc 4.4.x or later. - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") - set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") - endif() - endif() - - ################################## - # Find the TBB include dir - ################################## - - find_path(TBB_INCLUDE_DIRS tbb/tbb.h - HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} - PATH_SUFFIXES include) - - ################################## - # Set version strings - ################################## - - if(TBB_INCLUDE_DIRS) - set(_tbb_version_file_prior_to_tbb_2021_1 "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h") - set(_tbb_version_file_after_tbb_2021_1 "${TBB_INCLUDE_DIRS}/oneapi/tbb/version.h") - - if (EXISTS "${_tbb_version_file_prior_to_tbb_2021_1}") - file(READ "${_tbb_version_file_prior_to_tbb_2021_1}" _tbb_version_file ) - elseif (EXISTS "${_tbb_version_file_after_tbb_2021_1}") - file(READ "${_tbb_version_file_after_tbb_2021_1}" _tbb_version_file ) - else() - message(FATAL_ERROR "Found TBB installation: ${TBB_INCLUDE_DIRS} " - "missing version header.") - endif() - - string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" - TBB_VERSION_MAJOR "${_tbb_version_file}") - string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" - TBB_VERSION_MINOR "${_tbb_version_file}") - string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" - TBB_INTERFACE_VERSION "${_tbb_version_file}") - set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") - endif() - - ################################## - # Find TBB components - ################################## - - if(TBB_VERSION VERSION_LESS 4.3) - set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc tbb) - else() - set(TBB_SEARCH_COMPOMPONENTS tbb_preview tbbmalloc_proxy tbbmalloc tbb) - endif() - - # Find each component - foreach(_comp ${TBB_SEARCH_COMPOMPONENTS}) - if(";${TBB_FIND_COMPONENTS};tbb;" MATCHES ";${_comp};") - - # Search for the libraries - find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp} - HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH - PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - - find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}_debug - HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH - PATH_SUFFIXES ${TBB_LIB_PATH_SUFFIX}) - - if(TBB_${_comp}_LIBRARY_DEBUG) - list(APPEND TBB_LIBRARIES_DEBUG "${TBB_${_comp}_LIBRARY_DEBUG}") - endif() - if(TBB_${_comp}_LIBRARY_RELEASE) - list(APPEND TBB_LIBRARIES_RELEASE "${TBB_${_comp}_LIBRARY_RELEASE}") - endif() - if(TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE} AND NOT TBB_${_comp}_LIBRARY) - set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_${TBB_BUILD_TYPE}}") - endif() - - if(TBB_${_comp}_LIBRARY AND EXISTS "${TBB_${_comp}_LIBRARY}") - set(TBB_${_comp}_FOUND TRUE) - else() - set(TBB_${_comp}_FOUND FALSE) - endif() - - # Mark internal variables as advanced - mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) - mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) - mark_as_advanced(TBB_${_comp}_LIBRARY) - - endif() - endforeach() - - ################################## - # Set compile flags and libraries - ################################## - - set(TBB_DEFINITIONS_RELEASE "") - set(TBB_DEFINITIONS_DEBUG "-DTBB_USE_DEBUG=1") - - if(TBB_LIBRARIES_${TBB_BUILD_TYPE}) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_${TBB_BUILD_TYPE}}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_${TBB_BUILD_TYPE}}") - elseif(TBB_LIBRARIES_RELEASE) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_RELEASE}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_RELEASE}") - elseif(TBB_LIBRARIES_DEBUG) - set(TBB_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}") - set(TBB_LIBRARIES "${TBB_LIBRARIES_DEBUG}") - endif() - - find_package_handle_standard_args(TBB - REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES - HANDLE_COMPONENTS - VERSION_VAR TBB_VERSION) - - ################################## - # Create targets - ################################## - - if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND) - # Start fix to support different targets for tbb, tbbmalloc, etc. - # (Jose Luis Blanco, Jan 2019) - # Iterate over tbb, tbbmalloc, etc. - foreach(libname ${TBB_SEARCH_COMPOMPONENTS}) - if ((NOT TBB_${libname}_LIBRARY_RELEASE) AND (NOT TBB_${libname}_LIBRARY_DEBUG)) - continue() - endif() - - add_library(${libname} SHARED IMPORTED) - - set_target_properties(${libname} PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS} - IMPORTED_LOCATION ${TBB_${libname}_LIBRARY_RELEASE}) - if(TBB_${libname}_LIBRARY_RELEASE AND TBB_${libname}_LIBRARY_DEBUG) - set_target_properties(${libname} PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "$<$,$>:TBB_USE_DEBUG=1>" - IMPORTED_LOCATION_DEBUG ${TBB_${libname}_LIBRARY_DEBUG} - IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_${libname}_LIBRARY_DEBUG} - IMPORTED_LOCATION_RELEASE ${TBB_${libname}_LIBRARY_RELEASE} - IMPORTED_LOCATION_MINSIZEREL ${TBB_${libname}_LIBRARY_RELEASE} - ) - elseif(TBB_${libname}_LIBRARY_RELEASE) - set_target_properties(${libname} PROPERTIES IMPORTED_LOCATION ${TBB_${libname}_LIBRARY_RELEASE}) - else() - set_target_properties(${libname} PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "${TBB_DEFINITIONS_DEBUG}" - IMPORTED_LOCATION ${TBB_${libname}_LIBRARY_DEBUG} - ) - endif() - endforeach() - # End of fix to support different targets - endif() - - mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) - - unset(TBB_ARCHITECTURE) - unset(TBB_BUILD_TYPE) - unset(TBB_LIB_PATH_SUFFIX) - unset(TBB_DEFAULT_SEARCH_DIR) - -endif() diff --git a/cmake/GtsamBuildTypes.cmake b/cmake/GtsamBuildTypes.cmake index b24be5f08..2aad58abb 100644 --- a/cmake/GtsamBuildTypes.cmake +++ b/cmake/GtsamBuildTypes.cmake @@ -55,9 +55,6 @@ if(NOT CMAKE_BUILD_TYPE AND NOT MSVC AND NOT XCODE_VERSION) "Choose the type of build, options are: None Debug Release Timing Profiling RelWithDebInfo." FORCE) endif() -# Add option for using build type postfixes to allow installing multiple build modes -option(GTSAM_BUILD_TYPE_POSTFIXES "Enable/Disable appending the build type to the name of compiled libraries" ON) - # Define all cache variables, to be populated below depending on the OS/compiler: set(GTSAM_COMPILE_OPTIONS_PRIVATE "" CACHE INTERNAL "(Do not edit) Private compiler flags for all build configurations." FORCE) set(GTSAM_COMPILE_OPTIONS_PUBLIC "" CACHE INTERNAL "(Do not edit) Public compiler flags (exported to user projects) for all build configurations." FORCE) @@ -82,6 +79,13 @@ set(GTSAM_COMPILE_DEFINITIONS_PRIVATE_RELWITHDEBINFO "NDEBUG" CACHE STRING "(Us set(GTSAM_COMPILE_DEFINITIONS_PRIVATE_RELEASE "NDEBUG" CACHE STRING "(User editable) Private preprocessor macros for Release configuration.") set(GTSAM_COMPILE_DEFINITIONS_PRIVATE_PROFILING "NDEBUG" CACHE STRING "(User editable) Private preprocessor macros for Profiling configuration.") set(GTSAM_COMPILE_DEFINITIONS_PRIVATE_TIMING "NDEBUG;ENABLE_TIMING" CACHE STRING "(User editable) Private preprocessor macros for Timing configuration.") + +mark_as_advanced(GTSAM_COMPILE_DEFINITIONS_PRIVATE_DEBUG) +mark_as_advanced(GTSAM_COMPILE_DEFINITIONS_PRIVATE_RELWITHDEBINFO) +mark_as_advanced(GTSAM_COMPILE_DEFINITIONS_PRIVATE_RELEASE) +mark_as_advanced(GTSAM_COMPILE_DEFINITIONS_PRIVATE_PROFILING) +mark_as_advanced(GTSAM_COMPILE_DEFINITIONS_PRIVATE_TIMING) + if(MSVC) # Common to all configurations: list_append_cache(GTSAM_COMPILE_DEFINITIONS_PRIVATE @@ -143,6 +147,13 @@ else() set(GTSAM_COMPILE_OPTIONS_PRIVATE_TIMING -g -O3 CACHE STRING "(User editable) Private compiler flags for Timing configuration.") endif() +mark_as_advanced(GTSAM_COMPILE_OPTIONS_PRIVATE_COMMON) +mark_as_advanced(GTSAM_COMPILE_OPTIONS_PRIVATE_DEBUG) +mark_as_advanced(GTSAM_COMPILE_OPTIONS_PRIVATE_RELWITHDEBINFO) +mark_as_advanced(GTSAM_COMPILE_OPTIONS_PRIVATE_RELEASE) +mark_as_advanced(GTSAM_COMPILE_OPTIONS_PRIVATE_PROFILING) +mark_as_advanced(GTSAM_COMPILE_OPTIONS_PRIVATE_TIMING) + # Enable C++17: if (NOT CMAKE_VERSION VERSION_LESS 3.8) set(GTSAM_COMPILE_FEATURES_PUBLIC "cxx_std_17" CACHE STRING "CMake compile features property for all gtsam targets.") @@ -198,7 +209,6 @@ if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") endif() if (NOT MSVC) - option(GTSAM_BUILD_WITH_MARCH_NATIVE "Enable/Disable building with all instructions supported by native architecture (binary may not be portable!)" OFF) if(GTSAM_BUILD_WITH_MARCH_NATIVE) # Check if Apple OS and compiler is [Apple]Clang if(APPLE AND (${CMAKE_CXX_COMPILER_ID} MATCHES "^(Apple)?Clang$")) diff --git a/cmake/GtsamTesting.cmake b/cmake/GtsamTesting.cmake index 573fb696a..47b059213 100644 --- a/cmake/GtsamTesting.cmake +++ b/cmake/GtsamTesting.cmake @@ -42,7 +42,7 @@ endmacro() # GTSAM_BUILD_EXAMPLES_ALWAYS is enabled. They may also be built with 'make examples'. # # Usage example: -# gtsamAddExamplesGlob("*.cpp" "BrokenExample.cpp" "gtsam;GeographicLib") +# gtsamAddExamplesGlob("*.cpp" "BrokenExample.cpp" "gtsam;GeographicLib" ON) # # Arguments: # globPatterns: The list of files or glob patterns from which to create examples, with @@ -51,8 +51,9 @@ endmacro() # excludedFiles: A list of files or globs to exclude, e.g. "C*.cpp;BrokenExample.cpp". Pass # an empty string "" if nothing needs to be excluded. # linkLibraries: The list of libraries to link to. -macro(gtsamAddExamplesGlob globPatterns excludedFiles linkLibraries) - gtsamAddExesGlob_impl("${globPatterns}" "${excludedFiles}" "${linkLibraries}" "examples" ${GTSAM_BUILD_EXAMPLES_ALWAYS}) +# buildWithAll: Build examples with `make` and/or `make all` +macro(gtsamAddExamplesGlob globPatterns excludedFiles linkLibraries buildWithAll) + gtsamAddExesGlob_impl("${globPatterns}" "${excludedFiles}" "${linkLibraries}" "examples" ${buildWithAll}) endmacro() @@ -76,8 +77,9 @@ endmacro() # excludedFiles: A list of files or globs to exclude, e.g. "C*.cpp;BrokenExample.cpp". Pass # an empty string "" if nothing needs to be excluded. # linkLibraries: The list of libraries to link to. -macro(gtsamAddTimingGlob globPatterns excludedFiles linkLibraries) - gtsamAddExesGlob_impl("${globPatterns}" "${excludedFiles}" "${linkLibraries}" "timing" ${GTSAM_BUILD_TIMING_ALWAYS}) +# buildWithAll: Build examples with `make` and/or `make all` +macro(gtsamAddTimingGlob globPatterns excludedFiles linkLibraries buildWithAll) + gtsamAddExesGlob_impl("${globPatterns}" "${excludedFiles}" "${linkLibraries}" "timing" ${buildWithAll}) endmacro() @@ -86,9 +88,8 @@ endmacro() # Build macros for using tests enable_testing() -option(GTSAM_BUILD_TESTS "Enable/Disable building of tests" ON) -option(GTSAM_BUILD_EXAMPLES_ALWAYS "Build examples with 'make all' (build with 'make examples' if not)" ON) -option(GTSAM_BUILD_TIMING_ALWAYS "Build timing scripts with 'make all' (build with 'make timing' if not" OFF) +#TODO(Varun) Move to HandlePrintConfiguration.cmake. This will require additional changes. +option(GTSAM_BUILD_TESTS "Enable/Disable building of tests" ON) # Add option for combining unit tests if(MSVC OR XCODE_VERSION) @@ -123,6 +124,7 @@ add_custom_target(timing) # Implementations of this file's macros: macro(gtsamAddTestsGlob_impl groupName globPatterns excludedFiles linkLibraries) + #TODO(Varun) Building of tests should not depend on global gtsam flag if(GTSAM_BUILD_TESTS) # Add group target if it doesn't already exist if(NOT TARGET check.${groupName}) diff --git a/cmake/HandleGeneralOptions.cmake b/cmake/HandleGeneralOptions.cmake index 13000a5b0..302bdf860 100644 --- a/cmake/HandleGeneralOptions.cmake +++ b/cmake/HandleGeneralOptions.cmake @@ -8,6 +8,18 @@ else() set(GTSAM_UNSTABLE_AVAILABLE 0) endif() +### GtsamTesting related options +option(GTSAM_BUILD_EXAMPLES_ALWAYS "Build examples with 'make all' (build with 'make examples' if not)" ON) +option(GTSAM_BUILD_TIMING_ALWAYS "Build timing scripts with 'make all' (build with 'make timing' if not" OFF) +### + +# Add option for using build type postfixes to allow installing multiple build modes +option(GTSAM_BUILD_TYPE_POSTFIXES "Enable/Disable appending the build type to the name of compiled libraries" ON) + +if (NOT MSVC) + option(GTSAM_BUILD_WITH_MARCH_NATIVE "Enable/Disable building with all instructions supported by native architecture (binary may not be portable!)" OFF) +endif() + # Configurable Options if(GTSAM_UNSTABLE_AVAILABLE) option(GTSAM_BUILD_UNSTABLE "Enable/Disable libgtsam_unstable" ON) diff --git a/cmake/HandleTBB.cmake b/cmake/HandleTBB.cmake index fb944ba5b..393aeb345 100644 --- a/cmake/HandleTBB.cmake +++ b/cmake/HandleTBB.cmake @@ -14,7 +14,7 @@ if (GTSAM_WITH_TBB) endif() # all definitions and link requisites will go via imported targets: # tbb & tbbmalloc - list(APPEND GTSAM_ADDITIONAL_LIBRARIES tbb tbbmalloc) + list(APPEND GTSAM_ADDITIONAL_LIBRARIES TBB::tbb TBB::tbbmalloc) else() set(GTSAM_USE_TBB 0) # This will go into config.h endif() diff --git a/cmake/README.html b/cmake/README.html index 8170cd489..9cdb5c758 100644 --- a/cmake/README.html +++ b/cmake/README.html @@ -47,9 +47,9 @@ linkLibraries: The list of libraries to link to in addition to CppUnitLite.
  • -

    gtsamAddExamplesGlob(globPatterns excludedFiles linkLibraries) Add scripts that will serve as examples of how to use the library. A list of files or glob patterns is specified, and one executable will be created for each matching .cpp file. These executables will not be installed. They are build with 'make all' if GTSAM_BUILD_EXAMPLES_ALWAYS is enabled. They may also be built with 'make examples'.

    +

    gtsamAddExamplesGlob(globPatterns excludedFiles linkLibraries buildWithAll) Add scripts that will serve as examples of how to use the library. A list of files or glob patterns is specified, and one executable will be created for each matching .cpp file. These executables will not be installed. They are build with 'make all' if GTSAM_BUILD_EXAMPLES_ALWAYS is enabled. They may also be built with 'make examples'.

    Usage example:

    -
    gtsamAddExamplesGlob("*.cpp" "BrokenExample.cpp" "gtsam;GeographicLib")
    +
    gtsamAddExamplesGlob("*.cpp" "BrokenExample.cpp" "gtsam;GeographicLib" ON)
     

    Arguments:

    globPatterns:  The list of files or glob patterns from which to create unit tests, with
    @@ -58,6 +58,7 @@ linkLibraries: The list of libraries to link to in addition to CppUnitLite.
     excludedFiles: A list of files or globs to exclude, e.g. "C*.cpp;BrokenExample.cpp".  Pass
                    an empty string "" if nothing needs to be excluded.
     linkLibraries: The list of libraries to link to.
    +buildWithAll: Build examples with `make` and/or `make all`
     
  • diff --git a/cmake/README.md b/cmake/README.md index 569a401b1..4203b8d3c 100644 --- a/cmake/README.md +++ b/cmake/README.md @@ -52,11 +52,11 @@ Defines two useful functions for creating CTest unit tests. Also immediately cr Pass an empty string "" if nothing needs to be excluded. linkLibraries: The list of libraries to link to in addition to CppUnitLite. -* `gtsamAddExamplesGlob(globPatterns excludedFiles linkLibraries)` Add scripts that will serve as examples of how to use the library. A list of files or glob patterns is specified, and one executable will be created for each matching .cpp file. These executables will not be installed. They are build with 'make all' if GTSAM_BUILD_EXAMPLES_ALWAYS is enabled. They may also be built with 'make examples'. +* `gtsamAddExamplesGlob(globPatterns excludedFiles linkLibraries buildWithAll)` Add scripts that will serve as examples of how to use the library. A list of files or glob patterns is specified, and one executable will be created for each matching .cpp file. These executables will not be installed. They are build with 'make all' if GTSAM_BUILD_EXAMPLES_ALWAYS is enabled. They may also be built with 'make examples'. Usage example: - gtsamAddExamplesGlob("*.cpp" "BrokenExample.cpp" "gtsam;GeographicLib") + gtsamAddExamplesGlob("*.cpp" "BrokenExample.cpp" "gtsam;GeographicLib" ON) Arguments: @@ -66,6 +66,7 @@ Defines two useful functions for creating CTest unit tests. Also immediately cr excludedFiles: A list of files or globs to exclude, e.g. "C*.cpp;BrokenExample.cpp". Pass an empty string "" if nothing needs to be excluded. linkLibraries: The list of libraries to link to. + buildWithAll: Build examples with `make` and/or `make all` ## GtsamMakeConfigFile diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index f0975821f..05e0a13ef 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -56,7 +56,7 @@ if (GTSAM_BUILD_DOCS) if (GTSAM_BUILD_UNSTABLE) list(APPEND doc_subdirs ${gtsam_unstable_doc_subdirs}) endif() - if (GTSAM_BUILD_EXAMPLES) + if (GTSAM_BUILD_EXAMPLES_ALWAYS) list(APPEND doc_subdirs examples) endif() diff --git a/doc/Hybrid.lyx b/doc/Hybrid.lyx index c854a4845..44df81e39 100644 --- a/doc/Hybrid.lyx +++ b/doc/Hybrid.lyx @@ -86,7 +86,7 @@ Hybrid Inference \end_layout \begin_layout Author -Frank Dellaert & Varun Agrawal +Frank Dellaert \end_layout \begin_layout Date @@ -264,12 +264,110 @@ If we take the log of we get \begin_inset Formula \begin{equation} -\log P(x|y,m)=\log P_{m}(x|y)=K_{gcm}-E_{gcm}(x,y).\label{eq:gm_log} +\log P(x|y,m)=\log P_{m}(x|y)=K_{gc}(m)-E_{gcm}(x,y).\label{eq:gm_log} \end{equation} \end_inset -Equating + +\end_layout + +\begin_layout Standard +\noindent +For conciseness, we will write +\begin_inset Formula $K_{gc}(m)$ +\end_inset + + as +\begin_inset Formula $K_{gcm}$ +\end_inset + +. +\end_layout + +\begin_layout Standard +\SpecialChar allowbreak + +\end_layout + +\begin_layout Standard +\noindent +The key point here is that +\family roman +\series medium +\shape up +\size normal +\emph off +\bar no +\strikeout off +\xout off +\uuline off +\uwave off +\noun off +\color none + +\begin_inset Formula $K_{gm}$ +\end_inset + + +\family default +\series default +\shape default +\size default +\emph default +\bar default +\strikeout default +\xout default +\uuline default +\uwave default +\noun default +\color inherit + is the log-normalization constant for the complete +\emph on +GaussianMixture +\emph default + across all values of +\begin_inset Formula $m$ +\end_inset + +, and cannot be dependent on the value of +\begin_inset Formula $m$ +\end_inset + +. + In contrast, +\begin_inset Formula $K_{gcm}$ +\end_inset + + is the log-normalization constant for a specific +\emph on +GaussianConditional +\emph default +mode (thus dependent on +\begin_inset Formula $m$ +\end_inset + +) and can have differing values based on the covariance matrices for each + mode. + Thus to obtain a constant +\begin_inset Formula $K_{gm}$ +\end_inset + + which satisfies the invariant, we need to specify +\begin_inset Formula $E_{gm}(x,y,m)$ +\end_inset + + accordingly. +\end_layout + +\begin_layout Standard +\SpecialChar allowbreak + +\end_layout + +\begin_layout Standard +\noindent +By equating \begin_inset CommandInset ref LatexCommand eqref reference "eq:gm_invariant" @@ -289,7 +387,7 @@ noprefix "false" \end_inset - we see that this can be achieved by defining the error +, we see that this can be achieved by defining the error \begin_inset Formula $E_{gm}(x,y,m)$ \end_inset @@ -541,9 +639,9 @@ noprefix "false" : \begin_inset Formula -\[ -E_{mf}(y,m)=C+E_{gcm}(\bar{x},y)-K_{gcm}. -\] +\begin{equation} +E_{mf}(y,m)=C+E_{gcm}(\bar{x},y)-K_{gcm}\label{eq:mixture_factor} +\end{equation} \end_inset @@ -555,8 +653,25 @@ Unfortunately, we can no longer choose \begin_inset Formula $m$ \end_inset - to make the constant disappear. - There are two possibilities: + to make the constant disappear, since +\begin_inset Formula $C$ +\end_inset + + has to be a constant applicable across all +\begin_inset Formula $m$ +\end_inset + +. +\end_layout + +\begin_layout Standard +\SpecialChar allowbreak + +\end_layout + +\begin_layout Standard +\noindent +There are two possibilities: \end_layout \begin_layout Enumerate diff --git a/doc/Hybrid.pdf b/doc/Hybrid.pdf index 558be2902..182573412 100644 Binary files a/doc/Hybrid.pdf and b/doc/Hybrid.pdf differ diff --git a/docker/ubuntu-gtsam-python/Dockerfile b/docker/ubuntu-gtsam-python/Dockerfile index 85eed4d4e..4a7c4b37f 100644 --- a/docker/ubuntu-gtsam-python/Dockerfile +++ b/docker/ubuntu-gtsam-python/Dockerfile @@ -6,9 +6,6 @@ FROM borglab/ubuntu-gtsam:bionic # Install pip RUN apt-get install -y python3-pip python3-dev -# Install python wrapper requirements -RUN python3 -m pip install -U -r /usr/src/gtsam/python/requirements.txt - # Run cmake again, now with python toolbox on WORKDIR /usr/src/gtsam/build RUN cmake \ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 52d90deb9..280b82265 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -20,4 +20,4 @@ if (NOT GTSAM_USE_BOOST_FEATURES) ) endif() -gtsamAddExamplesGlob("*.cpp" "${excluded_examples}" "gtsam;${Boost_PROGRAM_OPTIONS_LIBRARY}") +gtsamAddExamplesGlob("*.cpp" "${excluded_examples}" "gtsam;${Boost_PROGRAM_OPTIONS_LIBRARY}" ${GTSAM_BUILD_EXAMPLES_ALWAYS}) diff --git a/examples/GNCExample.cpp b/examples/GNCExample.cpp new file mode 100644 index 000000000..8a4f0706f --- /dev/null +++ b/examples/GNCExample.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 GNCExample.cpp + * @brief Simple example showcasing a Graduated Non-Convexity based solver + * @author Achintya Mohan + */ + +/** + * A simple 2D pose graph optimization example + * - The robot is initially at origin (0.0, 0.0, 0.0) + * - We have full odometry measurements for 2 motions + * - The robot first moves to (1.0, 0.0, 0.1) and then to (1.0, 1.0, 0.2) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace gtsam; + +int main() { + cout << "Graduated Non-Convexity Example\n"; + + NonlinearFactorGraph graph; + + // Add a prior to the first point, set to the origin + auto priorNoise = noiseModel::Isotropic::Sigma(3, 0.1); + graph.addPrior(1, Pose2(0.0, 0.0, 0.0), priorNoise); + + // Add additional factors, noise models must be Gaussian + Pose2 x1(1.0, 0.0, 0.1); + graph.emplace_shared>(1, 2, x1, noiseModel::Isotropic::Sigma(3, 0.2)); + Pose2 x2(0.0, 1.0, 0.1); + graph.emplace_shared>(2, 3, x2, noiseModel::Isotropic::Sigma(3, 0.4)); + + // Initial estimates + Values initial; + initial.insert(1, Pose2(0.2, 0.5, -0.1)); + initial.insert(2, Pose2(0.8, 0.3, 0.1)); + initial.insert(3, Pose2(0.8, 0.2, 0.3)); + + // Set options for the non-minimal solver + LevenbergMarquardtParams lmParams; + lmParams.setMaxIterations(1000); + lmParams.setRelativeErrorTol(1e-5); + + // Set GNC-specific options + GncParams gncParams(lmParams); + gncParams.setLossType(GncLossType::TLS); + + // Optimize the graph and print results + GncOptimizer> optimizer(graph, initial, gncParams); + Values result = optimizer.optimize(); + result.print("Final Result:"); + + return 0; +} + diff --git a/gtsam/CMakeLists.txt b/gtsam/CMakeLists.txt index 40028ad00..e35f5aada 100644 --- a/gtsam/CMakeLists.txt +++ b/gtsam/CMakeLists.txt @@ -111,7 +111,7 @@ if(GTSAM_SUPPORT_NESTED_DISSECTION) endif() # Versions -set(gtsam_version ${GTSAM_VERSION_MAJOR}.${GTSAM_VERSION_MINOR}.${GTSAM_VERSION_PATCH}) +set(gtsam_version ${GTSAM_VERSION_STRING}) set(gtsam_soversion ${GTSAM_VERSION_MAJOR}) message(STATUS "GTSAM Version: ${gtsam_version}") message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") @@ -144,10 +144,6 @@ if (GTSAM_USE_EIGEN_MKL) target_include_directories(gtsam PUBLIC ${MKL_INCLUDE_DIR}) endif() -if(GTSAM_USE_TBB) - target_include_directories(gtsam PUBLIC ${TBB_INCLUDE_DIRS}) -endif() - # Add includes for source directories 'BEFORE' boost and any system include # paths so that the compiler uses GTSAM headers in our source directory instead # of any previously installed GTSAM headers. diff --git a/gtsam/base/Group.h b/gtsam/base/Group.h index e76a273da..09534efc3 100644 --- a/gtsam/base/Group.h +++ b/gtsam/base/Group.h @@ -22,13 +22,6 @@ #include -#ifdef GTSAM_USE_BOOST_FEATURES -#include -#include -#include -#include -#endif - #include namespace gtsam { diff --git a/gtsam/base/serialization.h b/gtsam/base/serialization.h index e615afe83..18612bc22 100644 --- a/gtsam/base/serialization.h +++ b/gtsam/base/serialization.h @@ -17,6 +17,7 @@ * @date Feb 7, 2012 */ +#ifdef GTSAM_ENABLE_BOOST_SERIALIZATION #pragma once #include @@ -270,3 +271,4 @@ void deserializeBinary(const std::string& serialized, T& output, ///@} } // namespace gtsam +#endif diff --git a/gtsam/base/timing.cpp b/gtsam/base/timing.cpp index 154a564db..b43595066 100644 --- a/gtsam/base/timing.cpp +++ b/gtsam/base/timing.cpp @@ -19,9 +19,10 @@ #include #include +#include +#include #include #include -#include #include #include #include diff --git a/gtsam/base/types.h b/gtsam/base/types.h index 3f1c1cd3d..c734678d2 100644 --- a/gtsam/base/types.h +++ b/gtsam/base/types.h @@ -19,16 +19,11 @@ #pragma once +#include // for GTSAM_USE_TBB #include -#ifdef GTSAM_USE_BOOST_FEATURES -#include -#include -#endif -#include // for GTSAM_USE_TBB #include #include - #include #include diff --git a/gtsam/basis/Basis.h b/gtsam/basis/Basis.h index 41cdeeaaa..0b2b3606b 100644 --- a/gtsam/basis/Basis.h +++ b/gtsam/basis/Basis.h @@ -80,7 +80,7 @@ using Weights = Eigen::Matrix; /* 1xN vector */ * * @ingroup basis */ -Matrix kroneckerProductIdentity(size_t M, const Weights& w); +Matrix GTSAM_EXPORT kroneckerProductIdentity(size_t M, const Weights& w); /** * CRTP Base class for function bases diff --git a/gtsam/discrete/AlgebraicDecisionTree.h b/gtsam/discrete/AlgebraicDecisionTree.h index 425ad15f9..9f55f3b63 100644 --- a/gtsam/discrete/AlgebraicDecisionTree.h +++ b/gtsam/discrete/AlgebraicDecisionTree.h @@ -29,14 +29,14 @@ namespace gtsam { /** - * Algebraic Decision Trees fix the range to double - * Just has some nice constructors and some syntactic sugar - * TODO: consider eliminating this class altogether? + * An algebraic decision tree fixes the range of a DecisionTree to double. + * Just has some nice constructors and some syntactic sugar. + * TODO(dellaert): consider eliminating this class altogether? * * @ingroup discrete */ template - class GTSAM_EXPORT AlgebraicDecisionTree : public DecisionTree { + class AlgebraicDecisionTree : public DecisionTree { /** * @brief Default method used by `labelFormatter` or `valueFormatter` when * printing. @@ -81,20 +81,62 @@ namespace gtsam { AlgebraicDecisionTree(const L& label, double y1, double y2) : Base(label, y1, y2) {} - /** Create a new leaf function splitting on a variable */ + /** + * @brief Create a new leaf function splitting on a variable + * + * @param labelC: The label with cardinality 2 + * @param y1: The value for the first key + * @param y2: The value for the second key + * + * Example: + * @code{.cpp} + * std::pair A {"a", 2}; + * AlgebraicDecisionTree a(A, 0.6, 0.4); + * @endcode + */ AlgebraicDecisionTree(const typename Base::LabelC& labelC, double y1, double y2) : Base(labelC, y1, y2) {} - /** Create from keys and vector table */ + /** + * @brief Create from keys with cardinalities and a vector table + * + * @param labelCs: The keys, with cardinalities, given as pairs + * @param ys: The vector table + * + * Example with three keys, A, B, and C, with cardinalities 2, 3, and 2, + * respectively, and a vector table of size 12: + * @code{.cpp} + * DiscreteKey A(0, 2), B(1, 3), C(2, 2); + * const vector cpt{ + * 1.0 / 3, 2.0 / 3, 3.0 / 7, 4.0 / 7, 5.0 / 11, 6.0 / 11, // + * 1.0 / 9, 8.0 / 9, 3.0 / 6, 3.0 / 6, 5.0 / 10, 5.0 / 10}; + * AlgebraicDecisionTree expected(A & B & C, cpt); + * @endcode + * The table is given in the following order: + * A=0, B=0, C=0 + * A=0, B=0, C=1 + * ... + * A=1, B=1, C=1 + * Hence, the first line in the table is for A==0, and the second for A==1. + * In each line, the first two entries are for B==0, the next two for B==1, + * and the last two for B==2. Each pair is for a C value of 0 and 1. + */ AlgebraicDecisionTree // (const std::vector& labelCs, - const std::vector& ys) { + const std::vector& ys) { this->root_ = Base::create(labelCs.begin(), labelCs.end(), ys.begin(), ys.end()); } - /** Create from keys and string table */ + /** + * @brief Create from keys and string table + * + * @param labelCs: The keys, with cardinalities, given as pairs + * @param table: The string table, given as a string of doubles. + * + * @note Table needs to be in same order as the vector table in the other constructor. + */ AlgebraicDecisionTree // (const std::vector& labelCs, const std::string& table) { @@ -109,7 +151,13 @@ namespace gtsam { Base::create(labelCs.begin(), labelCs.end(), ys.begin(), ys.end()); } - /** Create a new function splitting on a variable */ + /** + * @brief Create a range of decision trees, splitting on a single variable. + * + * @param begin: Iterator to beginning of a range of decision trees + * @param end: Iterator to end of a range of decision trees + * @param label: The label to split on + */ template AlgebraicDecisionTree(Iterator begin, Iterator end, const L& label) : Base(nullptr) { diff --git a/gtsam/discrete/DecisionTree-inl.h b/gtsam/discrete/DecisionTree-inl.h index 9d618dea0..0c3229e7b 100644 --- a/gtsam/discrete/DecisionTree-inl.h +++ b/gtsam/discrete/DecisionTree-inl.h @@ -93,7 +93,8 @@ namespace gtsam { /// print void print(const std::string& s, const LabelFormatter& labelFormatter, const ValueFormatter& valueFormatter) const override { - std::cout << s << " Leaf " << valueFormatter(constant_) << std::endl; + std::cout << s << " Leaf [" << nrAssignments() << "] " + << valueFormatter(constant_) << std::endl; } /** Write graphviz format to stream `os`. */ @@ -626,7 +627,7 @@ namespace gtsam { // B=1 // A=0: 3 // A=1: 4 - // Note, through the magic of "compose", create([A B],[1 2 3 4]) will produce + // Note, through the magic of "compose", create([A B],[1 3 2 4]) will produce // exactly the same tree as above: the highest label is always the root. // However, it will be *way* faster if labels are given highest to lowest. template @@ -827,6 +828,16 @@ namespace gtsam { return total; } + /****************************************************************************/ + template + size_t DecisionTree::nrAssignments() const { + size_t n = 0; + this->visitLeaf([&n](const DecisionTree::Leaf& leaf) { + n += leaf.nrAssignments(); + }); + return n; + } + /****************************************************************************/ // fold is just done with a visit template diff --git a/gtsam/discrete/DecisionTree.h b/gtsam/discrete/DecisionTree.h index ed1908485..10daac1ed 100644 --- a/gtsam/discrete/DecisionTree.h +++ b/gtsam/discrete/DecisionTree.h @@ -39,9 +39,23 @@ namespace gtsam { /** - * Decision Tree - * L = label for variables - * Y = function range (any algebra), e.g., bool, int, double + * @brief a decision tree is a function from assignments to values. + * @tparam L label for variables + * @tparam Y function range (any algebra), e.g., bool, int, double + * + * After creating a decision tree on some variables, the tree can be evaluated + * on an assignment to those variables. Example: + * + * @code{.cpp} + * // Create a decision stump one one variable 'a' with values 10 and 20. + * DecisionTree tree('a', 10, 20); + * + * // Evaluate the tree on an assignment to the variable. + * int value0 = tree({{'a', 0}}); // value0 = 10 + * int value1 = tree({{'a', 1}}); // value1 = 20 + * @endcode + * + * More examples can be found in testDecisionTree.cpp * * @ingroup discrete */ @@ -136,7 +150,8 @@ namespace gtsam { NodePtr root_; protected: - /** Internal recursive function to create from keys, cardinalities, + /** + * Internal recursive function to create from keys, cardinalities, * and Y values */ template @@ -167,7 +182,13 @@ namespace gtsam { /** Create a constant */ explicit DecisionTree(const Y& y); - /// Create tree with 2 assignments `y1`, `y2`, splitting on variable `label` + /** + * @brief Create tree with 2 assignments `y1`, `y2`, splitting on variable `label` + * + * @param label The variable to split on. + * @param y1 The value for the first assignment. + * @param y2 The value for the second assignment. + */ DecisionTree(const L& label, const Y& y1, const Y& y2); /** Allow Label+Cardinality for convenience */ @@ -299,6 +320,42 @@ namespace gtsam { /// Return the number of leaves in the tree. size_t nrLeaves() const; + /** + * @brief This is a convenience function which returns the total number of + * leaf assignments in the decision tree. + * This function is not used for anymajor operations within the discrete + * factor graph framework. + * + * Leaf assignments represent the cardinality of each leaf node, e.g. in a + * binary tree each leaf has 2 assignments. This includes counts removed + * from implicit pruning hence, it will always be >= nrLeaves(). + * + * E.g. we have a decision tree as below, where each node has 2 branches: + * + * Choice(m1) + * 0 Choice(m0) + * 0 0 Leaf 0.0 + * 0 1 Leaf 0.0 + * 1 Choice(m0) + * 1 0 Leaf 1.0 + * 1 1 Leaf 2.0 + * + * In the unpruned form, the tree will have 4 assignments, 2 for each key, + * and 4 leaves. + * + * In the pruned form, the number of assignments is still 4 but the number + * of leaves is now 3, as below: + * + * Choice(m1) + * 0 Leaf 0.0 + * 1 Choice(m0) + * 1 0 Leaf 1.0 + * 1 1 Leaf 2.0 + * + * @return size_t + */ + size_t nrAssignments() const; + /** * @brief Fold a binary function over the tree, returning accumulator. * diff --git a/gtsam/discrete/DecisionTreeFactor.cpp b/gtsam/discrete/DecisionTreeFactor.cpp index 5fb5ae2e6..7202e6853 100644 --- a/gtsam/discrete/DecisionTreeFactor.cpp +++ b/gtsam/discrete/DecisionTreeFactor.cpp @@ -33,16 +33,13 @@ namespace gtsam { /* ************************************************************************ */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys& keys, - const ADT& potentials) - : DiscreteFactor(keys.indices()), - ADT(potentials), - cardinalities_(keys.cardinalities()) {} + const ADT& potentials) + : DiscreteFactor(keys.indices(), keys.cardinalities()), ADT(potentials) {} /* ************************************************************************ */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteConditional& c) - : DiscreteFactor(c.keys()), - AlgebraicDecisionTree(c), - cardinalities_(c.cardinalities_) {} + : DiscreteFactor(c.keys(), c.cardinalities()), + AlgebraicDecisionTree(c) {} /* ************************************************************************ */ bool DecisionTreeFactor::equals(const DiscreteFactor& other, @@ -85,6 +82,22 @@ namespace gtsam { ADT::print("", formatter); } + /* ************************************************************************ */ + DecisionTreeFactor DecisionTreeFactor::apply(ADT::Unary op) const { + // apply operand + ADT result = ADT::apply(op); + // Make a new factor + return DecisionTreeFactor(discreteKeys(), result); + } + + /* ************************************************************************ */ + DecisionTreeFactor DecisionTreeFactor::apply(ADT::UnaryAssignment op) const { + // apply operand + ADT result = ADT::apply(op); + // Make a new factor + return DecisionTreeFactor(discreteKeys(), result); + } + /* ************************************************************************ */ DecisionTreeFactor DecisionTreeFactor::apply(const DecisionTreeFactor& f, ADT::Binary op) const { @@ -151,15 +164,23 @@ namespace gtsam { } // create new factor, note we collect keys that are not in frontalKeys - // TODO(frank): why do we need this??? result should contain correct keys!!! + /* + Due to branch merging, the labels in `result` may be missing some keys + E.g. After branch merging, we may get a ADT like: + Leaf [2] 1.0204082 + + This is missing the key values used for branching. + */ + KeyVector difference, frontalKeys_(frontalKeys), keys_(keys()); + // Get the difference of the frontalKeys and the factor keys using set_difference + std::sort(keys_.begin(), keys_.end()); + std::sort(frontalKeys_.begin(), frontalKeys_.end()); + std::set_difference(keys_.begin(), keys_.end(), frontalKeys_.begin(), + frontalKeys_.end(), back_inserter(difference)); + DiscreteKeys dkeys; - for (i = 0; i < keys().size(); i++) { - Key j = keys()[i]; - // TODO(frank): inefficient! - if (std::find(frontalKeys.begin(), frontalKeys.end(), j) != - frontalKeys.end()) - continue; - dkeys.push_back(DiscreteKey(j, cardinality(j))); + for (Key key : difference) { + dkeys.push_back(DiscreteKey(key, cardinality(key))); } return std::make_shared(dkeys, result); } @@ -182,15 +203,47 @@ namespace gtsam { } /* ************************************************************************ */ - DiscreteKeys DecisionTreeFactor::discreteKeys() const { - DiscreteKeys result; - for (auto&& key : keys()) { - DiscreteKey dkey(key, cardinality(key)); - if (std::find(result.begin(), result.end(), dkey) == result.end()) { - result.push_back(dkey); + std::vector DecisionTreeFactor::probabilities() const { + // Set of all keys + std::set allKeys(keys().begin(), keys().end()); + + std::vector probs; + + /* An operation that takes each leaf probability, and computes the + * nrAssignments by checking the difference between the keys in the factor + * and the keys in the assignment. + * The nrAssignments is then used to append + * the correct number of leaf probability values to the `probs` vector + * defined above. + */ + auto op = [&](const Assignment& a, double p) { + // Get all the keys in the current assignment + std::set assignment_keys; + for (auto&& [k, _] : a) { + assignment_keys.insert(k); } - } - return result; + + // Find the keys missing in the assignment + std::vector diff; + std::set_difference(allKeys.begin(), allKeys.end(), + assignment_keys.begin(), assignment_keys.end(), + std::back_inserter(diff)); + + // Compute the total number of assignments in the (pruned) subtree + size_t nrAssignments = 1; + for (auto&& k : diff) { + nrAssignments *= cardinalities_.at(k); + } + // Add p `nrAssignments` times to the probs vector. + probs.insert(probs.end(), nrAssignments, p); + + return p; + }; + + // Go through the tree + this->apply(op); + + return probs; } /* ************************************************************************ */ @@ -288,29 +341,22 @@ namespace gtsam { /* ************************************************************************ */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys& keys, - const vector& table) - : DiscreteFactor(keys.indices()), - AlgebraicDecisionTree(keys, table), - cardinalities_(keys.cardinalities()) {} + const vector& table) + : DiscreteFactor(keys.indices(), keys.cardinalities()), + AlgebraicDecisionTree(keys, table) {} /* ************************************************************************ */ DecisionTreeFactor::DecisionTreeFactor(const DiscreteKeys& keys, - const string& table) - : DiscreteFactor(keys.indices()), - AlgebraicDecisionTree(keys, table), - cardinalities_(keys.cardinalities()) {} + const string& table) + : DiscreteFactor(keys.indices(), keys.cardinalities()), + AlgebraicDecisionTree(keys, table) {} /* ************************************************************************ */ DecisionTreeFactor DecisionTreeFactor::prune(size_t maxNrAssignments) const { const size_t N = maxNrAssignments; // Get the probabilities in the decision tree so we can threshold. - std::vector probabilities; - this->visitLeaf([&](const Leaf& leaf) { - size_t nrAssignments = leaf.nrAssignments(); - double prob = leaf.constant(); - probabilities.insert(probabilities.end(), nrAssignments, prob); - }); + std::vector probabilities = this->probabilities(); // The number of probabilities can be lower than max_leaves if (probabilities.size() <= N) { diff --git a/gtsam/discrete/DecisionTreeFactor.h b/gtsam/discrete/DecisionTreeFactor.h index 95054bcdb..a9a7e5ce0 100644 --- a/gtsam/discrete/DecisionTreeFactor.h +++ b/gtsam/discrete/DecisionTreeFactor.h @@ -50,10 +50,6 @@ namespace gtsam { typedef std::shared_ptr shared_ptr; typedef AlgebraicDecisionTree ADT; - protected: - std::map cardinalities_; - - public: /// @name Standard Constructors /// @{ @@ -63,11 +59,46 @@ namespace gtsam { /** Constructor from DiscreteKeys and AlgebraicDecisionTree */ DecisionTreeFactor(const DiscreteKeys& keys, const ADT& potentials); - /** Constructor from doubles */ + /** + * @brief Constructor from doubles + * + * @param keys The discrete keys. + * @param table The table of values. + * + * @throw std::invalid_argument if the size of `table` does not match the + * number of assignments. + * + * Example: + * @code{.cpp} + * DiscreteKey X(0,2), Y(1,3); + * const std::vector table {2, 5, 3, 6, 4, 7}; + * DecisionTreeFactor f1({X, Y}, table); + * @endcode + * + * The values in the table should be laid out so that the first key varies + * the slowest, and the last key the fastest. + */ DecisionTreeFactor(const DiscreteKeys& keys, - const std::vector& table); + const std::vector& table); - /** Constructor from string */ + /** + * @brief Constructor from string + * + * @param keys The discrete keys. + * @param table The table of values. + * + * @throw std::invalid_argument if the size of `table` does not match the + * number of assignments. + * + * Example: + * @code{.cpp} + * DiscreteKey X(0,2), Y(1,3); + * DecisionTreeFactor factor({X, Y}, "2 5 3 6 4 7"); + * @endcode + * + * The values in the table should be laid out so that the first key varies + * the slowest, and the last key the fastest. + */ DecisionTreeFactor(const DiscreteKeys& keys, const std::string& table); /// Single-key specialization @@ -119,8 +150,6 @@ namespace gtsam { static double safe_div(const double& a, const double& b); - size_t cardinality(Key j) const { return cardinalities_.at(j); } - /// divide by factor f (safely) DecisionTreeFactor operator/(const DecisionTreeFactor& f) const { return apply(f, safe_div); @@ -153,6 +182,19 @@ namespace gtsam { /// @name Advanced Interface /// @{ + /** + * Apply unary operator (*this) "op" f + * @param op a unary operator that operates on AlgebraicDecisionTree + */ + DecisionTreeFactor apply(ADT::Unary op) const; + + /** + * Apply unary operator (*this) "op" f + * @param op a unary operator that operates on AlgebraicDecisionTree. Takes + * both the assignment and the value. + */ + DecisionTreeFactor apply(ADT::UnaryAssignment op) const; + /** * Apply binary operator (*this) "op" f * @param f the second argument for op @@ -179,8 +221,8 @@ namespace gtsam { /// Enumerate all values into a map from values to double. std::vector> enumerate() const; - /// Return all the discrete keys associated with this factor. - DiscreteKeys discreteKeys() const; + /// Get all the probabilities in order of assignment values + std::vector probabilities() const; /** * @brief Prune the decision tree of discrete variables. @@ -260,7 +302,6 @@ namespace gtsam { void serialize(ARCHIVE& ar, const unsigned int /*version*/) { ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base); ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(ADT); - ar& BOOST_SERIALIZATION_NVP(cardinalities_); } #endif }; diff --git a/gtsam/discrete/DiscreteBayesTree.h b/gtsam/discrete/DiscreteBayesTree.h index e9b1325a0..89baf49a9 100644 --- a/gtsam/discrete/DiscreteBayesTree.h +++ b/gtsam/discrete/DiscreteBayesTree.h @@ -58,6 +58,11 @@ class GTSAM_EXPORT DiscreteBayesTreeClique //** evaluate conditional probability of subtree for given DiscreteValues */ double evaluate(const DiscreteValues& values) const; + + //** (Preferred) sugar for the above for given DiscreteValues */ + double operator()(const DiscreteValues& values) const { + return evaluate(values); + } }; /* ************************************************************************* */ @@ -105,4 +110,12 @@ class GTSAM_EXPORT DiscreteBayesTree /// @} }; +/// traits +template <> +struct traits + : public Testable {}; + +template <> +struct traits : public Testable {}; + } // namespace gtsam diff --git a/gtsam/discrete/DiscreteConditional.h b/gtsam/discrete/DiscreteConditional.h index 183cf8561..50fa6e161 100644 --- a/gtsam/discrete/DiscreteConditional.h +++ b/gtsam/discrete/DiscreteConditional.h @@ -77,6 +77,18 @@ class GTSAM_EXPORT DiscreteConditional const Signature::Table& table) : DiscreteConditional(Signature(key, parents, table)) {} + /** + * Construct from key, parents, and a vector specifying the + * conditional probability table (CPT) in 00 01 10 11 order. For + * three-valued, it would be 00 01 02 10 11 12 20 21 22, etc.... + * + * Example: DiscreteConditional P(D, {B,E}, table); + */ + DiscreteConditional(const DiscreteKey& key, const DiscreteKeys& parents, + const std::vector& table) + : DiscreteConditional(1, DiscreteKeys{key} & parents, + ADT(DiscreteKeys{key} & parents, table)) {} + /** * Construct from key, parents, and a string specifying the conditional * probability table (CPT) in 00 01 10 11 order. For three-valued, it would diff --git a/gtsam/discrete/DiscreteFactor.cpp b/gtsam/discrete/DiscreteFactor.cpp index 2b1bc36a3..b44d4fce2 100644 --- a/gtsam/discrete/DiscreteFactor.cpp +++ b/gtsam/discrete/DiscreteFactor.cpp @@ -28,6 +28,18 @@ using namespace std; namespace gtsam { +/* ************************************************************************ */ +DiscreteKeys DiscreteFactor::discreteKeys() const { + DiscreteKeys result; + for (auto&& key : keys()) { + DiscreteKey dkey(key, cardinality(key)); + if (std::find(result.begin(), result.end(), dkey) == result.end()) { + result.push_back(dkey); + } + } + return result; +} + /* ************************************************************************* */ double DiscreteFactor::error(const DiscreteValues& values) const { return -std::log((*this)(values)); diff --git a/gtsam/discrete/DiscreteFactor.h b/gtsam/discrete/DiscreteFactor.h index f00ebc499..24b2b55e4 100644 --- a/gtsam/discrete/DiscreteFactor.h +++ b/gtsam/discrete/DiscreteFactor.h @@ -36,28 +36,35 @@ class HybridValues; * @ingroup discrete */ class GTSAM_EXPORT DiscreteFactor: public Factor { - -public: - + public: // typedefs needed to play nice with gtsam - typedef DiscreteFactor This; ///< This class - typedef std::shared_ptr shared_ptr; ///< shared_ptr to this class - typedef Factor Base; ///< Our base class + typedef DiscreteFactor This; ///< This class + typedef std::shared_ptr + shared_ptr; ///< shared_ptr to this class + typedef Factor Base; ///< Our base class - using Values = DiscreteValues; ///< backwards compatibility + using Values = DiscreteValues; ///< backwards compatibility -public: + protected: + /// Map of Keys and their cardinalities. + std::map cardinalities_; + public: /// @name Standard Constructors /// @{ /** Default constructor creates empty factor */ DiscreteFactor() {} - /** Construct from container of keys. This constructor is used internally from derived factor - * constructors, either from a container of keys or from a boost::assign::list_of. */ - template - DiscreteFactor(const CONTAINER& keys) : Base(keys) {} + /** + * Construct from container of keys and map of cardinalities. + * This constructor is used internally from derived factor constructors, + * either from a container of keys or from a boost::assign::list_of. + */ + template + DiscreteFactor(const CONTAINER& keys, + const std::map cardinalities = {}) + : Base(keys), cardinalities_(cardinalities) {} /// @} /// @name Testable @@ -77,6 +84,13 @@ public: /// @name Standard Interface /// @{ + /// Return all the discrete keys associated with this factor. + DiscreteKeys discreteKeys() const; + + std::map cardinalities() const { return cardinalities_; } + + size_t cardinality(Key j) const { return cardinalities_.at(j); } + /// Find value for given assignment of values to variables virtual double operator()(const DiscreteValues&) const = 0; @@ -124,6 +138,17 @@ public: const Names& names = {}) const = 0; /// @} + + private: +#ifdef GTSAM_ENABLE_BOOST_SERIALIZATION + /** Serialization function */ + friend class boost::serialization::access; + template + void serialize(ARCHIVE& ar, const unsigned int /*version*/) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base); + ar& BOOST_SERIALIZATION_NVP(cardinalities_); + } +#endif }; // DiscreteFactor diff --git a/gtsam/discrete/DiscreteFactorGraph.h b/gtsam/discrete/DiscreteFactorGraph.h index 650fe7636..d0dc282b4 100644 --- a/gtsam/discrete/DiscreteFactorGraph.h +++ b/gtsam/discrete/DiscreteFactorGraph.h @@ -41,16 +41,30 @@ class DiscreteJunctionTree; /** * @brief Main elimination function for DiscreteFactorGraph. - * - * @param factors - * @param keys - * @return GTSAM_EXPORT + * + * @param factors The factor graph to eliminate. + * @param frontalKeys An ordering for which variables to eliminate. + * @return A pair of the resulting conditional and the separator factor. * @ingroup discrete */ -GTSAM_EXPORT std::pair, DecisionTreeFactor::shared_ptr> -EliminateDiscrete(const DiscreteFactorGraph& factors, const Ordering& keys); +GTSAM_EXPORT +std::pair +EliminateDiscrete(const DiscreteFactorGraph& factors, + const Ordering& frontalKeys); + +/** + * @brief Alternate elimination function for that creates non-normalized lookup tables. + * + * @param factors The factor graph to eliminate. + * @param frontalKeys An ordering for which variables to eliminate. + * @return A pair of the resulting lookup table and the separator factor. + * @ingroup discrete + */ +GTSAM_EXPORT +std::pair +EliminateForMPE(const DiscreteFactorGraph& factors, + const Ordering& frontalKeys); -/* ************************************************************************* */ template<> struct EliminationTraits { typedef DiscreteFactor FactorType; ///< Type of factors in factor graph @@ -60,12 +74,14 @@ template<> struct EliminationTraits typedef DiscreteEliminationTree EliminationTreeType; ///< Type of elimination tree typedef DiscreteBayesTree BayesTreeType; ///< Type of Bayes tree typedef DiscreteJunctionTree JunctionTreeType; ///< Type of Junction tree + /// The default dense elimination function static std::pair, std::shared_ptr > DefaultEliminate(const FactorGraphType& factors, const Ordering& keys) { return EliminateDiscrete(factors, keys); } + /// The default ordering generation function static Ordering DefaultOrderingFunc( const FactorGraphType& graph, @@ -74,7 +90,6 @@ template<> struct EliminationTraits } }; -/* ************************************************************************* */ /** * A Discrete Factor Graph is a factor graph where all factors are Discrete, i.e. * Factor == DiscreteFactor @@ -108,8 +123,8 @@ class GTSAM_EXPORT DiscreteFactorGraph /** Implicit copy/downcast constructor to override explicit template container * constructor */ - template - DiscreteFactorGraph(const FactorGraph& graph) : Base(graph) {} + template + DiscreteFactorGraph(const FactorGraph& graph) : Base(graph) {} /// @name Testable /// @{ @@ -227,10 +242,6 @@ class GTSAM_EXPORT DiscreteFactorGraph /// @} }; // \ DiscreteFactorGraph -std::pair // -EliminateForMPE(const DiscreteFactorGraph& factors, - const Ordering& frontalKeys); - /// traits template <> struct traits : public Testable {}; diff --git a/gtsam/discrete/DiscreteJunctionTree.h b/gtsam/discrete/DiscreteJunctionTree.h index 385c659e4..f6171c672 100644 --- a/gtsam/discrete/DiscreteJunctionTree.h +++ b/gtsam/discrete/DiscreteJunctionTree.h @@ -66,4 +66,6 @@ namespace gtsam { DiscreteJunctionTree(const DiscreteEliminationTree& eliminationTree); }; + /// typedef for wrapper: + using DiscreteCluster = DiscreteJunctionTree::Cluster; } diff --git a/gtsam/discrete/DiscreteKey.cpp b/gtsam/discrete/DiscreteKey.cpp index 79d11d8a7..17f233695 100644 --- a/gtsam/discrete/DiscreteKey.cpp +++ b/gtsam/discrete/DiscreteKey.cpp @@ -49,6 +49,7 @@ namespace gtsam { void DiscreteKeys::print(const std::string& s, const KeyFormatter& keyFormatter) const { + std::cout << (s.empty() ? "" : s + " ") << std::endl; for (auto&& dkey : *this) { std::cout << DefaultKeyFormatter(dkey.first) << " " << dkey.second << std::endl; diff --git a/gtsam/discrete/DiscreteKey.h b/gtsam/discrete/DiscreteKey.h index 3a626c6b3..44cc192ef 100644 --- a/gtsam/discrete/DiscreteKey.h +++ b/gtsam/discrete/DiscreteKey.h @@ -74,6 +74,12 @@ namespace gtsam { return *this; } + /// Add multiple keys (non-const!) + DiscreteKeys& operator&(const DiscreteKeys& keys) { + this->insert(this->end(), keys.begin(), keys.end()); + return *this; + } + /// Print the keys and cardinalities. void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const; diff --git a/gtsam/discrete/DiscreteValues.h b/gtsam/discrete/DiscreteValues.h index 8a6d6f930..9ec08302b 100644 --- a/gtsam/discrete/DiscreteValues.h +++ b/gtsam/discrete/DiscreteValues.h @@ -120,6 +120,11 @@ class GTSAM_EXPORT DiscreteValues : public Assignment { /// @} }; +/// Free version of CartesianProduct. +inline std::vector cartesianProduct(const DiscreteKeys& keys) { + return DiscreteValues::CartesianProduct(keys); +} + /// Free version of markdown. std::string markdown(const DiscreteValues& values, const KeyFormatter& keyFormatter = DefaultKeyFormatter, diff --git a/gtsam/discrete/SignatureParser.h b/gtsam/discrete/SignatureParser.h index e6b402e44..ddc548fc6 100644 --- a/gtsam/discrete/SignatureParser.h +++ b/gtsam/discrete/SignatureParser.h @@ -22,6 +22,8 @@ #include #include +#include + namespace gtsam { /** * @brief A simple parser that replaces the boost spirit parser. @@ -47,7 +49,7 @@ namespace gtsam { * * Also fails if the rows are not of the same size. */ -struct SignatureParser { +struct GTSAM_EXPORT SignatureParser { using Row = std::vector; using Table = std::vector; diff --git a/gtsam/discrete/TableFactor.cpp b/gtsam/discrete/TableFactor.cpp index 5fe3cd9d1..f4e023a4d 100644 --- a/gtsam/discrete/TableFactor.cpp +++ b/gtsam/discrete/TableFactor.cpp @@ -13,11 +13,12 @@ * @file TableFactor.cpp * @brief discrete factor * @date May 4, 2023 - * @author Yoonwoo Kim + * @author Yoonwoo Kim, Varun Agrawal */ #include #include +#include #include #include @@ -33,8 +34,7 @@ TableFactor::TableFactor() {} /* ************************************************************************ */ TableFactor::TableFactor(const DiscreteKeys& dkeys, const TableFactor& potentials) - : DiscreteFactor(dkeys.indices()), - cardinalities_(potentials.cardinalities_) { + : DiscreteFactor(dkeys.indices(), dkeys.cardinalities()) { sparse_table_ = potentials.sparse_table_; denominators_ = potentials.denominators_; sorted_dkeys_ = discreteKeys(); @@ -44,11 +44,11 @@ TableFactor::TableFactor(const DiscreteKeys& dkeys, /* ************************************************************************ */ TableFactor::TableFactor(const DiscreteKeys& dkeys, const Eigen::SparseVector& table) - : DiscreteFactor(dkeys.indices()), sparse_table_(table.size()) { + : DiscreteFactor(dkeys.indices(), dkeys.cardinalities()), + sparse_table_(table.size()) { sparse_table_ = table; double denom = table.size(); for (const DiscreteKey& dkey : dkeys) { - cardinalities_.insert(dkey); denom /= dkey.second; denominators_.insert(std::pair(dkey.first, denom)); } @@ -56,11 +56,51 @@ TableFactor::TableFactor(const DiscreteKeys& dkeys, sort(sorted_dkeys_.begin(), sorted_dkeys_.end()); } +/* ************************************************************************ */ +TableFactor::TableFactor(const DiscreteKeys& dkeys, + const DecisionTree& dtree) + : TableFactor(dkeys, DecisionTreeFactor(dkeys, dtree)) {} + +/** + * @brief Compute the correct ordering of the leaves in the decision tree. + * + * This is done by first taking all the values which have modulo 0 value with + * the cardinality of the innermost key `n`, and we go up to modulo n. + * + * @param dt The DecisionTree + * @return std::vector + */ +std::vector ComputeLeafOrdering(const DiscreteKeys& dkeys, + const DecisionTreeFactor& dt) { + std::vector probs = dt.probabilities(); + std::vector ordered; + + size_t n = dkeys[0].second; + + for (size_t k = 0; k < n; ++k) { + for (size_t idx = 0; idx < probs.size(); ++idx) { + if (idx % n == k) { + ordered.push_back(probs[idx]); + } + } + } + return ordered; +} + +/* ************************************************************************ */ +TableFactor::TableFactor(const DiscreteKeys& dkeys, + const DecisionTreeFactor& dtf) + : TableFactor(dkeys, ComputeLeafOrdering(dkeys, dtf)) {} + +/* ************************************************************************ */ +TableFactor::TableFactor(const DiscreteConditional& c) + : TableFactor(c.discreteKeys(), c) {} + /* ************************************************************************ */ Eigen::SparseVector TableFactor::Convert( const std::vector& table) { Eigen::SparseVector sparse_table(table.size()); - // Count number of nonzero elements in table and reserving the space. + // Count number of nonzero elements in table and reserve the space. const uint64_t nnz = std::count_if(table.begin(), table.end(), [](uint64_t i) { return i != 0; }); sparse_table.reserve(nnz); @@ -214,6 +254,45 @@ void TableFactor::print(const string& s, const KeyFormatter& formatter) const { cout << "number of nnzs: " << sparse_table_.nonZeros() << endl; } +/* ************************************************************************ */ +TableFactor TableFactor::apply(Unary op) const { + // Initialize new factor. + uint64_t cardi = 1; + for (auto [key, c] : cardinalities_) cardi *= c; + Eigen::SparseVector sparse_table(cardi); + sparse_table.reserve(sparse_table_.nonZeros()); + + // Populate + for (SparseIt it(sparse_table_); it; ++it) { + sparse_table.coeffRef(it.index()) = op(it.value()); + } + + // Free unused memory and return. + sparse_table.pruned(); + sparse_table.data().squeeze(); + return TableFactor(discreteKeys(), sparse_table); +} + +/* ************************************************************************ */ +TableFactor TableFactor::apply(UnaryAssignment op) const { + // Initialize new factor. + uint64_t cardi = 1; + for (auto [key, c] : cardinalities_) cardi *= c; + Eigen::SparseVector sparse_table(cardi); + sparse_table.reserve(sparse_table_.nonZeros()); + + // Populate + for (SparseIt it(sparse_table_); it; ++it) { + DiscreteValues assignment = findAssignments(it.index()); + sparse_table.coeffRef(it.index()) = op(assignment, it.value()); + } + + // Free unused memory and return. + sparse_table.pruned(); + sparse_table.data().squeeze(); + return TableFactor(discreteKeys(), sparse_table); +} + /* ************************************************************************ */ TableFactor TableFactor::apply(const TableFactor& f, Binary op) const { if (keys_.empty() && sparse_table_.nonZeros() == 0) @@ -435,18 +514,6 @@ std::vector> TableFactor::enumerate() const { return result; } -/* ************************************************************************ */ -DiscreteKeys TableFactor::discreteKeys() const { - DiscreteKeys result; - for (auto&& key : keys()) { - DiscreteKey dkey(key, cardinality(key)); - if (std::find(result.begin(), result.end(), dkey) == result.end()) { - result.push_back(dkey); - } - } - return result; -} - // Print out header. /* ************************************************************************ */ string TableFactor::markdown(const KeyFormatter& keyFormatter, diff --git a/gtsam/discrete/TableFactor.h b/gtsam/discrete/TableFactor.h index 1462180e0..828e794e6 100644 --- a/gtsam/discrete/TableFactor.h +++ b/gtsam/discrete/TableFactor.h @@ -12,7 +12,7 @@ /** * @file TableFactor.h * @date May 4, 2023 - * @author Yoonwoo Kim + * @author Yoonwoo Kim, Varun Agrawal */ #pragma once @@ -32,6 +32,7 @@ namespace gtsam { +class DiscreteConditional; class HybridValues; /** @@ -44,8 +45,6 @@ class HybridValues; */ class GTSAM_EXPORT TableFactor : public DiscreteFactor { protected: - /// Map of Keys and their cardinalities. - std::map cardinalities_; /// SparseVector of nonzero probabilities. Eigen::SparseVector sparse_table_; @@ -57,10 +56,10 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { /** * @brief Uses lazy cartesian product to find nth entry in the cartesian - * product of arrays in O(1) - * Example) - * v0 | v1 | val - * 0 | 0 | 10 + * product of arrays in O(1) + * Example) + * v0 | v1 | val + * 0 | 0 | 10 * 0 | 1 | 21 * 1 | 0 | 32 * 1 | 1 | 43 @@ -75,13 +74,13 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { * @brief Return ith key in keys_ as a DiscreteKey * @param i ith key in keys_ * @return DiscreteKey - * */ + */ DiscreteKey discreteKey(size_t i) const { return DiscreteKey(keys_[i], cardinalities_.at(keys_[i])); } /// Convert probability table given as doubles to SparseVector. - /// Example) {0, 1, 1, 0, 0, 1, 0} -> values: {1, 1, 1}, indices: {1, 2, 5} + /// Example) {0, 1, 1, 0, 0, 1, 0} -> values: {1, 1, 1}, indices: {1, 2, 5} static Eigen::SparseVector Convert(const std::vector& table); /// Convert probability table given as string to SparseVector. @@ -94,6 +93,9 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { typedef std::shared_ptr shared_ptr; typedef Eigen::SparseVector::InnerIterator SparseIt; typedef std::vector> AssignValList; + using Unary = std::function; + using UnaryAssignment = + std::function&, const double&)>; using Binary = std::function; public: @@ -142,6 +144,15 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { TableFactor(const DiscreteKey& key, const std::vector& row) : TableFactor(DiscreteKeys{key}, row) {} + /// Constructor from DecisionTreeFactor + TableFactor(const DiscreteKeys& keys, const DecisionTreeFactor& dtf); + + /// Constructor from DecisionTree/AlgebraicDecisionTree + TableFactor(const DiscreteKeys& keys, const DecisionTree& dtree); + + /** Construct from a DiscreteConditional type */ + explicit TableFactor(const DiscreteConditional& c); + /// @} /// @name Testable /// @{ @@ -175,13 +186,11 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { return apply(f, Ring::mul); }; - /// multiple with DecisionTreeFactor + /// multiply with DecisionTreeFactor DecisionTreeFactor operator*(const DecisionTreeFactor& f) const override; static double safe_div(const double& a, const double& b); - size_t cardinality(Key j) const { return cardinalities_.at(j); } - /// divide by factor f (safely) TableFactor operator/(const TableFactor& f) const { return apply(f, safe_div); @@ -218,6 +227,18 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { /// @name Advanced Interface /// @{ + /** + * Apply unary operator `op(*this)` where `op` accepts the discrete value. + * @param op a unary operator that operates on TableFactor + */ + TableFactor apply(Unary op) const; + /** + * Apply unary operator `op(*this)` where `op` accepts the discrete assignment + * and the value at that assignment. + * @param op a unary operator that operates on TableFactor + */ + TableFactor apply(UnaryAssignment op) const; + /** * Apply binary operator (*this) "op" f * @param f the second argument for op @@ -225,10 +246,19 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { */ TableFactor apply(const TableFactor& f, Binary op) const; - /// Return keys in contract mode. + /** + * Return keys in contract mode. + * + * Modes are each of the dimensions of a sparse tensor, + * and the contract modes represent which dimensions will + * be involved in contraction (aka tensor multiplication). + */ DiscreteKeys contractDkeys(const TableFactor& f) const; - /// Return keys in free mode. + /** + * @brief Return keys in free mode which are the dimensions + * not involved in the contraction operation. + */ DiscreteKeys freeDkeys(const TableFactor& f) const; /// Return union of DiscreteKeys in two factors. @@ -274,9 +304,6 @@ class GTSAM_EXPORT TableFactor : public DiscreteFactor { /// Enumerate all values into a map from values to double. std::vector> enumerate() const; - /// Return all the discrete keys associated with this factor. - DiscreteKeys discreteKeys() const; - /** * @brief Prune the decision tree of discrete variables. * diff --git a/gtsam/discrete/discrete.i b/gtsam/discrete/discrete.i index 78efd57e2..a1731f8e5 100644 --- a/gtsam/discrete/discrete.i +++ b/gtsam/discrete/discrete.i @@ -17,6 +17,8 @@ class DiscreteKeys { }; // DiscreteValues is added in specializations/discrete.h as a std::map +std::vector cartesianProduct( + const gtsam::DiscreteKeys& keys); string markdown( const gtsam::DiscreteValues& values, const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter); @@ -31,27 +33,30 @@ string html(const gtsam::DiscreteValues& values, std::map> names); #include -class DiscreteFactor { +virtual class DiscreteFactor : gtsam::Factor { void print(string s = "DiscreteFactor\n", const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter) const; bool equals(const gtsam::DiscreteFactor& other, double tol = 1e-9) const; - bool empty() const; - size_t size() const; double operator()(const gtsam::DiscreteValues& values) const; }; #include virtual class DecisionTreeFactor : gtsam::DiscreteFactor { DecisionTreeFactor(); - + DecisionTreeFactor(const gtsam::DiscreteKey& key, const std::vector& spec); DecisionTreeFactor(const gtsam::DiscreteKey& key, string table); - + + DecisionTreeFactor(const gtsam::DiscreteKeys& keys, + const std::vector& table); DecisionTreeFactor(const gtsam::DiscreteKeys& keys, string table); + + DecisionTreeFactor(const std::vector& keys, + const std::vector& table); DecisionTreeFactor(const std::vector& keys, string table); - + DecisionTreeFactor(const gtsam::DiscreteConditional& c); void print(string s = "DecisionTreeFactor\n", @@ -59,6 +64,8 @@ virtual class DecisionTreeFactor : gtsam::DiscreteFactor { gtsam::DefaultKeyFormatter) const; bool equals(const gtsam::DecisionTreeFactor& other, double tol = 1e-9) const; + size_t cardinality(gtsam::Key j) const; + double operator()(const gtsam::DiscreteValues& values) const; gtsam::DecisionTreeFactor operator*(const gtsam::DecisionTreeFactor& f) const; size_t cardinality(gtsam::Key j) const; @@ -66,6 +73,7 @@ virtual class DecisionTreeFactor : gtsam::DiscreteFactor { gtsam::DecisionTreeFactor* sum(size_t nrFrontals) const; gtsam::DecisionTreeFactor* sum(const gtsam::Ordering& keys) const; gtsam::DecisionTreeFactor* max(size_t nrFrontals) const; + gtsam::DecisionTreeFactor* max(const gtsam::Ordering& keys) const; string dot( const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter, @@ -203,10 +211,16 @@ class DiscreteBayesTreeClique { DiscreteBayesTreeClique(const gtsam::DiscreteConditional* conditional); const gtsam::DiscreteConditional* conditional() const; bool isRoot() const; + size_t nrChildren() const; + const gtsam::DiscreteBayesTreeClique* operator[](size_t i) const; + void print(string s = "DiscreteBayesTreeClique", + const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; void printSignature( const string& s = "Clique: ", const gtsam::KeyFormatter& formatter = gtsam::DefaultKeyFormatter) const; double evaluate(const gtsam::DiscreteValues& values) const; + double operator()(const gtsam::DiscreteValues& values) const; }; class DiscreteBayesTree { @@ -220,6 +234,9 @@ class DiscreteBayesTree { bool empty() const; const DiscreteBayesTreeClique* operator[](size_t j) const; + double evaluate(const gtsam::DiscreteValues& values) const; + double operator()(const gtsam::DiscreteValues& values) const; + string dot(const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter) const; void saveGraph(string s, @@ -242,9 +259,9 @@ class DiscreteBayesTree { class DiscreteLookupTable : gtsam::DiscreteConditional{ DiscreteLookupTable(size_t nFrontals, const gtsam::DiscreteKeys& keys, const gtsam::DecisionTreeFactor::ADT& potentials); - void print( - const std::string& s = "Discrete Lookup Table: ", - const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter) const; + void print(string s = "Discrete Lookup Table: ", + const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; size_t argmax(const gtsam::DiscreteValues& parentsValues) const; }; @@ -263,6 +280,14 @@ class DiscreteLookupDAG { }; #include +pair +EliminateDiscrete(const gtsam::DiscreteFactorGraph& factors, + const gtsam::Ordering& frontalKeys); + +pair +EliminateForMPE(const gtsam::DiscreteFactorGraph& factors, + const gtsam::Ordering& frontalKeys); + class DiscreteFactorGraph { DiscreteFactorGraph(); DiscreteFactorGraph(const gtsam::DiscreteBayesNet& bayesNet); @@ -277,6 +302,7 @@ class DiscreteFactorGraph { void add(const gtsam::DiscreteKey& j, const std::vector& spec); void add(const gtsam::DiscreteKeys& keys, string spec); void add(const std::vector& keys, string spec); + void add(const std::vector& keys, const std::vector& spec); bool empty() const; size_t size() const; @@ -290,25 +316,46 @@ class DiscreteFactorGraph { double operator()(const gtsam::DiscreteValues& values) const; gtsam::DiscreteValues optimize() const; - gtsam::DiscreteBayesNet sumProduct(); - gtsam::DiscreteBayesNet sumProduct(gtsam::Ordering::OrderingType type); + gtsam::DiscreteBayesNet sumProduct( + gtsam::Ordering::OrderingType type = gtsam::Ordering::COLAMD); gtsam::DiscreteBayesNet sumProduct(const gtsam::Ordering& ordering); - gtsam::DiscreteLookupDAG maxProduct(); - gtsam::DiscreteLookupDAG maxProduct(gtsam::Ordering::OrderingType type); + gtsam::DiscreteLookupDAG maxProduct( + gtsam::Ordering::OrderingType type = gtsam::Ordering::COLAMD); gtsam::DiscreteLookupDAG maxProduct(const gtsam::Ordering& ordering); - gtsam::DiscreteBayesNet* eliminateSequential(); - gtsam::DiscreteBayesNet* eliminateSequential(gtsam::Ordering::OrderingType type); + gtsam::DiscreteBayesNet* eliminateSequential( + gtsam::Ordering::OrderingType type = gtsam::Ordering::COLAMD); + gtsam::DiscreteBayesNet* eliminateSequential( + gtsam::Ordering::OrderingType type, + const gtsam::DiscreteFactorGraph::Eliminate& function); gtsam::DiscreteBayesNet* eliminateSequential(const gtsam::Ordering& ordering); + gtsam::DiscreteBayesNet* eliminateSequential( + const gtsam::Ordering& ordering, + const gtsam::DiscreteFactorGraph::Eliminate& function); pair - eliminatePartialSequential(const gtsam::Ordering& ordering); + eliminatePartialSequential(const gtsam::Ordering& ordering); + pair + eliminatePartialSequential( + const gtsam::Ordering& ordering, + const gtsam::DiscreteFactorGraph::Eliminate& function); - gtsam::DiscreteBayesTree* eliminateMultifrontal(); - gtsam::DiscreteBayesTree* eliminateMultifrontal(gtsam::Ordering::OrderingType type); - gtsam::DiscreteBayesTree* eliminateMultifrontal(const gtsam::Ordering& ordering); + gtsam::DiscreteBayesTree* eliminateMultifrontal( + gtsam::Ordering::OrderingType type = gtsam::Ordering::COLAMD); + gtsam::DiscreteBayesTree* eliminateMultifrontal( + gtsam::Ordering::OrderingType type, + const gtsam::DiscreteFactorGraph::Eliminate& function); + gtsam::DiscreteBayesTree* eliminateMultifrontal( + const gtsam::Ordering& ordering); + gtsam::DiscreteBayesTree* eliminateMultifrontal( + const gtsam::Ordering& ordering, + const gtsam::DiscreteFactorGraph::Eliminate& function); pair - eliminatePartialMultifrontal(const gtsam::Ordering& ordering); + eliminatePartialMultifrontal(const gtsam::Ordering& ordering); + pair + eliminatePartialMultifrontal( + const gtsam::Ordering& ordering, + const gtsam::DiscreteFactorGraph::Eliminate& function); string dot( const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter, @@ -328,4 +375,41 @@ class DiscreteFactorGraph { std::map> names) const; }; +#include + +class DiscreteEliminationTree { + DiscreteEliminationTree(const gtsam::DiscreteFactorGraph& factorGraph, + const gtsam::VariableIndex& structure, + const gtsam::Ordering& order); + + DiscreteEliminationTree(const gtsam::DiscreteFactorGraph& factorGraph, + const gtsam::Ordering& order); + + void print( + string name = "EliminationTree: ", + const gtsam::KeyFormatter& formatter = gtsam::DefaultKeyFormatter) const; + bool equals(const gtsam::DiscreteEliminationTree& other, + double tol = 1e-9) const; +}; + +#include + +class DiscreteCluster { + gtsam::Ordering orderedFrontalKeys; + gtsam::DiscreteFactorGraph factors; + const gtsam::DiscreteCluster& operator[](size_t i) const; + size_t nrChildren() const; + void print(string s = "", const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; +}; + +class DiscreteJunctionTree { + DiscreteJunctionTree(const gtsam::DiscreteEliminationTree& eliminationTree); + void print( + string name = "JunctionTree: ", + const gtsam::KeyFormatter& formatter = gtsam::DefaultKeyFormatter) const; + size_t nrRoots() const; + const gtsam::DiscreteCluster& operator[](size_t i) const; +}; + } // namespace gtsam diff --git a/gtsam/discrete/tests/testDecisionTree.cpp b/gtsam/discrete/tests/testDecisionTree.cpp index d2a94ddc3..af1b6dd8a 100644 --- a/gtsam/discrete/tests/testDecisionTree.cpp +++ b/gtsam/discrete/tests/testDecisionTree.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -75,6 +76,19 @@ struct traits : public Testable {}; GTSAM_CONCEPT_TESTABLE_INST(CrazyDecisionTree) +/* ************************************************************************** */ +// Test char labels and int range +/* ************************************************************************** */ + +// Create a decision stump one one variable 'a' with values 10 and 20. +TEST(DecisionTree, Constructor) { + DecisionTree tree('a', 10, 20); + + // Evaluate the tree on an assignment to the variable. + EXPECT_LONGS_EQUAL(10, tree({{'a', 0}})); + EXPECT_LONGS_EQUAL(20, tree({{'a', 1}})); +} + /* ************************************************************************** */ // Test string labels and int range /* ************************************************************************** */ @@ -118,18 +132,47 @@ struct Ring { static inline int mul(const int& a, const int& b) { return a * b; } }; +/* ************************************************************************** */ +// Check that creating decision trees respects key order. +TEST(DecisionTree, ConstructorOrder) { + // Create labels + string A("A"), B("B"); + + const std::vector ys1 = {1, 2, 3, 4}; + DT tree1({{B, 2}, {A, 2}}, ys1); // faster version, as B is "higher" than A! + + const std::vector ys2 = {1, 3, 2, 4}; + DT tree2({{A, 2}, {B, 2}}, ys2); // slower version ! + + // Both trees will be the same, tree is order from high to low labels. + // Choice(B) + // 0 Choice(A) + // 0 0 Leaf 1 + // 0 1 Leaf 2 + // 1 Choice(A) + // 1 0 Leaf 3 + // 1 1 Leaf 4 + + EXPECT(tree2.equals(tree1)); + + // Check the values are as expected by calling the () operator: + EXPECT_LONGS_EQUAL(1, tree1({{A, 0}, {B, 0}})); + EXPECT_LONGS_EQUAL(3, tree1({{A, 0}, {B, 1}})); + EXPECT_LONGS_EQUAL(2, tree1({{A, 1}, {B, 0}})); + EXPECT_LONGS_EQUAL(4, tree1({{A, 1}, {B, 1}})); +} + /* ************************************************************************** */ // test DT -TEST(DecisionTree, example) { +TEST(DecisionTree, Example) { // Create labels string A("A"), B("B"), C("C"); - // create a value - Assignment x00, x01, x10, x11; - x00[A] = 0, x00[B] = 0; - x01[A] = 0, x01[B] = 1; - x10[A] = 1, x10[B] = 0; - x11[A] = 1, x11[B] = 1; + // Create assignments using brace initialization: + Assignment x00{{A, 0}, {B, 0}}; + Assignment x01{{A, 0}, {B, 1}}; + Assignment x10{{A, 1}, {B, 0}}; + Assignment x11{{A, 1}, {B, 1}}; // empty DT empty; @@ -241,8 +284,7 @@ TEST(DecisionTree, ConvertValuesOnly) { StringBoolTree f2(f1, bool_of_int); // Check a value - Assignment x00; - x00["A"] = 0, x00["B"] = 0; + Assignment x00 {{A, 0}, {B, 0}}; EXPECT(!f2(x00)); } @@ -266,10 +308,11 @@ TEST(DecisionTree, ConvertBoth) { // Check some values Assignment