diff --git a/.github/scripts/python.sh b/.github/scripts/python.sh index 08b8084a05..0c04eeec3d 100644 --- a/.github/scripts/python.sh +++ b/.github/scripts/python.sh @@ -65,7 +65,7 @@ function build() # Set to 2 cores so that Actions does not error out during resource provisioning. cmake --build build -j2 - $PYTHON -m pip install --user build/python + cmake --build build --target python-install } function test() @@ -73,6 +73,13 @@ function test() cd $GITHUB_WORKSPACE/python/gtsam/tests $PYTHON -m unittest discover -v cd $GITHUB_WORKSPACE + + cd $GITHUB_WORKSPACE/python/gtsam_unstable/tests + $PYTHON -m unittest discover -v + cd $GITHUB_WORKSPACE + + # cmake --build build --target python-test + # cmake --build build --target python-test-unstable } # select between build or test diff --git a/.github/workflows/build-python.yml b/.github/workflows/build-python.yml index 91bc4e80ac..ce3685f878 100644 --- a/.github/workflows/build-python.yml +++ b/.github/workflows/build-python.yml @@ -18,6 +18,8 @@ jobs: CTEST_PARALLEL_LEVEL: 2 CMAKE_BUILD_TYPE: ${{ matrix.build_type }} PYTHON_VERSION: ${{ matrix.python_version }} + BOOST_VERSION: 1.72.0 + BOOST_EXE: boost_1_72_0-msvc-14.2 strategy: fail-fast: true @@ -30,6 +32,7 @@ jobs: ubuntu-20.04-gcc-9-tbb, ubuntu-20.04-clang-9, macOS-11-xcode-13.4.1, + windows-2022-msbuild, ] build_type: [Release] @@ -56,6 +59,10 @@ jobs: compiler: xcode version: "13.4.1" + - name: windows-2022-msbuild + os: windows-2022 + platform: 64 + steps: - name: Checkout uses: actions/checkout@v3 @@ -97,29 +104,74 @@ jobs: echo "CC=clang" >> $GITHUB_ENV echo "CXX=clang++" >> $GITHUB_ENV + - name: Setup msbuild (Windows) + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x${{matrix.platform}} + toolset: 14.38 + + - name: cl version (Windows) + if: runner.os == 'Windows' + shell: cmd + run: cl + + - name: Setup python (Windows) + uses: actions/setup-python@v4 + if: runner.os == 'Windows' + with: + python-version: ${{ matrix.python_version }} + + - name: Install ninja (Windows) + if: runner.os == 'Windows' + shell: bash + run: | + choco install ninja + ninja --version + where ninja + + - name: Install Boost (Windows) + if: runner.os == 'Windows' + shell: powershell + run: | + # Snippet from: https://github.com/actions/virtual-environments/issues/2667 + $BOOST_PATH = "C:\hostedtoolcache\windows\Boost\$env:BOOST_VERSION\x86_64" + + # Use the prebuilt binary for Windows + $Url = "https://sourceforge.net/projects/boost/files/boost-binaries/$env:BOOST_VERSION/$env:BOOST_EXE-${{matrix.platform}}.exe" + (New-Object System.Net.WebClient).DownloadFile($Url, "$env:TEMP\boost.exe") + Start-Process -Wait -FilePath "$env:TEMP\boost.exe" "/SILENT","/SP-","/SUPPRESSMSGBOXES","/DIR=$BOOST_PATH" + + # Set the BOOST_ROOT variable + echo "BOOST_ROOT=$BOOST_PATH" >> $env:GITHUB_ENV + - name: Set GTSAM_WITH_TBB Flag if: matrix.flag == 'tbb' run: | echo "GTSAM_WITH_TBB=ON" >> $GITHUB_ENV echo "GTSAM Uses TBB" - - name: Set Swap Space + - name: Set Swap Space (Linux) if: runner.os == 'Linux' uses: pierotofy/set-swap-space@master with: swap-size-gb: 6 - - name: Install System Dependencies + - name: Install System Dependencies (Linux, macOS) + if: runner.os != 'Windows' run: | bash .github/scripts/python.sh -d - name: Install Python Dependencies + shell: bash run: python$PYTHON_VERSION -m pip install -r python/dev_requirements.txt - name: Build + shell: bash run: | bash .github/scripts/python.sh -b - name: Test + shell: bash run: | bash .github/scripts/python.sh -t diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index f0568394fa..a73842a982 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -27,7 +27,7 @@ jobs: # Github Actions requires a single row to be added to the build matrix. # See https://help.github.com/en/articles/workflow-syntax-for-github-actions. name: [ - windows-2019-cl, + windows-2022-cl, ] build_type: [ @@ -37,12 +37,25 @@ jobs: build_unstable: [ON] include: - - name: windows-2019-cl - os: windows-2019 + - name: windows-2022-cl + os: windows-2022 compiler: cl platform: 64 steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup msbuild + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x${{ matrix.platform }} + toolset: 14.38 + + - name: cl version + shell: cmd + run: cl + - name: Install Dependencies shell: powershell run: | @@ -91,14 +104,6 @@ jobs: # Set the BOOST_ROOT variable echo "BOOST_ROOT=$BOOST_PATH" >> $env:GITHUB_ENV - - 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: | diff --git a/gtsam/3rdparty/cephes/CMakeLists.txt b/gtsam/3rdparty/cephes/CMakeLists.txt index e840e9e493..5940d39d2d 100644 --- a/gtsam/3rdparty/cephes/CMakeLists.txt +++ b/gtsam/3rdparty/cephes/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.10) enable_testing() project( cephes @@ -8,7 +8,6 @@ project( set(CEPHES_HEADER_FILES cephes.h - cephes/cephes_names.h cephes/dd_idefs.h cephes/dd_real.h cephes/dd_real_idefs.h diff --git a/gtsam/3rdparty/cephes/LICENSE.txt b/gtsam/3rdparty/cephes/LICENSE.txt new file mode 100644 index 0000000000..6c2842a963 --- /dev/null +++ b/gtsam/3rdparty/cephes/LICENSE.txt @@ -0,0 +1,7 @@ +MIT License + +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. \ No newline at end of file diff --git a/gtsam/3rdparty/cephes/README.md b/gtsam/3rdparty/cephes/README.md new file mode 100644 index 0000000000..63e3a6c8c6 --- /dev/null +++ b/gtsam/3rdparty/cephes/README.md @@ -0,0 +1,22 @@ +# README + +This is a vendored version of the Cephes Mathematical Library. The source code can be found on [netlib.org](https://www.netlib.org/cephes/). + +The software is provided with an [MIT License](https://smath.com/en-US/view/CephesMathLibrary/license). + +## Original Readme + +Some software in this archive may be from the book _Methods and +Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +International, 1989) or from the Cephes Mathematical Library, a +commercial product. In either event, it is copyrighted by the author. +What you see here may be used freely but it comes with no support or +guarantee. + +The two known misprints in the book are repaired here in the +source listings for the gamma function and the incomplete beta +integral. + + +Stephen L. Moshier +moshier@na-net.ornl.gov diff --git a/gtsam/3rdparty/cephes/cephes.h b/gtsam/3rdparty/cephes/cephes.h index 629733eef0..ed53e521bb 100644 --- a/gtsam/3rdparty/cephes/cephes.h +++ b/gtsam/3rdparty/cephes/cephes.h @@ -1,160 +1,166 @@ #ifndef CEPHES_H #define CEPHES_H -#include "cephes/cephes_names.h" +#include "dllexport.h" #ifdef __cplusplus extern "C" { #endif -extern int airy(double x, double *ai, double *aip, double *bi, double *bip); - -extern double bdtrc(double k, int n, double p); -extern double bdtr(double k, int n, double p); -extern double bdtri(double k, int n, double y); - -extern double besselpoly(double a, double lambda, double nu); - -extern double beta(double a, double b); -extern double lbeta(double a, double b); - -extern double btdtr(double a, double b, double x); - -extern double cbrt(double x); -extern double chbevl(double x, double array[], int n); -extern double chdtrc(double df, double x); -extern double chdtr(double df, double x); -extern double chdtri(double df, double y); -extern double dawsn(double xx); - -extern double ellie(double phi, double m); -extern double ellik(double phi, double m); -extern double ellpe(double x); - -extern int ellpj(double u, double m, double *sn, double *cn, double *dn, double *ph); -extern double ellpk(double x); -extern double exp10(double x); -extern double exp2(double x); - -extern double expn(int n, double x); - -extern double fdtrc(double a, double b, double x); -extern double fdtr(double a, double b, double x); -extern double fdtri(double a, double b, double y); - -extern int fresnl(double xxa, double *ssa, double *cca); -extern double Gamma(double x); -extern double lgam(double x); -extern double lgam_sgn(double x, int *sign); -extern double gammasgn(double x); - -extern double gdtr(double a, double b, double x); -extern double gdtrc(double a, double b, double x); -extern double gdtri(double a, double b, double y); - -extern double hyp2f1(double a, double b, double c, double x); -extern double hyperg(double a, double b, double x); -extern double threef0(double a, double b, double c, double x, double *err); - -extern double i0(double x); -extern double i0e(double x); -extern double i1(double x); -extern double i1e(double x); -extern double igamc(double a, double x); -extern double igam(double a, double x); -extern double igam_fac(double a, double x); -extern double igamci(double a, double q); -extern double igami(double a, double p); - -extern double incbet(double aa, double bb, double xx); -extern double incbi(double aa, double bb, double yy0); - -extern double iv(double v, double x); -extern double j0(double x); -extern double y0(double x); -extern double j1(double x); -extern double y1(double x); - -extern double jn(int n, double x); -extern double jv(double n, double x); -extern double k0(double x); -extern double k0e(double x); -extern double k1(double x); -extern double k1e(double x); -extern double kn(int nn, double x); - -extern double nbdtrc(int k, int n, double p); -extern double nbdtr(int k, int n, double p); -extern double nbdtri(int k, int n, double p); - -extern double ndtr(double a); -extern double log_ndtr(double a); -extern double erfc(double a); -extern double erf(double x); -extern double erfinv(double y); -extern double erfcinv(double y); -extern double ndtri(double y0); - -extern double pdtrc(double k, double m); -extern double pdtr(double k, double m); -extern double pdtri(int k, double y); - -extern double poch(double x, double m); - -extern double psi(double x); - -extern double rgamma(double x); -extern double round(double x); - -extern int shichi(double x, double *si, double *ci); -extern int sici(double x, double *si, double *ci); - -extern double radian(double d, double m, double s); -extern double sindg(double x); -extern double sinpi(double x); -extern double cosdg(double x); -extern double cospi(double x); - -extern double spence(double x); - -extern double stdtr(int k, double t); -extern double stdtri(int k, double p); - -extern double struve_h(double v, double x); -extern double struve_l(double v, double x); -extern double struve_power_series(double v, double x, int is_h, double *err); -extern double struve_asymp_large_z(double v, double z, int is_h, double *err); -extern double struve_bessel_series(double v, double z, int is_h, double *err); - -extern double yv(double v, double x); - -extern double tandg(double x); -extern double cotdg(double x); - -extern double log1p(double x); -extern double log1pmx(double x); -extern double expm1(double x); -extern double cosm1(double x); -extern double lgam1p(double x); - -extern double yn(int n, double x); -extern double zeta(double x, double q); -extern double zetac(double x); - -extern double smirnov(int n, double d); -extern double smirnovi(int n, double p); -extern double smirnovp(int n, double d); -extern double smirnovc(int n, double d); -extern double smirnovci(int n, double p); -extern double kolmogorov(double x); -extern double kolmogi(double p); -extern double kolmogp(double x); -extern double kolmogc(double x); -extern double kolmogci(double p); - -extern double lanczos_sum_expg_scaled(double x); - -extern double owens_t(double h, double a); +CEPHES_EXTERN_EXPORT int airy(double x, double *ai, double *aip, double *bi, + double *bip); + +CEPHES_EXTERN_EXPORT double bdtrc(double k, int n, double p); +CEPHES_EXTERN_EXPORT double bdtr(double k, int n, double p); +CEPHES_EXTERN_EXPORT double bdtri(double k, int n, double y); + +CEPHES_EXTERN_EXPORT double besselpoly(double a, double lambda, double nu); + +CEPHES_EXTERN_EXPORT double beta(double a, double b); +CEPHES_EXTERN_EXPORT double lbeta(double a, double b); + +CEPHES_EXTERN_EXPORT double btdtr(double a, double b, double x); + +CEPHES_EXTERN_EXPORT double cbrt(double x); +CEPHES_EXTERN_EXPORT double chbevl(double x, double array[], int n); +CEPHES_EXTERN_EXPORT double chdtrc(double df, double x); +CEPHES_EXTERN_EXPORT double chdtr(double df, double x); +CEPHES_EXTERN_EXPORT double chdtri(double df, double y); +CEPHES_EXTERN_EXPORT double dawsn(double xx); + +CEPHES_EXTERN_EXPORT double ellie(double phi, double m); +CEPHES_EXTERN_EXPORT double ellik(double phi, double m); +CEPHES_EXTERN_EXPORT double ellpe(double x); + +CEPHES_EXTERN_EXPORT int ellpj(double u, double m, double *sn, double *cn, + double *dn, double *ph); +CEPHES_EXTERN_EXPORT double ellpk(double x); +CEPHES_EXTERN_EXPORT double exp10(double x); +CEPHES_EXTERN_EXPORT double exp2(double x); + +CEPHES_EXTERN_EXPORT double expn(int n, double x); + +CEPHES_EXTERN_EXPORT double fdtrc(double a, double b, double x); +CEPHES_EXTERN_EXPORT double fdtr(double a, double b, double x); +CEPHES_EXTERN_EXPORT double fdtri(double a, double b, double y); + +CEPHES_EXTERN_EXPORT int fresnl(double xxa, double *ssa, double *cca); +CEPHES_EXTERN_EXPORT double Gamma(double x); +CEPHES_EXTERN_EXPORT double lgam(double x); +CEPHES_EXTERN_EXPORT double lgam_sgn(double x, int *sign); +CEPHES_EXTERN_EXPORT double gammasgn(double x); + +CEPHES_EXTERN_EXPORT double gdtr(double a, double b, double x); +CEPHES_EXTERN_EXPORT double gdtrc(double a, double b, double x); +CEPHES_EXTERN_EXPORT double gdtri(double a, double b, double y); + +CEPHES_EXTERN_EXPORT double hyp2f1(double a, double b, double c, double x); +CEPHES_EXTERN_EXPORT double hyperg(double a, double b, double x); +CEPHES_EXTERN_EXPORT double threef0(double a, double b, double c, double x, + double *err); + +CEPHES_EXTERN_EXPORT double i0(double x); +CEPHES_EXTERN_EXPORT double i0e(double x); +CEPHES_EXTERN_EXPORT double i1(double x); +CEPHES_EXTERN_EXPORT double i1e(double x); +CEPHES_EXTERN_EXPORT double igamc(double a, double x); +CEPHES_EXTERN_EXPORT double igam(double a, double x); +CEPHES_EXTERN_EXPORT double igam_fac(double a, double x); +CEPHES_EXTERN_EXPORT double igamci(double a, double q); +CEPHES_EXTERN_EXPORT double igami(double a, double p); + +CEPHES_EXTERN_EXPORT double incbet(double aa, double bb, double xx); +CEPHES_EXTERN_EXPORT double incbi(double aa, double bb, double yy0); + +CEPHES_EXTERN_EXPORT double iv(double v, double x); +CEPHES_EXTERN_EXPORT double j0(double x); +CEPHES_EXTERN_EXPORT double y0(double x); +CEPHES_EXTERN_EXPORT double j1(double x); +CEPHES_EXTERN_EXPORT double y1(double x); + +CEPHES_EXTERN_EXPORT double jn(int n, double x); +CEPHES_EXTERN_EXPORT double jv(double n, double x); +CEPHES_EXTERN_EXPORT double k0(double x); +CEPHES_EXTERN_EXPORT double k0e(double x); +CEPHES_EXTERN_EXPORT double k1(double x); +CEPHES_EXTERN_EXPORT double k1e(double x); +CEPHES_EXTERN_EXPORT double kn(int nn, double x); + +CEPHES_EXTERN_EXPORT double nbdtrc(int k, int n, double p); +CEPHES_EXTERN_EXPORT double nbdtr(int k, int n, double p); +CEPHES_EXTERN_EXPORT double nbdtri(int k, int n, double p); + +CEPHES_EXTERN_EXPORT double ndtr(double a); +CEPHES_EXTERN_EXPORT double log_ndtr(double a); +CEPHES_EXTERN_EXPORT double erfc(double a); +CEPHES_EXTERN_EXPORT double erf(double x); +CEPHES_EXTERN_EXPORT double erfinv(double y); +CEPHES_EXTERN_EXPORT double erfcinv(double y); +CEPHES_EXTERN_EXPORT double ndtri(double y0); + +CEPHES_EXTERN_EXPORT double pdtrc(double k, double m); +CEPHES_EXTERN_EXPORT double pdtr(double k, double m); +CEPHES_EXTERN_EXPORT double pdtri(int k, double y); + +CEPHES_EXTERN_EXPORT double poch(double x, double m); + +CEPHES_EXTERN_EXPORT double psi(double x); + +CEPHES_EXTERN_EXPORT double rgamma(double x); +CEPHES_EXTERN_EXPORT double round(double x); + +CEPHES_EXTERN_EXPORT int shichi(double x, double *si, double *ci); +CEPHES_EXTERN_EXPORT int sici(double x, double *si, double *ci); + +CEPHES_EXTERN_EXPORT double radian(double d, double m, double s); +CEPHES_EXTERN_EXPORT double sindg(double x); +CEPHES_EXTERN_EXPORT double sinpi(double x); +CEPHES_EXTERN_EXPORT double cosdg(double x); +CEPHES_EXTERN_EXPORT double cospi(double x); + +CEPHES_EXTERN_EXPORT double spence(double x); + +CEPHES_EXTERN_EXPORT double stdtr(int k, double t); +CEPHES_EXTERN_EXPORT double stdtri(int k, double p); + +CEPHES_EXTERN_EXPORT double struve_h(double v, double x); +CEPHES_EXTERN_EXPORT double struve_l(double v, double x); +CEPHES_EXTERN_EXPORT double struve_power_series(double v, double x, int is_h, + double *err); +CEPHES_EXTERN_EXPORT double struve_asymp_large_z(double v, double z, int is_h, + double *err); +CEPHES_EXTERN_EXPORT double struve_bessel_series(double v, double z, int is_h, + double *err); + +CEPHES_EXTERN_EXPORT double yv(double v, double x); + +CEPHES_EXTERN_EXPORT double tandg(double x); +CEPHES_EXTERN_EXPORT double cotdg(double x); + +CEPHES_EXTERN_EXPORT double log1p(double x); +CEPHES_EXTERN_EXPORT double log1pmx(double x); +CEPHES_EXTERN_EXPORT double expm1(double x); +CEPHES_EXTERN_EXPORT double cosm1(double x); +CEPHES_EXTERN_EXPORT double lgam1p(double x); + +CEPHES_EXTERN_EXPORT double yn(int n, double x); +CEPHES_EXTERN_EXPORT double zeta(double x, double q); +CEPHES_EXTERN_EXPORT double zetac(double x); + +CEPHES_EXTERN_EXPORT double smirnov(int n, double d); +CEPHES_EXTERN_EXPORT double smirnovi(int n, double p); +CEPHES_EXTERN_EXPORT double smirnovp(int n, double d); +CEPHES_EXTERN_EXPORT double smirnovc(int n, double d); +CEPHES_EXTERN_EXPORT double smirnovci(int n, double p); +CEPHES_EXTERN_EXPORT double kolmogorov(double x); +CEPHES_EXTERN_EXPORT double kolmogi(double p); +CEPHES_EXTERN_EXPORT double kolmogp(double x); +CEPHES_EXTERN_EXPORT double kolmogc(double x); +CEPHES_EXTERN_EXPORT double kolmogci(double p); + +CEPHES_EXTERN_EXPORT double lanczos_sum_expg_scaled(double x); + +CEPHES_EXTERN_EXPORT double owens_t(double h, double a); #ifdef __cplusplus } diff --git a/gtsam/3rdparty/cephes/cephes/cephes_names.h b/gtsam/3rdparty/cephes/cephes/cephes_names.h deleted file mode 100644 index 94be8c880a..0000000000 --- a/gtsam/3rdparty/cephes/cephes/cephes_names.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef CEPHES_NAMES_H -#define CEPHES_NAMES_H - -#define airy cephes_airy -#define bdtrc cephes_bdtrc -#define bdtr cephes_bdtr -#define bdtri cephes_bdtri -#define besselpoly cephes_besselpoly -#define beta cephes_beta -#define lbeta cephes_lbeta -#define btdtr cephes_btdtr -#define cbrt cephes_cbrt -#define chdtrc cephes_chdtrc -#define chbevl cephes_chbevl -#define chdtr cephes_chdtr -#define chdtri cephes_chdtri -#define dawsn cephes_dawsn -#define ellie cephes_ellie -#define ellik cephes_ellik -#define ellpe cephes_ellpe -#define ellpj cephes_ellpj -#define ellpk cephes_ellpk -#define exp10 cephes_exp10 -#define exp2 cephes_exp2 -#define expn cephes_expn -#define fdtrc cephes_fdtrc -#define fdtr cephes_fdtr -#define fdtri cephes_fdtri -#define fresnl cephes_fresnl -#define Gamma cephes_Gamma -#define lgam cephes_lgam -#define lgam_sgn cephes_lgam_sgn -#define gammasgn cephes_gammasgn -#define gdtr cephes_gdtr -#define gdtrc cephes_gdtrc -#define gdtri cephes_gdtri -#define hyp2f1 cephes_hyp2f1 -#define hyperg cephes_hyperg -#define i0 cephes_i0 -#define i0e cephes_i0e -#define i1 cephes_i1 -#define i1e cephes_i1e -#define igamc cephes_igamc -#define igam cephes_igam -#define igami cephes_igami -#define incbet cephes_incbet -#define incbi cephes_incbi -#define iv cephes_iv -#define j0 cephes_j0 -#define y0 cephes_y0 -#define j1 cephes_j1 -#define y1 cephes_y1 -#define jn cephes_jn -#define jv cephes_jv -#define k0 cephes_k0 -#define k0e cephes_k0e -#define k1 cephes_k1 -#define k1e cephes_k1e -#define kn cephes_kn -#define nbdtrc cephes_nbdtrc -#define nbdtr cephes_nbdtr -#define nbdtri cephes_nbdtri -#define ndtr cephes_ndtr -#define erfc cephes_erfc -#define erf cephes_erf -#define erfinv cephes_erfinv -#define erfcinv cephes_erfcinv -#define ndtri cephes_ndtri -#define pdtrc cephes_pdtrc -#define pdtr cephes_pdtr -#define pdtri cephes_pdtri -#define poch cephes_poch -#define psi cephes_psi -#define rgamma cephes_rgamma -#define riemann_zeta cephes_riemann_zeta -// #define round cephes_round // Commented out since it clashes with std::round -#define shichi cephes_shichi -#define sici cephes_sici -#define radian cephes_radian -#define sindg cephes_sindg -#define sinpi cephes_sinpi -#define cosdg cephes_cosdg -#define cospi cephes_cospi -#define sincos cephes_sincos -#define spence cephes_spence -#define stdtr cephes_stdtr -#define stdtri cephes_stdtri -#define struve_h cephes_struve_h -#define struve_l cephes_struve_l -#define struve_power_series cephes_struve_power_series -#define struve_asymp_large_z cephes_struve_asymp_large_z -#define struve_bessel_series cephes_struve_bessel_series -#define yv cephes_yv -#define tandg cephes_tandg -#define cotdg cephes_cotdg -#define log1p cephes_log1p -#define expm1 cephes_expm1 -#define cosm1 cephes_cosm1 -#define yn cephes_yn -#define zeta cephes_zeta -#define zetac cephes_zetac -#define smirnov cephes_smirnov -#define smirnovc cephes_smirnovc -#define smirnovi cephes_smirnovi -#define smirnovci cephes_smirnovci -#define smirnovp cephes_smirnovp -#define kolmogorov cephes_kolmogorov -#define kolmogi cephes_kolmogi -#define kolmogp cephes_kolmogp -#define kolmogc cephes_kolmogc -#define kolmogci cephes_kolmogci -#define owens_t cephes_owens_t - -#endif diff --git a/gtsam/3rdparty/cephes/cephes/mconf.h b/gtsam/3rdparty/cephes/cephes/mconf.h index c59d17a470..5e971afadf 100644 --- a/gtsam/3rdparty/cephes/cephes/mconf.h +++ b/gtsam/3rdparty/cephes/cephes/mconf.h @@ -56,7 +56,6 @@ #include #include -#include "cephes_names.h" #include "cephes.h" #include "polevl.h" #include "sf_error.h" diff --git a/gtsam/3rdparty/cephes/dllexport.h b/gtsam/3rdparty/cephes/dllexport.h new file mode 100644 index 0000000000..525587164e --- /dev/null +++ b/gtsam/3rdparty/cephes/dllexport.h @@ -0,0 +1,25 @@ +// Macros for exporting DLL symbols on Windows +// Usage example: +// In header file: +// class CEPHES_EXPORT MyClass { ... }; +// +// Results in the following declarations: +// When included while compiling the library itself: +// class __declspec(dllexport) MyClass { ... }; +// When included while compiling other code against the library: +// class __declspec(dllimport) MyClass { ... }; + +#pragma once + +#ifdef _WIN32 +# define CEPHES_EXPORT __declspec(dllimport) +# define CEPHES_EXTERN_EXPORT __declspec(dllimport) +#else +#ifdef __APPLE__ +# define CEPHES_EXPORT __attribute__((visibility("default"))) +# define CEPHES_EXTERN_EXPORT extern +#else +# define CEPHES_EXPORT +# define CEPHES_EXTERN_EXPORT extern +#endif +#endif diff --git a/gtsam/base/std_optional_serialization.h b/gtsam/base/std_optional_serialization.h index 5c250eab43..7e0f2e844a 100644 --- a/gtsam/base/std_optional_serialization.h +++ b/gtsam/base/std_optional_serialization.h @@ -8,10 +8,10 @@ * Functionality to serialize std::optional to boost::archive * Inspired from this PR: https://github.com/boostorg/serialization/pull/163 * ---------------------------------------------------------------------------- */ +#pragma once // Defined only if boost serialization is enabled #ifdef GTSAM_ENABLE_BOOST_SERIALIZATION -#pragma once #include #include @@ -55,7 +55,14 @@ namespace std { template<> struct is_trivially_move_constructible& t, const unsigned int version) { } // namespace serialization } // namespace boost -#endif +#endif // BOOST_VERSION < 108400 +#endif // GTSAM_ENABLE_BOOST_SERIALIZATION diff --git a/gtsam/base/utilities.h b/gtsam/base/utilities.h index 03e9636da4..a67c5d1b6c 100644 --- a/gtsam/base/utilities.h +++ b/gtsam/base/utilities.h @@ -4,6 +4,8 @@ #include #include +#include + namespace gtsam { /** * For Python __str__(). @@ -11,7 +13,7 @@ namespace gtsam { * of an object when it prints to cout. * https://stackoverflow.com/questions/5419356/redirect-stdout-stderr-to-a-string */ -struct RedirectCout { +struct GTSAM_EXPORT RedirectCout { /// constructor -- redirect stdout buffer to a stringstream buffer RedirectCout() : ssBuffer_(), coutBuffer_(std::cout.rdbuf(ssBuffer_.rdbuf())) {} diff --git a/gtsam/discrete/DiscreteValues.h b/gtsam/discrete/DiscreteValues.h index 9ec08302bd..9fdff014cc 100644 --- a/gtsam/discrete/DiscreteValues.h +++ b/gtsam/discrete/DiscreteValues.h @@ -126,12 +126,12 @@ inline std::vector cartesianProduct(const DiscreteKeys& keys) { } /// Free version of markdown. -std::string markdown(const DiscreteValues& values, +std::string GTSAM_EXPORT markdown(const DiscreteValues& values, const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DiscreteValues::Names& names = {}); /// Free version of html. -std::string html(const DiscreteValues& values, +std::string GTSAM_EXPORT html(const DiscreteValues& values, const KeyFormatter& keyFormatter = DefaultKeyFormatter, const DiscreteValues::Names& names = {}); diff --git a/gtsam/geometry/CameraSet.h b/gtsam/geometry/CameraSet.h index cf4beb8830..26d4952c8b 100644 --- a/gtsam/geometry/CameraSet.h +++ b/gtsam/geometry/CameraSet.h @@ -327,12 +327,16 @@ class CameraSet : public std::vector> { * g = F' * (b - E * P * E' * b) * Fixed size version */ +#ifdef _WIN32 +#if _MSC_VER < 1937 template // N = 2 or 3 static SymmetricBlockMatrix SchurComplement( const FBlocks& Fs, const Matrix& E, const Eigen::Matrix& P, const Vector& b) { return SchurComplement(Fs, E, P, b); } +#endif +#endif /// Computes Point Covariance P, with lambda parameter template // N = 2 or 3 (point dimension) diff --git a/gtsam/geometry/Rot3.h b/gtsam/geometry/Rot3.h index 2b9c5a45a8..7e05ee4daf 100644 --- a/gtsam/geometry/Rot3.h +++ b/gtsam/geometry/Rot3.h @@ -396,7 +396,7 @@ class GTSAM_EXPORT Rot3 : public LieGroup { Matrix3 AdjointMap() const { return matrix(); } // Chart at origin, depends on compile-time flag ROT3_DEFAULT_COORDINATES_MODE - struct ChartAtOrigin { + struct GTSAM_EXPORT ChartAtOrigin { static Rot3 Retract(const Vector3& v, ChartJacobian H = {}); static Vector3 Local(const Rot3& r, ChartJacobian H = {}); }; diff --git a/gtsam/geometry/Similarity3.h b/gtsam/geometry/Similarity3.h index cd4af89bc0..05bd0431e1 100644 --- a/gtsam/geometry/Similarity3.h +++ b/gtsam/geometry/Similarity3.h @@ -202,6 +202,18 @@ class GTSAM_EXPORT Similarity3 : public LieGroup { /// @{ 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_NVP(R_); + ar & BOOST_SERIALIZATION_NVP(t_); + ar & BOOST_SERIALIZATION_NVP(s_); + } + #endif + /// Calculate expmap and logmap coefficients. static Matrix3 GetV(Vector3 w, double lambda); diff --git a/gtsam/gtsam.i b/gtsam/gtsam.i index 834d5a1476..6d77e8eda0 100644 --- a/gtsam/gtsam.i +++ b/gtsam/gtsam.i @@ -39,6 +39,11 @@ class KeyList { void remove(size_t key); void serialize() const; + + // Special dunder methods for Python wrapping + __len__(); + __contains__(size_t key); + __iter__(); }; // Actually a FastSet @@ -64,6 +69,11 @@ class KeySet { bool count(size_t key) const; // returns true if value exists void serialize() const; + + // Special dunder methods for Python wrapping + __len__(); + __contains__(size_t key); + __iter__(); }; // Actually a vector, needed for Matlab @@ -85,6 +95,11 @@ class KeyVector { void push_back(size_t key) const; void serialize() const; + + // Special dunder methods for Python wrapping + __len__(); + __contains__(size_t key); + __iter__(); }; // Actually a FastMap diff --git a/gtsam/navigation/BarometricFactor.h b/gtsam/navigation/BarometricFactor.h index 38677ed589..70cae8d367 100644 --- a/gtsam/navigation/BarometricFactor.h +++ b/gtsam/navigation/BarometricFactor.h @@ -91,7 +91,7 @@ class GTSAM_EXPORT BarometricFactor : public NoiseModelFactorN { -0.00649; } - inline double baroOut(const double& meters) { + inline double baroOut(const double& meters) const { double temp = 15.04 - 0.00649 * meters; return 101.29 * std::pow(((temp + 273.1) / 288.08), 5.256); } diff --git a/gtsam/navigation/navigation.i b/gtsam/navigation/navigation.i index 8e6090e06a..92864c18a0 100644 --- a/gtsam/navigation/navigation.i +++ b/gtsam/navigation/navigation.i @@ -294,7 +294,7 @@ virtual class GPSFactor : gtsam::NonlinearFactor{ // Testable void print(string s = "", const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter) const; - bool equals(const gtsam::GPSFactor& expected, double tol); + bool equals(const gtsam::NonlinearFactor& expected, double tol); // Standard Interface gtsam::Point3 measurementIn() const; @@ -307,12 +307,29 @@ virtual class GPSFactor2 : gtsam::NonlinearFactor { // Testable void print(string s = "", const gtsam::KeyFormatter& keyFormatter = gtsam::DefaultKeyFormatter) const; - bool equals(const gtsam::GPSFactor2& expected, double tol); + bool equals(const gtsam::NonlinearFactor& expected, double tol); // Standard Interface gtsam::Point3 measurementIn() const; }; +#include +virtual class BarometricFactor : gtsam::NonlinearFactor { + BarometricFactor(); + BarometricFactor(size_t key, size_t baroKey, const double& baroIn, + const gtsam::noiseModel::Base* model); + + // Testable + void print(string s = "", const gtsam::KeyFormatter& keyFormatter = + gtsam::DefaultKeyFormatter) const; + bool equals(const gtsam::NonlinearFactor& expected, double tol); + + // Standard Interface + const double& measurementIn() const; + double heightOut(double n) const; + double baroOut(const double& meters) const; +}; + #include virtual class Scenario { gtsam::Pose3 pose(double t) const; diff --git a/gtsam/nonlinear/CustomFactor.h b/gtsam/nonlinear/CustomFactor.h index ac29420320..c4015db373 100644 --- a/gtsam/nonlinear/CustomFactor.h +++ b/gtsam/nonlinear/CustomFactor.h @@ -42,7 +42,7 @@ using CustomErrorFunction = std::function + struct handle_matrix, true> { + inline Eigen::Matrix operator()(Key j, const Value* const pointer) { + auto ptr = dynamic_cast>*>(pointer); + if (ptr) { + // value returns a const Matrix&, and the return makes a copy !!!!! + return ptr->value(); + } else { + // If a fixed matrix was stored, we end up here as well. + throw ValuesIncorrectType(j, typeid(*pointer), typeid(Eigen::Matrix)); + } + } + }; + + // Handle fixed matrices + template + struct handle_matrix, false> { + inline Eigen::Matrix operator()(Key j, const Value* const pointer) { + auto ptr = dynamic_cast>*>(pointer); + if (ptr) { + // value returns a const MatrixMN&, and the return makes a copy !!!!! + return ptr->value(); + } else { + Matrix A; + // Check if a dynamic matrix was stored + auto ptr = dynamic_cast*>(pointer); + if (ptr) { + A = ptr->value(); + } else { + // Or a dynamic vector + A = handle_matrix()(j, pointer); // will throw if not.... + } + // Yes: check size, and throw if not a match + if (A.rows() != M || A.cols() != N) + throw NoMatchFoundForFixed(M, N, A.rows(), A.cols()); + else + return A; // copy but not malloc + } + } + }; + + // Handle matrices + template + struct handle> { + Eigen::Matrix operator()(Key j, const Value* const pointer) { + return handle_matrix, + (M == Eigen::Dynamic || N == Eigen::Dynamic)>()(j, pointer); + } + }; +#endif // #if _MSC_VER < 1937 +#endif // #ifdef _WIN32 + } // internal /* ************************************************************************* */ diff --git a/gtsam/sfm/DsfTrackGenerator.h b/gtsam/sfm/DsfTrackGenerator.h index 27cfdfa2ba..35102401e9 100644 --- a/gtsam/sfm/DsfTrackGenerator.h +++ b/gtsam/sfm/DsfTrackGenerator.h @@ -69,7 +69,7 @@ using MatchIndicesMap = std::map; * correspondence indices, from each image. * @param Length-N list of keypoints, for N images/cameras. */ -std::vector tracksFromPairwiseMatches( +std::vector GTSAM_EXPORT tracksFromPairwiseMatches( const MatchIndicesMap& matches, const KeypointsVector& keypoints, bool verbose = false); diff --git a/gtsam/slam/slam.i b/gtsam/slam/slam.i index 7135137bba..64977a2a5f 100644 --- a/gtsam/slam/slam.i +++ b/gtsam/slam/slam.i @@ -7,12 +7,13 @@ namespace gtsam { #include #include #include +#include // ###### #include template virtual class BetweenFactor : gtsam::NoiseModelFactor { BetweenFactor(size_t key1, size_t key2, const T& relativePose, diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index f874c2f217..ba55ac2af2 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -94,6 +94,14 @@ set(interface_headers set(GTSAM_PYTHON_TARGET gtsam_py) set(GTSAM_PYTHON_UNSTABLE_TARGET gtsam_unstable_py) +set(GTSAM_OUTPUT_NAME "gtsam") +set(GTSAM_UNSTABLE_OUTPUT_NAME "gtsam_unstable") + +if(MSVC) + set(GTSAM_OUTPUT_NAME "gtsam_py") + set(GTSAM_UNSTABLE_OUTPUT_NAME "gtsam_unstable_py") +endif() + pybind_wrap(${GTSAM_PYTHON_TARGET} # target "${interface_headers}" # interface_headers "gtsam.cpp" # generated_cpp @@ -109,12 +117,30 @@ pybind_wrap(${GTSAM_PYTHON_TARGET} # target set_target_properties(${GTSAM_PYTHON_TARGET} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" INSTALL_RPATH_USE_LINK_PATH TRUE - OUTPUT_NAME "gtsam" + OUTPUT_NAME "${GTSAM_OUTPUT_NAME}" LIBRARY_OUTPUT_DIRECTORY "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam" DEBUG_POSTFIX "" # Otherwise you will have a wrong name RELWITHDEBINFO_POSTFIX "" # Otherwise you will have a wrong name ) +if(WIN32) + set_target_properties(${GTSAM_PYTHON_TARGET} PROPERTIES + SUFFIX ".pyd" + ) + ADD_CUSTOM_COMMAND(TARGET ${GTSAM_PYTHON_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam/${GTSAM_OUTPUT_NAME}.pyd" + "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam/gtsam.pyd" + ) + ADD_CUSTOM_COMMAND(TARGET ${GTSAM_PYTHON_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$;$" + "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam/" + COMMAND_EXPAND_LISTS + VERBATIM + ) +endif() + # Set the path for the GTSAM python module set(GTSAM_MODULE_PATH ${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam) @@ -188,7 +214,7 @@ if(GTSAM_UNSTABLE_BUILD_PYTHON) set_target_properties(${GTSAM_PYTHON_UNSTABLE_TARGET} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib" INSTALL_RPATH_USE_LINK_PATH TRUE - OUTPUT_NAME "gtsam_unstable" + OUTPUT_NAME "${GTSAM_UNSTABLE_OUTPUT_NAME}" LIBRARY_OUTPUT_DIRECTORY "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam_unstable" DEBUG_POSTFIX "" # Otherwise you will have a wrong name RELWITHDEBINFO_POSTFIX "" # Otherwise you will have a wrong name @@ -208,13 +234,39 @@ if(GTSAM_UNSTABLE_BUILD_PYTHON) # Add gtsam_unstable to the install target list(APPEND GTSAM_PYTHON_DEPENDENCIES ${GTSAM_PYTHON_UNSTABLE_TARGET}) - + if(WIN32) + set_target_properties(${GTSAM_PYTHON_UNSTABLE_TARGET} PROPERTIES + SUFFIX ".pyd" + ) + ADD_CUSTOM_COMMAND(TARGET ${GTSAM_PYTHON_UNSTABLE_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam_unstable/${GTSAM_UNSTABLE_OUTPUT_NAME}.pyd" + "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam_unstable/gtsam_unstable.pyd" + ) + ADD_CUSTOM_COMMAND(TARGET ${GTSAM_PYTHON_UNSTABLE_TARGET} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$;$" + "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam_unstable/" + COMMAND_EXPAND_LISTS + VERBATIM + ) + endif() + # Custom make command to run all GTSAM_UNSTABLE Python tests + add_custom_target( + python-test-unstable + COMMAND + ${CMAKE_COMMAND} -E env # add package to python path so no need to install + "PYTHONPATH=${GTSAM_PYTHON_BUILD_DIRECTORY}/$ENV{PYTHONPATH}" + ${PYTHON_EXECUTABLE} -m unittest discover -v -s . + DEPENDS ${GTSAM_PYTHON_DEPENDENCIES} ${GTSAM_PYTHON_TEST_FILES} + WORKING_DIRECTORY "${GTSAM_PYTHON_BUILD_DIRECTORY}/gtsam_unstable/tests" + ) endif() # Add custom target so we can install with `make python-install` set(GTSAM_PYTHON_INSTALL_TARGET python-install) add_custom_target(${GTSAM_PYTHON_INSTALL_TARGET} - COMMAND ${PYTHON_EXECUTABLE} -m pip install . + COMMAND ${PYTHON_EXECUTABLE} -m pip install --user . DEPENDS ${GTSAM_PYTHON_DEPENDENCIES} WORKING_DIRECTORY ${GTSAM_PYTHON_BUILD_DIRECTORY}) diff --git a/python/gtsam/tests/test_Utilities.py b/python/gtsam/tests/test_Utilities.py index 851684f124..3dd472c75e 100644 --- a/python/gtsam/tests/test_Utilities.py +++ b/python/gtsam/tests/test_Utilities.py @@ -12,13 +12,14 @@ import unittest import numpy as np +from gtsam.utils.test_case import GtsamTestCase import gtsam -from gtsam.utils.test_case import GtsamTestCase class TestUtilites(GtsamTestCase): """Test various GTSAM utilities.""" + def test_createKeyList(self): """Test createKeyList.""" I = [0, 1, 2] @@ -28,6 +29,17 @@ def test_createKeyList(self): kl = gtsam.utilities.createKeyList("s", I) self.assertEqual(kl.size(), 3) + def test_KeyList_iteration(self): + """Tests for KeyList iteration""" + I = [0, 1, 2] + kl = gtsam.utilities.createKeyList(I) + + self.assertEqual(len(kl), len(I)) + + for i, key in enumerate(kl): + self.assertTrue(key in kl) + self.assertEqual(I[i], key) + def test_createKeyVector(self): """Test createKeyVector.""" I = [0, 1, 2] @@ -37,6 +49,17 @@ def test_createKeyVector(self): kl = gtsam.utilities.createKeyVector("s", I) self.assertEqual(len(kl), 3) + def test_KeyVector_iteration(self): + """Tests for KeyVector iteration""" + I = [0, 1, 2] + kv = gtsam.utilities.createKeyVector(I) + + self.assertEqual(len(kv), len(I)) + + for i, key in enumerate(kv): + self.assertTrue(key in kv) + self.assertEqual(I[i], key) + def test_createKeySet(self): """Test createKeySet.""" I = [0, 1, 2] @@ -46,6 +69,17 @@ def test_createKeySet(self): kl = gtsam.utilities.createKeySet("s", I) self.assertEqual(kl.size(), 3) + def test_KeySet_iteration(self): + """Tests for KeySet iteration""" + I = [0, 1, 2] + ks = gtsam.utilities.createKeySet(I) + + self.assertEqual(len(ks), len(I)) + + for i, key in enumerate(ks): + self.assertTrue(key in ks) + self.assertEqual(I[i], key) + def test_extractPoint2(self): """Test extractPoint2.""" initial = gtsam.Values() diff --git a/python/setup.py.in b/python/setup.py.in index e15e390754..824a6656ed 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -11,7 +11,8 @@ print("PACKAGES: ", packages) package_data = { '': [ "./*.so", - "./*.dll" + "./*.dll", + "./*.pyd", ] } diff --git a/wrap/gtwrap/interface_parser/classes.py b/wrap/gtwrap/interface_parser/classes.py index b63a0b5eb2..8967bea93b 100644 --- a/wrap/gtwrap/interface_parser/classes.py +++ b/wrap/gtwrap/interface_parser/classes.py @@ -12,13 +12,14 @@ from typing import Any, Iterable, List, Union -from pyparsing import Literal, Optional, ZeroOrMore # type: ignore +from pyparsing import ZeroOrMore # type: ignore +from pyparsing import Literal, Optional, Word, alphas from .enum import Enum from .function import ArgumentList, ReturnType from .template import Template -from .tokens import (CLASS, COLON, CONST, IDENT, LBRACE, LPAREN, OPERATOR, - RBRACE, RPAREN, SEMI_COLON, STATIC, VIRTUAL) +from .tokens import (CLASS, COLON, CONST, DUNDER, IDENT, LBRACE, LPAREN, + OPERATOR, RBRACE, RPAREN, SEMI_COLON, STATIC, VIRTUAL) from .type import TemplatedType, Typename from .utils import collect_namespaces from .variable import Variable @@ -212,6 +213,26 @@ def __repr__(self) -> str: ) +class DunderMethod: + """Special Python double-underscore (dunder) methods, e.g. __iter__, __contains__""" + rule = ( + DUNDER # + + (Word(alphas))("name") # + + DUNDER # + + LPAREN # + + ArgumentList.rule("args_list") # + + RPAREN # + + SEMI_COLON # BR + ).setParseAction(lambda t: DunderMethod(t.name, t.args_list)) + + def __init__(self, name: str, args: ArgumentList): + self.name = name + self.args = args + + def __repr__(self) -> str: + return f"DunderMethod: __{self.name}__({self.args})" + + class Class: """ Rule to parse a class defined in the interface file. @@ -223,11 +244,13 @@ class Hello { }; ``` """ + class Members: """ Rule for all the members within a class. """ - rule = ZeroOrMore(Constructor.rule # + rule = ZeroOrMore(DunderMethod.rule # + ^ Constructor.rule # ^ Method.rule # ^ StaticMethod.rule # ^ Variable.rule # @@ -235,11 +258,12 @@ class Members: ^ Enum.rule # ).setParseAction(lambda t: Class.Members(t.asList())) - def __init__(self, - members: List[Union[Constructor, Method, StaticMethod, - Variable, Operator]]): + def __init__(self, members: List[Union[Constructor, Method, + StaticMethod, Variable, + Operator, Enum, DunderMethod]]): self.ctors = [] self.methods = [] + self.dunder_methods = [] self.static_methods = [] self.properties = [] self.operators = [] @@ -251,6 +275,8 @@ def __init__(self, self.methods.append(m) elif isinstance(m, StaticMethod): self.static_methods.append(m) + elif isinstance(m, DunderMethod): + self.dunder_methods.append(m) elif isinstance(m, Variable): self.properties.append(m) elif isinstance(m, Operator): @@ -271,8 +297,8 @@ def __init__(self, + SEMI_COLON # BR ).setParseAction(lambda t: Class( t.template, t.is_virtual, t.name, t.parent_class, t.members.ctors, t. - members.methods, t.members.static_methods, t.members.properties, t. - members.operators, t.members.enums)) + members.methods, t.members.static_methods, t.members.dunder_methods, t. + members.properties, t.members.operators, t.members.enums)) def __init__( self, @@ -283,6 +309,7 @@ def __init__( ctors: List[Constructor], methods: List[Method], static_methods: List[StaticMethod], + dunder_methods: List[DunderMethod], properties: List[Variable], operators: List[Operator], enums: List[Enum], @@ -308,6 +335,7 @@ def __init__( self.ctors = ctors self.methods = methods self.static_methods = static_methods + self.dunder_methods = dunder_methods self.properties = properties self.operators = operators self.enums = enums @@ -326,6 +354,8 @@ def __init__( method.parent = self for static_method in self.static_methods: static_method.parent = self + for dunder_method in self.dunder_methods: + dunder_method.parent = self for _property in self.properties: _property.parent = self diff --git a/wrap/gtwrap/interface_parser/function.py b/wrap/gtwrap/interface_parser/function.py index b408844886..5385c744f1 100644 --- a/wrap/gtwrap/interface_parser/function.py +++ b/wrap/gtwrap/interface_parser/function.py @@ -82,7 +82,7 @@ def from_parse_result(parse_result: ParseResults): return ArgumentList([]) def __repr__(self) -> str: - return repr(tuple(self.args_list)) + return ",".join([repr(x) for x in self.args_list]) def __len__(self) -> int: return len(self.args_list) diff --git a/wrap/gtwrap/interface_parser/tokens.py b/wrap/gtwrap/interface_parser/tokens.py index 02e6d82f84..11c99d19c2 100644 --- a/wrap/gtwrap/interface_parser/tokens.py +++ b/wrap/gtwrap/interface_parser/tokens.py @@ -22,6 +22,7 @@ LPAREN, RPAREN, LBRACE, RBRACE, COLON, SEMI_COLON = map(Suppress, "(){}:;") LOPBRACK, ROPBRACK, COMMA, EQUAL = map(Suppress, "<>,=") +DUNDER = Suppress(Literal("__")) # Default argument passed to functions/methods. # Allow anything up to ',' or ';' except when they diff --git a/wrap/gtwrap/pybind_wrapper.py b/wrap/gtwrap/pybind_wrapper.py index 78730a909f..479c2d67d4 100755 --- a/wrap/gtwrap/pybind_wrapper.py +++ b/wrap/gtwrap/pybind_wrapper.py @@ -45,6 +45,8 @@ def __init__(self, 'continue', 'global', 'pass' ] + self.dunder_methods = ('len', 'contains', 'iter') + # amount of indentation to add before each function/method declaration. self.method_indent = '\n' + (' ' * 8) @@ -153,6 +155,51 @@ def _wrap_print(self, ret: str, method: parser.Method, cpp_class: str, suffix=suffix) return ret + def _wrap_dunder(self, + method, + cpp_class, + prefix, + suffix, + method_suffix=""): + """ + Wrap a Python double-underscore (dunder) method. + + E.g. __len__() gets wrapped as `.def("__len__", [](gtsam::KeySet* self) {return self->size();})` + + Supported methods are: + - __contains__(T x) + - __len__() + - __iter__() + """ + py_method = method.name + method_suffix + args_names = method.args.names() + py_args_names = self._py_args_names(method.args) + args_signature_with_names = self._method_args_signature(method.args) + + if method.name == 'len': + function_call = "return std::distance(self->begin(), self->end());" + elif method.name == 'contains': + function_call = f"return std::find(self->begin(), self->end(), {method.args.args_list[0].name}) != self->end();" + elif method.name == 'iter': + function_call = "return py::make_iterator(self->begin(), self->end());" + + ret = ('{prefix}.def("__{py_method}__",' + '[]({self}{opt_comma}{args_signature_with_names}){{' + '{function_call}' + '}}' + '{py_args_names}){suffix}'.format( + prefix=prefix, + py_method=py_method, + self=f"{cpp_class}* self", + opt_comma=', ' if args_names else '', + args_signature_with_names=args_signature_with_names, + function_call=function_call, + py_args_names=py_args_names, + suffix=suffix, + )) + + return ret + def _wrap_method(self, method, cpp_class, @@ -235,6 +282,20 @@ def _wrap_method(self, return ret + def wrap_dunder_methods(self, + methods, + cpp_class, + prefix='\n' + ' ' * 8, + suffix=''): + res = "" + for method in methods: + res += self._wrap_dunder(method=method, + cpp_class=cpp_class, + prefix=prefix, + suffix=suffix) + + return res + def wrap_methods(self, methods, cpp_class, @@ -398,6 +459,7 @@ def wrap_instantiated_class( '{wrapped_ctors}' '{wrapped_methods}' '{wrapped_static_methods}' + '{wrapped_dunder_methods}' '{wrapped_properties}' '{wrapped_operators};\n'.format( class_declaration=class_declaration, @@ -406,6 +468,8 @@ def wrap_instantiated_class( instantiated_class.methods, cpp_class), wrapped_static_methods=self.wrap_methods( instantiated_class.static_methods, cpp_class), + wrapped_dunder_methods=self.wrap_dunder_methods( + instantiated_class.dunder_methods, cpp_class), wrapped_properties=self.wrap_properties( instantiated_class.properties, cpp_class), wrapped_operators=self.wrap_operators( diff --git a/wrap/gtwrap/template_instantiator/classes.py b/wrap/gtwrap/template_instantiator/classes.py index ce51d5b967..7026546785 100644 --- a/wrap/gtwrap/template_instantiator/classes.py +++ b/wrap/gtwrap/template_instantiator/classes.py @@ -57,6 +57,8 @@ def __init__(self, original: parser.Class, instantiations=(), new_name=''): # Instantiate all instance methods self.methods = self.instantiate_methods(typenames) + + self.dunder_methods = original.dunder_methods super().__init__( self.template, @@ -66,6 +68,7 @@ def __init__(self, original: parser.Class, instantiations=(), new_name=''): self.ctors, self.methods, self.static_methods, + self.dunder_methods, self.properties, self.operators, self.enums, diff --git a/wrap/requirements.txt b/wrap/requirements.txt index 0aac9302e5..f43fdda617 100644 --- a/wrap/requirements.txt +++ b/wrap/requirements.txt @@ -1,2 +1,2 @@ -pyparsing==2.4.7 +pyparsing==3.1.1 pytest>=6.2.4 diff --git a/wrap/tests/expected/matlab/FastSet.m b/wrap/tests/expected/matlab/FastSet.m new file mode 100644 index 0000000000..4d2a1813e8 --- /dev/null +++ b/wrap/tests/expected/matlab/FastSet.m @@ -0,0 +1,36 @@ +%class FastSet, see Doxygen page for details +%at https://gtsam.org/doxygen/ +% +%-------Constructors------- +%FastSet() +% +classdef FastSet < handle + properties + ptr_FastSet = 0 + end + methods + function obj = FastSet(varargin) + if nargin == 2 && isa(varargin{1}, 'uint64') && varargin{1} == uint64(5139824614673773682) + my_ptr = varargin{2}; + class_wrapper(73, my_ptr); + elseif nargin == 0 + my_ptr = class_wrapper(74); + else + error('Arguments do not match any overload of FastSet constructor'); + end + obj.ptr_FastSet = my_ptr; + end + + function delete(obj) + class_wrapper(75, obj.ptr_FastSet); + end + + function display(obj), obj.print(''); end + %DISPLAY Calls print on the object + function disp(obj), obj.display; end + %DISP Calls print on the object + end + + methods(Static = true) + end +end diff --git a/wrap/tests/expected/matlab/MyFactorPosePoint2.m b/wrap/tests/expected/matlab/MyFactorPosePoint2.m index 4a30bd4894..ac5b134f9b 100644 --- a/wrap/tests/expected/matlab/MyFactorPosePoint2.m +++ b/wrap/tests/expected/matlab/MyFactorPosePoint2.m @@ -15,9 +15,9 @@ function obj = MyFactorPosePoint2(varargin) if nargin == 2 && isa(varargin{1}, 'uint64') && varargin{1} == uint64(5139824614673773682) my_ptr = varargin{2}; - class_wrapper(73, my_ptr); + class_wrapper(76, my_ptr); elseif nargin == 4 && isa(varargin{1},'numeric') && isa(varargin{2},'numeric') && isa(varargin{3},'double') && isa(varargin{4},'gtsam.noiseModel.Base') - my_ptr = class_wrapper(74, varargin{1}, varargin{2}, varargin{3}, varargin{4}); + my_ptr = class_wrapper(77, varargin{1}, varargin{2}, varargin{3}, varargin{4}); else error('Arguments do not match any overload of MyFactorPosePoint2 constructor'); end @@ -25,7 +25,7 @@ end function delete(obj) - class_wrapper(75, obj.ptr_MyFactorPosePoint2); + class_wrapper(78, obj.ptr_MyFactorPosePoint2); end function display(obj), obj.print(''); end @@ -36,19 +36,19 @@ function delete(obj) % PRINT usage: print(string s, KeyFormatter keyFormatter) : returns void % Doxygen can be found at https://gtsam.org/doxygen/ if length(varargin) == 2 && isa(varargin{1},'char') && isa(varargin{2},'gtsam.KeyFormatter') - class_wrapper(76, this, varargin{:}); + class_wrapper(79, this, varargin{:}); return end % PRINT usage: print(string s) : returns void % Doxygen can be found at https://gtsam.org/doxygen/ if length(varargin) == 1 && isa(varargin{1},'char') - class_wrapper(77, this, varargin{:}); + class_wrapper(80, this, varargin{:}); return end % PRINT usage: print() : returns void % Doxygen can be found at https://gtsam.org/doxygen/ if length(varargin) == 0 - class_wrapper(78, this, varargin{:}); + class_wrapper(81, this, varargin{:}); return end error('Arguments do not match any overload of function MyFactorPosePoint2.print'); diff --git a/wrap/tests/expected/matlab/class_wrapper.cpp b/wrap/tests/expected/matlab/class_wrapper.cpp index c4be52018c..e33f14238d 100644 --- a/wrap/tests/expected/matlab/class_wrapper.cpp +++ b/wrap/tests/expected/matlab/class_wrapper.cpp @@ -31,6 +31,8 @@ typedef std::set*> Collector_ForwardKinematic static Collector_ForwardKinematics collector_ForwardKinematics; typedef std::set*> Collector_TemplatedConstructor; static Collector_TemplatedConstructor collector_TemplatedConstructor; +typedef std::set*> Collector_FastSet; +static Collector_FastSet collector_FastSet; typedef std::set*> Collector_MyFactorPosePoint2; static Collector_MyFactorPosePoint2 collector_MyFactorPosePoint2; @@ -101,6 +103,12 @@ void _deleteAllObjects() collector_TemplatedConstructor.erase(iter++); anyDeleted = true; } } + { for(Collector_FastSet::iterator iter = collector_FastSet.begin(); + iter != collector_FastSet.end(); ) { + delete *iter; + collector_FastSet.erase(iter++); + anyDeleted = true; + } } { for(Collector_MyFactorPosePoint2::iterator iter = collector_MyFactorPosePoint2.begin(); iter != collector_MyFactorPosePoint2.end(); ) { delete *iter; @@ -844,7 +852,40 @@ void TemplatedConstructor_deconstructor_72(int nargout, mxArray *out[], int narg delete self; } -void MyFactorPosePoint2_collectorInsertAndMakeBase_73(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +void FastSet_collectorInsertAndMakeBase_73(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +{ + mexAtExit(&_deleteAllObjects); + typedef std::shared_ptr Shared; + + Shared *self = *reinterpret_cast (mxGetData(in[0])); + collector_FastSet.insert(self); +} + +void FastSet_constructor_74(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +{ + mexAtExit(&_deleteAllObjects); + typedef std::shared_ptr Shared; + + Shared *self = new Shared(new FastSet()); + collector_FastSet.insert(self); + out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL); + *reinterpret_cast (mxGetData(out[0])) = self; +} + +void FastSet_deconstructor_75(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +{ + typedef std::shared_ptr Shared; + checkArguments("delete_FastSet",nargout,nargin,1); + Shared *self = *reinterpret_cast(mxGetData(in[0])); + Collector_FastSet::iterator item; + item = collector_FastSet.find(self); + if(item != collector_FastSet.end()) { + collector_FastSet.erase(item); + } + delete self; +} + +void MyFactorPosePoint2_collectorInsertAndMakeBase_76(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { mexAtExit(&_deleteAllObjects); typedef std::shared_ptr> Shared; @@ -853,7 +894,7 @@ void MyFactorPosePoint2_collectorInsertAndMakeBase_73(int nargout, mxArray *out[ collector_MyFactorPosePoint2.insert(self); } -void MyFactorPosePoint2_constructor_74(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +void MyFactorPosePoint2_constructor_77(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { mexAtExit(&_deleteAllObjects); typedef std::shared_ptr> Shared; @@ -868,7 +909,7 @@ void MyFactorPosePoint2_constructor_74(int nargout, mxArray *out[], int nargin, *reinterpret_cast (mxGetData(out[0])) = self; } -void MyFactorPosePoint2_deconstructor_75(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +void MyFactorPosePoint2_deconstructor_78(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { typedef std::shared_ptr> Shared; checkArguments("delete_MyFactorPosePoint2",nargout,nargin,1); @@ -881,7 +922,7 @@ void MyFactorPosePoint2_deconstructor_75(int nargout, mxArray *out[], int nargin delete self; } -void MyFactorPosePoint2_print_76(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +void MyFactorPosePoint2_print_79(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { checkArguments("print",nargout,nargin-1,2); auto obj = unwrap_shared_ptr>(in[0], "ptr_MyFactorPosePoint2"); @@ -890,7 +931,7 @@ void MyFactorPosePoint2_print_76(int nargout, mxArray *out[], int nargin, const obj->print(s,keyFormatter); } -void MyFactorPosePoint2_print_77(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +void MyFactorPosePoint2_print_80(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { checkArguments("print",nargout,nargin-1,1); auto obj = unwrap_shared_ptr>(in[0], "ptr_MyFactorPosePoint2"); @@ -898,7 +939,7 @@ void MyFactorPosePoint2_print_77(int nargout, mxArray *out[], int nargin, const obj->print(s,gtsam::DefaultKeyFormatter); } -void MyFactorPosePoint2_print_78(int nargout, mxArray *out[], int nargin, const mxArray *in[]) +void MyFactorPosePoint2_print_81(int nargout, mxArray *out[], int nargin, const mxArray *in[]) { checkArguments("print",nargout,nargin-1,0); auto obj = unwrap_shared_ptr>(in[0], "ptr_MyFactorPosePoint2"); @@ -1137,22 +1178,31 @@ void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[]) TemplatedConstructor_deconstructor_72(nargout, out, nargin-1, in+1); break; case 73: - MyFactorPosePoint2_collectorInsertAndMakeBase_73(nargout, out, nargin-1, in+1); + FastSet_collectorInsertAndMakeBase_73(nargout, out, nargin-1, in+1); break; case 74: - MyFactorPosePoint2_constructor_74(nargout, out, nargin-1, in+1); + FastSet_constructor_74(nargout, out, nargin-1, in+1); break; case 75: - MyFactorPosePoint2_deconstructor_75(nargout, out, nargin-1, in+1); + FastSet_deconstructor_75(nargout, out, nargin-1, in+1); break; case 76: - MyFactorPosePoint2_print_76(nargout, out, nargin-1, in+1); + MyFactorPosePoint2_collectorInsertAndMakeBase_76(nargout, out, nargin-1, in+1); break; case 77: - MyFactorPosePoint2_print_77(nargout, out, nargin-1, in+1); + MyFactorPosePoint2_constructor_77(nargout, out, nargin-1, in+1); break; case 78: - MyFactorPosePoint2_print_78(nargout, out, nargin-1, in+1); + MyFactorPosePoint2_deconstructor_78(nargout, out, nargin-1, in+1); + break; + case 79: + MyFactorPosePoint2_print_79(nargout, out, nargin-1, in+1); + break; + case 80: + MyFactorPosePoint2_print_80(nargout, out, nargin-1, in+1); + break; + case 81: + MyFactorPosePoint2_print_81(nargout, out, nargin-1, in+1); break; } } catch(const std::exception& e) { diff --git a/wrap/tests/expected/python/class_pybind.cpp b/wrap/tests/expected/python/class_pybind.cpp index 86d69c2e0e..2292f46be0 100644 --- a/wrap/tests/expected/python/class_pybind.cpp +++ b/wrap/tests/expected/python/class_pybind.cpp @@ -91,6 +91,12 @@ PYBIND11_MODULE(class_py, m_) { .def(py::init(), py::arg("arg")) .def(py::init(), py::arg("arg")); + py::class_>(m_, "FastSet") + .def(py::init<>()) + .def("__len__",[](FastSet* self){return std::distance(self->begin(), self->end());}) + .def("__contains__",[](FastSet* self, size_t key){return std::find(self->begin(), self->end(), key) != self->end();}, py::arg("key")) + .def("__iter__",[](FastSet* self){return py::make_iterator(self->begin(), self->end());}); + py::class_, std::shared_ptr>>(m_, "MyFactorPosePoint2") .def(py::init>(), py::arg("key1"), py::arg("key2"), py::arg("measured"), py::arg("noiseModel")) .def("print",[](MyFactor* self, const string& s, const gtsam::KeyFormatter& keyFormatter){ py::scoped_ostream_redirect output; self->print(s, keyFormatter);}, py::arg("s") = "factor: ", py::arg("keyFormatter") = gtsam::DefaultKeyFormatter) diff --git a/wrap/tests/fixtures/class.i b/wrap/tests/fixtures/class.i index 766f55329a..775bbc737c 100644 --- a/wrap/tests/fixtures/class.i +++ b/wrap/tests/fixtures/class.i @@ -145,3 +145,12 @@ class TemplatedConstructor { class SuperCoolFactor; typedef SuperCoolFactor SuperCoolFactorPose3; + +/// @brief class with dunder methods for container behavior +class FastSet { + FastSet(); + + __len__(); + __contains__(size_t key); + __iter__(); +}; \ No newline at end of file diff --git a/wrap/tests/test_interface_parser.py b/wrap/tests/test_interface_parser.py index 45415995fe..2a923b3c5f 100644 --- a/wrap/tests/test_interface_parser.py +++ b/wrap/tests/test_interface_parser.py @@ -18,11 +18,12 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from gtwrap.interface_parser import (ArgumentList, Class, Constructor, Enum, - Enumerator, ForwardDeclaration, - GlobalFunction, Include, Method, Module, - Namespace, Operator, ReturnType, - StaticMethod, TemplatedType, Type, +from gtwrap.interface_parser import (ArgumentList, Class, Constructor, + DunderMethod, Enum, Enumerator, + ForwardDeclaration, GlobalFunction, + Include, Method, Module, Namespace, + Operator, ReturnType, StaticMethod, + TemplatedType, Type, TypedefTemplateInstantiation, Typename, Variable) from gtwrap.template_instantiator.classes import InstantiatedClass @@ -344,6 +345,17 @@ def test_constructor_templated(self): self.assertEqual(1, len(ret.args)) self.assertEqual("const T & name", ret.args.args_list[0].to_cpp()) + def test_dunder_method(self): + """Test for special python dunder methods.""" + iter_string = "__iter__();" + ret = DunderMethod.rule.parse_string(iter_string)[0] + self.assertEqual("iter", ret.name) + + contains_string = "__contains__(size_t key);" + ret = DunderMethod.rule.parse_string(contains_string)[0] + self.assertEqual("contains", ret.name) + self.assertTrue(len(ret.args) == 1) + def test_operator_overload(self): """Test for operator overloading.""" # Unary operator