diff --git a/.github/workflows/validate-build.yml b/.github/workflows/validate-build.yml index bf54cbb..aa31738 100644 --- a/.github/workflows/validate-build.yml +++ b/.github/workflows/validate-build.yml @@ -5,7 +5,7 @@ name: Compile and test C++ on: push: - branches: [ master ] + branches: [ main ] pull_request: types: [edited, submitted] @@ -17,16 +17,16 @@ jobs: matrix: #os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] os: [ubuntu-20.04, ubuntu-latest] - python-version: ["3.10"] + use_boost: ["false", "true"] steps: - name: Fetch sources uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python 3.10 uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} + python-version: "3.10" cache: pip - name: Install gcc toolchain @@ -48,7 +48,10 @@ jobs: path: 3rdparty/packagecache/ - name: Resolve C++ build dependencies (non-Windows) - run: meson setup build/ + run: >- + meson setup + -Duse_boost=${{ matrix.use_boost }} + build/ if: startsWith(matrix.os, 'window') == false #- name: Resolve C++ build dependencies (msvc toolchain) diff --git a/3rdparty/armadillo-code.wrap b/3rdparty/armadillo-code.wrap index 32adc01..0116f0d 100644 --- a/3rdparty/armadillo-code.wrap +++ b/3rdparty/armadillo-code.wrap @@ -4,7 +4,7 @@ revision = 14.0.2 depth = 1 patch_directory = armadillo-code -diff_files = armadillo-code/0001-Patch-armdillo-to-support-besselj.patch +diff_files = 0001-Patch-armadillo-to-support-besselj.patch [provide] -armadillo-code = armadillo_dep \ No newline at end of file +armadillo-code = armadillo_dep diff --git a/3rdparty/boost-math.wrap b/3rdparty/boost-math.wrap new file mode 100644 index 0000000..c428b57 --- /dev/null +++ b/3rdparty/boost-math.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = math-boost-1.83.0 +source_url = https://github.com/boostorg/math/archive/boost-1.83.0.tar.gz +source_filename = boost-1.83.0.tar.gz +source_hash = 53e5f7539a66899fe0fca3080405cbd5f7959da5394ec13664746741aece1705 + +patch_directory = boost-math + +[provide] +boost_math = boost_math_dep \ No newline at end of file diff --git a/3rdparty/packagefiles/0001-Patch-armadillo-to-support-besselj.patch b/3rdparty/packagefiles/0001-Patch-armadillo-to-support-besselj.patch new file mode 100644 index 0000000..4ccfe6c --- /dev/null +++ b/3rdparty/packagefiles/0001-Patch-armadillo-to-support-besselj.patch @@ -0,0 +1,50 @@ +From de3cda8744e89b65b186b721853a611eb8d505cd Mon Sep 17 00:00:00 2001 +From: Antony Chan +Date: Tue, 26 Nov 2024 17:23:16 -0800 +Subject: [PATCH] Patch armadillo to support besselj + +--- + include/armadillo_bits/eop_aux.hpp | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/include/armadillo_bits/eop_aux.hpp b/include/armadillo_bits/eop_aux.hpp +index b63eba6..8da912b 100644 +--- a/include/armadillo_bits/eop_aux.hpp ++++ b/include/armadillo_bits/eop_aux.hpp +@@ -20,6 +20,9 @@ + //! @{ + + ++#ifdef BOOST_HAS_BESSEL ++#include ++#endif + + //! use of the SFINAE approach to work around compiler limitations + //! http://en.wikipedia.org/wiki/SFINAE +@@ -138,8 +141,21 @@ class eop_aux + + template arma_inline static typename arma_integral_only::result pow (const T1 base, const T2 exponent) { return T1( std::pow( double(base), double(exponent) ) ); } + template arma_inline static typename arma_real_or_cx_only::result pow (const T1 base, const T2 exponent) { return T1( std::pow( base, exponent ) ); } +- +- ++ ++// Wrap the scalar Bessel function of the first kind. Enable 32-bit and 64-bit ++// algorithm. ++template ++arma_inline static typename arma_real_only::result ++besselj(const eT x) { ++#ifdef STD_HAS_BESSEL ++ using std::cyl_bessel_j; ++#elif defined(BOOST_HAS_BESSEL) ++ using boost::math::cyl_bessel_j; ++#endif ++ return cyl_bessel_j(double(order), x); ++} ++ ++ + template + arma_inline + static +-- +2.25.1 + diff --git a/3rdparty/packagefiles/armadillo-code/0001-Patch-armdillo-to-support-besselj.patch b/3rdparty/packagefiles/armadillo-code/0001-Patch-armdillo-to-support-besselj.patch deleted file mode 100644 index 0f873ca..0000000 --- a/3rdparty/packagefiles/armadillo-code/0001-Patch-armdillo-to-support-besselj.patch +++ /dev/null @@ -1,118 +0,0 @@ -From bbc3d52ee9d06a8b8d8a51ba425fa03d249a5dfd Mon Sep 17 00:00:00 2001 -From: Antony Chan -Date: Thu, 7 Nov 2024 10:46:43 -0800 -Subject: [PATCH] Patch armdillo to support besselj - ---- - include/armadillo_bits/eop_aux.hpp | 46 ++++++++++++++++++------------ - 1 file changed, 27 insertions(+), 19 deletions(-) - -diff --git a/include/armadillo_bits/eop_aux.hpp b/include/armadillo_bits/eop_aux.hpp -index b63eba6..6128895 100644 ---- a/include/armadillo_bits/eop_aux.hpp -+++ b/include/armadillo_bits/eop_aux.hpp -@@ -72,74 +72,82 @@ class eop_aux - template arma_inline static typename arma_real_or_cx_only::result cosh (const eT x) { return std::cosh (x); } - template arma_inline static typename arma_real_or_cx_only::result sinh (const eT x) { return std::sinh (x); } - template arma_inline static typename arma_real_or_cx_only::result tanh (const eT x) { return std::tanh (x); } -- -+ - template arma_inline static typename arma_unsigned_integral_only::result neg (const eT x) { return static_cast(-1 * x); } // TODO: not sure how to best handle this - template arma_inline static typename arma_signed_only::result neg (const eT x) { return -x; } -- -+ - template arma_inline static typename arma_integral_only::result floor (const eT x) { return x; } - template arma_inline static typename arma_real_only::result floor (const eT x) { return std::floor(x); } - template arma_inline static typename arma_cx_only::result floor (const eT& x) { return eT( std::floor(x.real()), std::floor(x.imag()) ); } -- -+ - template arma_inline static typename arma_integral_only::result ceil (const eT x) { return x; } - template arma_inline static typename arma_real_only::result ceil (const eT x) { return std::ceil(x); } - template arma_inline static typename arma_cx_only::result ceil (const eT& x) { return eT( std::ceil(x.real()), std::ceil(x.imag()) ); } -- -+ - template arma_inline static typename arma_integral_only::result round (const eT x) { return x; } - template arma_inline static typename arma_real_only::result round (const eT x) { return std::round(x); } - template arma_inline static typename arma_cx_only::result round (const eT& x) { return eT( std::round(x.real()), std::round(x.imag()) ); } -- -+ - template arma_inline static typename arma_integral_only::result trunc (const eT x) { return x; } - template arma_inline static typename arma_real_only::result trunc (const eT x) { return std::trunc(x); } - template arma_inline static typename arma_cx_only::result trunc (const eT& x) { return eT( std::trunc(x.real()), std::trunc(x.imag()) ); } -- -+ - template arma_inline static typename arma_integral_only::result log2 (const eT x) { return eT( std::log2(double(x)) ); } - template arma_inline static typename arma_real_only::result log2 (const eT x) { return std::log2(x); } - template arma_inline static typename arma_cx_only::result log2 (const eT& x) { typedef typename get_pod_type::result T; return std::log(x) / T(0.69314718055994530942); } -- -+ - template arma_inline static typename arma_integral_only::result log1p (const eT x) { return eT( std::log1p(double(x)) ); } - template arma_inline static typename arma_real_only::result log1p (const eT x) { return std::log1p(x); } - template arma_inline static typename arma_cx_only::result log1p (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_integral_only::result exp2 (const eT x) { return eT( std::exp2(double(x)) ); } - template arma_inline static typename arma_real_only::result exp2 (const eT x) { return std::exp2(x); } - template arma_inline static typename arma_cx_only::result exp2 (const eT& x) { typedef typename get_pod_type::result T; return std::pow( T(2), x); } -- -+ - template arma_inline static typename arma_integral_only::result exp10 (const eT x) { return eT( std::pow(double(10), double(x)) ); } - template arma_inline static typename arma_real_or_cx_only::result exp10 (const eT x) { typedef typename get_pod_type::result T; return std::pow( T(10), x); } -- -+ - template arma_inline static typename arma_integral_only::result expm1 (const eT x) { return eT( std::expm1(double(x)) ); } - template arma_inline static typename arma_real_only::result expm1 (const eT x) { return std::expm1(x); } - template arma_inline static typename arma_cx_only::result expm1 (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_unsigned_integral_only::result arma_abs (const eT x) { return x; } - template arma_inline static typename arma_signed_integral_only::result arma_abs (const eT x) { return std::abs(x); } - template arma_inline static typename arma_real_only::result arma_abs (const eT x) { return std::abs(x); } - template arma_inline static typename arma_real_only< T>::result arma_abs (const std::complex& x) { return std::abs(x); } -- -+ - template arma_inline static typename arma_integral_only::result cbrt (const eT x) { return eT( std::cbrt(double(x)) ); } - template arma_inline static typename arma_real_only::result cbrt (const eT x) { return std::cbrt(x); } - template arma_inline static typename arma_cx_only::result cbrt (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_integral_only::result erf (const eT x) { return eT( std::erf(double(x)) ); } - template arma_inline static typename arma_real_only::result erf (const eT x) { return std::erf(x); } - template arma_inline static typename arma_cx_only::result erf (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_integral_only::result erfc (const eT x) { return eT( std::erfc(double(x)) ); } - template arma_inline static typename arma_real_only::result erfc (const eT x) { return std::erfc(x); } - template arma_inline static typename arma_cx_only::result erfc (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_integral_only::result lgamma (const eT x) { return eT( std::lgamma(double(x)) ); } - template arma_inline static typename arma_real_only::result lgamma (const eT x) { return std::lgamma(x); } - template arma_inline static typename arma_cx_only::result lgamma (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_integral_only::result tgamma (const eT x) { return eT( std::tgamma(double(x)) ); } - template arma_inline static typename arma_real_only::result tgamma (const eT x) { return std::tgamma(x); } - template arma_inline static typename arma_cx_only::result tgamma (const eT& x) { arma_ignore(x); return eT(0); } -- -+ - template arma_inline static typename arma_integral_only::result pow (const T1 base, const T2 exponent) { return T1( std::pow( double(base), double(exponent) ) ); } - template arma_inline static typename arma_real_or_cx_only::result pow (const T1 base, const T2 exponent) { return T1( std::pow( base, exponent ) ); } -- -- -+ -+// Wrap the scalar Bessel function of the first kind. Enable 32-bit and 64-bit -+// algorithm. -+template -+arma_inline static typename arma_real_only::result -+besselj(const eT x) { -+ return std::cyl_bessel_j(double(order), x); -+} -+ -+ - template - arma_inline - static --- -2.25.1 - diff --git a/3rdparty/packagefiles/boost-math/meson.build b/3rdparty/packagefiles/boost-math/meson.build new file mode 100644 index 0000000..a5d8bda --- /dev/null +++ b/3rdparty/packagefiles/boost-math/meson.build @@ -0,0 +1,10 @@ +project('boost-math', 'cpp', + version: '1.83.0', +) + +boost_math_inc = include_directories('include') + +boost_math_dep = declare_dependency( + include_directories: boost_math_inc, + compile_args: ['-DBOOST_MATH_STANDALONE'], +) \ No newline at end of file diff --git a/besselj_armadillo_support/inc/armadillo_besselj_support.hpp b/besselj_armadillo_support/inc/armadillo_besselj_support.hpp index c047fc9..7109683 100644 --- a/besselj_armadillo_support/inc/armadillo_besselj_support.hpp +++ b/besselj_armadillo_support/inc/armadillo_besselj_support.hpp @@ -6,6 +6,10 @@ #include #include +#ifdef BOOST_HAS_BESSEL +#include +#endif + #include namespace arma { diff --git a/besselj_armadillo_support/meson.build b/besselj_armadillo_support/meson.build index a18207d..e72a0cc 100644 --- a/besselj_armadillo_support/meson.build +++ b/besselj_armadillo_support/meson.build @@ -1,33 +1,69 @@ armadillo_besselj_support_inc = include_directories('inc') -armadillo_dep = dependency('armadillo-code') +armadillo_dep = subproject('armadillo-code').get_variable('armadillo_dep') # TODO(Antony): use "cxx.has_function" when it supports std namespace. -if not cxx.compiles( - '''#include +if get_option('use_boost') + warning('Fallback to Boost.Math for the Bessel function implementation.') + bessel_source = 'BOOST_HAS_BESSEL' + boost_math_dep = dependency('boost', modules: ['math'], fallback: [ + 'boost-math', 'boost_math_dep', + ]) +elif cxx.compiles( + '''#include int main() { return int(std::cyl_bessel_j(0.0, 0.0)); } ''') + bessel_source = 'STD_HAS_BESSEL' + boost_math_dep = [] +else error('Bessel function (of the first kind) does not exist in the C++ toolchain. Is this ISO C++17 compilant?') endif test_besselj_support_exe = executable('test_besselj_support', sources: [ 'tests/test-besselj.cpp', + #'tests/test-armadillo-support.cpp', + ], + cpp_args: [ + '-D' + bessel_source, + ], + dependencies: [ + catch2_dep, + boost_math_dep, + ], +) + +test_besselj_wrapper_exe = executable('test_besselj_armadillo_wrapper', + sources: [ 'tests/test-armadillo-support.cpp', ], - include_directories: armadillo_besselj_support_inc, + cpp_args: [ + '-D' + bessel_source, + ], + include_directories: [ + armadillo_besselj_support_inc, + ], dependencies: [ catch2_dep, armadillo_dep, + boost_math_dep, ], ) -test('Besselj wrapper', +test('Besselj native support', test_besselj_support_exe, args: [ '-r', 'tap', ], protocol: 'tap', +) + +test('Besselj Armadillo wrapper', + test_besselj_wrapper_exe, + args: [ + '-r', 'tap', + ], + protocol: 'tap', ) \ No newline at end of file diff --git a/besselj_armadillo_support/tests/test-armadillo-support.cpp b/besselj_armadillo_support/tests/test-armadillo-support.cpp index e4e2028..90938d0 100644 --- a/besselj_armadillo_support/tests/test-armadillo-support.cpp +++ b/besselj_armadillo_support/tests/test-armadillo-support.cpp @@ -1,5 +1,7 @@ -#include #include + +// +#include #include #include diff --git a/besselj_armadillo_support/tests/test-besselj.cpp b/besselj_armadillo_support/tests/test-besselj.cpp index f7584c6..6800524 100644 --- a/besselj_armadillo_support/tests/test-besselj.cpp +++ b/besselj_armadillo_support/tests/test-besselj.cpp @@ -3,15 +3,19 @@ #include #include +#ifdef STD_HAS_BESSEL +using std::cyl_bessel_j; +#elif defined(BOOST_HAS_BESSEL) +#include +using boost::math::cyl_bessel_j; +#endif TEST_CASE("Toolchain has std::cyl_bessel_j") { - using std::cyl_bessel_j; using std::isnan; REQUIRE(!isnan(cyl_bessel_j(0.0, 0.0))); } TEST_CASE("Besselj spot check") { - using std::cyl_bessel_j; using Catch::Matchers::WithinAbs; using Catch::Matchers::WithinRel; diff --git a/examples/generate-psf.cpp b/examples/generate-psf.cpp index 77846c4..c697dcd 100644 --- a/examples/generate-psf.cpp +++ b/examples/generate-psf.cpp @@ -23,7 +23,7 @@ int main() { const auto psf = makePSF(params, {0.1_um, 0.25_um}, {256, 128}, 0.610_um, precision); -#ifdef ARMA_USE_HDF5 +#ifdef ARMA_USE_HDF5 std::cout << "Saving volume to HDF5...\n"; psf.save(hdf5_name("psf.h5", "psf", hdf5_opts::trans)); std::cout << R"(Done. @@ -52,4 +52,4 @@ Import to Python: std::cout << "PSF XZ plane saved to 'psf_xz.pgm'.\n"; } return 0; -} \ No newline at end of file +} diff --git a/meson.options b/meson.options index 99e00db..e944008 100644 --- a/meson.options +++ b/meson.options @@ -1 +1,2 @@ -option('install_examples', type: 'boolean', value: false) \ No newline at end of file +option('install_examples', type: 'boolean', value: false) +option('use_boost', type: 'boolean', value: false) diff --git a/microsc-psf/meson.build b/microsc-psf/meson.build index 31fa950..afcf89c 100644 --- a/microsc-psf/meson.build +++ b/microsc-psf/meson.build @@ -3,12 +3,16 @@ microsc_psf_lib = static_library('microsc-psf', sources: [ 'src/main.cpp', ], + cpp_args: [ + '-D' + bessel_source, + ], include_directories: [ microsc_psf_inc, armadillo_besselj_support_inc, ], dependencies: [ armadillo_dep, + boost_math_dep, ], ) diff --git a/microsc-psf/src/main.cpp b/microsc-psf/src/main.cpp index 9387cbf..967fdde 100644 --- a/microsc-psf/src/main.cpp +++ b/microsc-psf/src/main.cpp @@ -1,9 +1,9 @@ +#include #include +// This must come after #include "make_psf.h" -// This must come after header file . -#include namespace { // Return the range [0, N), excluding N.