diff --git a/CMakeLists.txt b/CMakeLists.txt index f65251466..4399a342f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,9 @@ # To use a specific compiler: #set(CMAKE_C_COMPILER "gcc-7") #set(CMAKE_CXX_COMPILER "/usr/bin/g++-7") - + #set(CMAKE_C_COMPILER "clang-18") + #set(CMAKE_CXX_COMPILER "clang++-18") + project(codac VERSION ${VERSION} LANGUAGES CXX) if(NOT VERSION_ID) @@ -81,8 +83,79 @@ if(MSVC) add_compile_options(/W4) else() - add_compile_options(-Wall -Wextra -Wpedantic) + add_compile_options(-Wall -Wextra -Wpedantic ) + endif() + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(-Wreturn-type -Wno-unused-parameter) + add_compile_options( -fno-omit-frame-pointer -g -O0 ) + add_compile_options( -fsanitize=address -fsanitize=undefined) + add_link_options(-fsanitize=address,undefined) endif() + + + + # FMA (fused multiply-add) is only ever activated here through an explicit + # compiler flag, and only when check_cxx_compiler_flag() confirms it is + # actually recognized. This matters because "the FMA flag" is not a + # portable concept: + # - x86/x86_64 (GCC/Clang): -mfma enables the FMA3 ISA extension. + # - x86/x64 (MSVC): there is no dedicated FMA flag; /arch:AVX2 implies it. + # - AArch64 (ARMv8-A, e.g. regular Raspberry Pi OS 64-bit): FMA + # (FMADD/FMLA) is part of the base ISA, scalar and NEON alike, so no + # flag exists or is needed; -mfma is simply unknown to the compiler + # there, the check below fails, and the flag is skipped (the compiler + # still emits FMA instructions on its own when it can). + # - ARMv7-A / 32-bit ARMv8-A (e.g. a Raspberry Pi 2 in AArch32, or a + # Raspberry Pi 3/4 running a 32-bit OS: the silicon is ARMv8-A but the + # 32-bit userland reports itself as armv7l or armv8l): FMA hardware is + # optional on ARMv7-A but mandatory on ARMv8-A even in its 32-bit + # execution state, and in both cases it is exposed the same way, via + # the NEON-VFPv4 FPU extension, activated by + # -mfpu=neon-vfpv4 -mfloat-abi=hard (plus -ffp-contract=fast, since + # C++ does not permit fusing a*b+c into FMA by default). -mfma itself + # is not a recognized flag on any 32-bit ARM target, so this is + # handled in its own branch below rather than falling through to the + # generic GCC/Clang case. + # - ARMv6 hard-float (e.g. Raspbian Bullseye armv6hf, Raspberry Pi + # Zero/1): the FPU predates fused multiply-add support (FMA was only + # introduced with VFPv4/ARMv7-A), so there is no equivalent flag to + # fall back to either; the check fails and we correctly get plain + # (unfused) multiply+add on that target. + # In all unsupported cases the check below simply leaves the flag out, + # which is the correct behavior rather than a build failure. + # CODAC_ARCH_CXX_FLAGS collects whichever of the flags below actually got + # applied, so that it can be re-exported as part of CODAC_CXX_FLAGS in the + # generated codac-config.cmake (see src/CMakeLists.txt). Without this, + # downstream consumers (examples, user projects built via find_package(CODAC)) + # compile Eigen's header-only templates with different alignment/instruction-set + # assumptions than the ones baked into the prebuilt static libraries they link + # against -- an ODR/ABI mismatch that Eigen documents as unsafe, and that in + # practice corrupts the heap (mismatched aligned_malloc/aligned_free paths). + set(CODAC_ARCH_CXX_FLAGS "") + + include(CheckCXXCompilerFlag) + if(MSVC) + check_cxx_compiler_flag("/arch:AVX2" COMPILER_SUPPORTS_FMA) + if(COMPILER_SUPPORTS_FMA) + add_compile_options(/arch:AVX2) + set(CODAC_ARCH_CXX_FLAGS "/arch:AVX2") + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang" AND CMAKE_SYSTEM_PROCESSOR MATCHES "^armv[7-9]") + check_cxx_compiler_flag("-mfpu=neon-vfpv4 -mfloat-abi=hard" COMPILER_SUPPORTS_FMA) + if(COMPILER_SUPPORTS_FMA) + add_compile_options(-mfpu=neon-vfpv4 -mfloat-abi=hard -ffp-contract=fast) + set(CODAC_ARCH_CXX_FLAGS "-mfpu=neon-vfpv4 -mfloat-abi=hard -ffp-contract=fast") + endif() + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + check_cxx_compiler_flag("-mfma" COMPILER_SUPPORTS_FMA) + if(COMPILER_SUPPORTS_FMA) + add_compile_options(-mfma ) + set(CODAC_ARCH_CXX_FLAGS "-mfma") + endif() + endif() + + # Temporary attempts to fix errors similar to: # _ number of sections exceeded object file format limit. diff --git a/examples/ellipsoid_example/CMakeLists.txt b/examples/ellipsoid_example/CMakeLists.txt index ce82f2152..cd4a9adaf 100644 --- a/examples/ellipsoid_example/CMakeLists.txt +++ b/examples/ellipsoid_example/CMakeLists.txt @@ -20,25 +20,27 @@ # Adding Eigen3 - # In case you installed Eigen3 in a local directory, you need - # to specify its path with the CMAKE_PREFIX_PATH option, e.g. - # set(CMAKE_PREFIX_PATH "~/eigen/build_install") - - find_package(Eigen3 3.4 REQUIRED NO_MODULE) - message(STATUS "Found Eigen3 version ${Eigen3_VERSION}") + # Codac builds and installs its own Eigen3 (see CODAC_INCLUDE_DIRS below), + # so no separate find_package(Eigen3) is needed -- and none would find it, + # since that build isn't registered as a system-discoverable package. + # Searching for an unrelated system Eigen3 here would risk pulling in a + # different version than the one codac-core/codac-graphics were compiled + # against, which is exactly the kind of ABI mismatch that broke examples + # like 01_batman (see the -mfma / CODAC_CXX_FLAGS fix above in this history). # Adding Codac - # In case you installed Codac in a local directory, you need + # In case you installed Codac in a local directory, you need # to specify its path with the CMAKE_PREFIX_PATH option. - set(CMAKE_PREFIX_PATH "~/Documents/Code_these/codac/build_install") + # set(CMAKE_PREFIX_PATH "~/codac/build_install") find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") + # Compilation add_executable(${PROJECT_NAME} main.cpp) target_compile_options(${PROJECT_NAME} PUBLIC ${CODAC_CXX_FLAGS}) target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CODAC_INCLUDE_DIRS}) - target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex Eigen3::Eigen) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES} Ibex::ibex) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index bab5e84ba..845097694 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -17,7 +17,9 @@ # Adds pybind11::headers, pybind11::module, pybind11::embed set(PYTHON_PACKAGE_NAME ${PROJECT_NAME}) - set(PYTHON_PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/python_package") + set(PYTHON_PACKAGE_DIR "${CMAKE_CURRENT_BINARY_DIR}/python_package" + CACHE INTERNAL "Codac Python package build directory" + ) file(MAKE_DIRECTORY ${PYTHON_PACKAGE_DIR}) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/${PYTHON_PACKAGE_NAME}/" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}") diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index 9d83a210d..d6348a9cd 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -160,6 +160,24 @@ PRIVATE ${PROJECT_NAME}-core ${PROJECT_NAME}-sympy ${LIBS} Ibex::ibex ) + # -------------------------------------------------------------- + # Sanitizer runtime as a shared library (Debug builds only) + # -------------------------------------------------------------- + # + # _core.so is loaded via dlopen() by an unsanitized python + # interpreter. By default Clang/GCC embed the ASan/UBSan runtime + # statically into shared libraries, which leaves symbols like + # __ubsan_vptr_type_cache or __asan_* unresolved at dlopen() time + # (python has no sanitizer runtime of its own to provide them). + # -shared-libsan makes _core.so depend on the sanitizer runtime as + # a proper shared library instead; that runtime is then LD_PRELOAD-ed + # for the python tests (see tests/CMakeLists.txt). + if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(_core PRIVATE -shared-libsan) + target_link_options(_core PRIVATE -shared-libsan) + endif() + + # Copy the generated library in the package folder add_custom_command(TARGET _core POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "$" "${PYTHON_PACKAGE_DIR}/${PYTHON_PACKAGE_NAME}" diff --git a/python/src/core/contractors/codac2_py_CtcFixpoint.cpp b/python/src/core/contractors/codac2_py_CtcFixpoint.cpp index 989c4baa7..9743f82f5 100644 --- a/python/src/core/contractors/codac2_py_CtcFixpoint.cpp +++ b/python/src/core/contractors/codac2_py_CtcFixpoint.cpp @@ -26,7 +26,7 @@ void export_CtcFixpoint(py::module& m, py::class_,pyCtcI exported .def(py::init( - [](const pyCtcIntervalVector& c, double ratio) + [](const CtcBase& c, double ratio) { return std::make_unique(c.copy(),ratio); }), diff --git a/python/src/core/contractors/codac2_py_CtcLazy.cpp b/python/src/core/contractors/codac2_py_CtcLazy.cpp index 10f25c61e..8d995281a 100644 --- a/python/src/core/contractors/codac2_py_CtcLazy.cpp +++ b/python/src/core/contractors/codac2_py_CtcLazy.cpp @@ -26,7 +26,7 @@ void export_CtcLazy(py::module& m, py::class_,pyCtcInter exported .def(py::init( - [](const pyCtcIntervalVector& c) + [](const CtcBase& c) { return std::make_unique(c.copy()); }), diff --git a/python/src/core/contractors/codac2_py_CtcNot.cpp b/python/src/core/contractors/codac2_py_CtcNot.cpp index f7e3ac89e..0769e01e9 100644 --- a/python/src/core/contractors/codac2_py_CtcNot.cpp +++ b/python/src/core/contractors/codac2_py_CtcNot.cpp @@ -26,7 +26,7 @@ void export_CtcNot(py::module& m, py::class_,pyCtcInterv exported .def(py::init( - [](const pyCtcIntervalVector& c) + [](const CtcBase& c) { return std::make_unique(c.copy()); }), diff --git a/python/src/core/domains/tube/codac2_py_Slice.h b/python/src/core/domains/tube/codac2_py_Slice.h index 268ebe3f9..8f260e842 100644 --- a/python/src/core/domains/tube/codac2_py_Slice.h +++ b/python/src/core/domains/tube/codac2_py_Slice.h @@ -33,9 +33,11 @@ py::class_> export_Slice(py::module& m, const std::string& name) // Methods from class SliceBase .def("t0_tf", &Slice::t0_tf, + py::return_value_policy::reference_internal, CONST_INTERVAL_REF_SLICEBASE_T0_TF_CONST) .def("tslice", &Slice::tslice, + py::return_value_policy::reference_internal, CONST_TSLICE_REF_SLICEBASE_TSLICE_CONST) // Methods from class Slice diff --git a/python/src/core/domains/tube/codac2_py_SlicedTube.h b/python/src/core/domains/tube/codac2_py_SlicedTube.h index 85bfc96e4..584f19b34 100644 --- a/python/src/core/domains/tube/codac2_py_SlicedTube.h +++ b/python/src/core/domains/tube/codac2_py_SlicedTube.h @@ -84,9 +84,11 @@ py::class_,TubeBase> export_SlicedTube(py::module& m, const std::s py::return_value_policy::reference, SHARED_PTR_SLICE_T_SLICEDTUBE_T_LAST_SLICE) - .def("slice", [](SlicedTube& x, std::shared_ptr it) -> Slice& + .def("slice", [](SlicedTube& x, TSlice& it) -> Slice& { - return *x.slice(it); + return *x.slice( + std::shared_ptr(&it, [](TSlice*) {}) + ); }, py::return_value_policy::reference, SHARED_PTR_SLICE_T_SLICEDTUBE_T_SLICE_SHARED_PTR_TSLICE, diff --git a/python/src/core/domains/tube/codac2_py_TDomain.cpp b/python/src/core/domains/tube/codac2_py_TDomain.cpp index c061ad9c5..3eadf7dfe 100644 --- a/python/src/core/domains/tube/codac2_py_TDomain.cpp +++ b/python/src/core/domains/tube/codac2_py_TDomain.cpp @@ -40,19 +40,21 @@ void export_TDomain(py::module& m) .def("tslices_vector", &TDomain::tslices_vector, VECTOR_TSLICE_TDOMAIN_TSLICES_VECTOR_CONST) - .def("tslice", [](TDomain& tdomain, double t) -> std::shared_ptr + .def("tslice", [](TDomain& tdomain, double t) -> TSlice& { auto it = tdomain.tslice(t); - return std::shared_ptr(&(*it), [](TSlice*){}); + return *it; }, - LIST_TSLICE_ITERATOR_TDOMAIN_TSLICE_DOUBLE + py::return_value_policy::reference_internal, + LIST_TSLICE_ITERATOR_TDOMAIN_TSLICE_DOUBLE, "t"_a) - .def("sample", [](TDomain& tdomain, double t, bool with_gate) -> std::shared_ptr + .def("sample", [](TDomain& tdomain, double t, bool with_gate) -> TSlice& { auto it = tdomain.sample(t, with_gate); - return std::shared_ptr(&(*it), [](TSlice*){}); + return *it; }, + py::return_value_policy::reference_internal, LIST_TSLICE_ITERATOR_TDOMAIN_SAMPLE_DOUBLE_BOOL, "t"_a, "with_gate"_a=false) diff --git a/python/src/core/domains/tube/codac2_py_tube_cart_prod.cpp b/python/src/core/domains/tube/codac2_py_tube_cart_prod.cpp index c7bf4e209..7ef0d2998 100644 --- a/python/src/core/domains/tube/codac2_py_tube_cart_prod.cpp +++ b/python/src/core/domains/tube/codac2_py_tube_cart_prod.cpp @@ -27,11 +27,11 @@ void export_tube_cart_prod(py::module& m) Index n = 0; for(const auto& li : l) { - assert_release(is_instance>(li) | is_instance>(li)); + assert_release(is_instance>(li) || is_instance>(li)); n += is_instance>(li) ? 1 : cast>(li).size(); } - assert_release(is_instance>(*l.begin()) | is_instance>(*l.begin())); + assert_release(is_instance>(*l.begin()) || is_instance>(*l.begin())); std::shared_ptr tdomain = is_instance>(*l.begin()) ? cast>(*l.begin()).tdomain() : cast>(*l.begin()).tdomain(); @@ -42,7 +42,7 @@ void export_tube_cart_prod(py::module& m) Index i = 0; for(const auto& li : l) { - assert_release(is_instance>(li) | is_instance>(li)); + assert_release(is_instance>(li) || is_instance>(li)); IntervalVector si = cart_prod( is_instance>(li) ? cart_prod(cast>(li).slice(it)->codomain()) : cast>(li).slice(it)->codomain()); diff --git a/python/src/core/matrices/codac2_py_MatrixBase.h b/python/src/core/matrices/codac2_py_MatrixBase.h index 457d03fbd..00f8ce98b 100644 --- a/python/src/core/matrices/codac2_py_MatrixBase.h +++ b/python/src/core/matrices/codac2_py_MatrixBase.h @@ -261,12 +261,12 @@ void export_MatrixBase(py::module& m, py::class_& pyclass) DOC_TO_BE_DEFINED, "nb_rows"_a, "nb_cols"_a) - .def("resize_save_values", [](S& x, Index_type nb_rows, Index_type nb_cols) + .def("conservativeResize", [](S& x, Index_type nb_rows, Index_type nb_cols) { matlab::test_integer(nb_rows, nb_cols); - x.resize_save_values(nb_rows, nb_cols); + x.conservativeResize(nb_rows, nb_cols); }, - MATRIX_ADDONS_MATRIXBASE_VOID_RESIZE_SAVE_VALUES_INDEX_INDEX, + "Resize the matrix while preserving the existing coefficients.", "nb_rows"_a, "nb_cols"_a) .def_static("zero", [](Index_type r, Index_type c) diff --git a/python/src/core/matrices/codac2_py_VectorBase.h b/python/src/core/matrices/codac2_py_VectorBase.h index 89b41e4cb..feea0fa83 100644 --- a/python/src/core/matrices/codac2_py_VectorBase.h +++ b/python/src/core/matrices/codac2_py_VectorBase.h @@ -105,12 +105,12 @@ void export_VectorBase([[maybe_unused]] py::module& m, py::class_& pyclass) DOC_TO_BE_DEFINED, "n"_a) - .def("resize_save_values", [](S& x, Index_type n) + .def("conservativeResize", [](S& x, Index_type n) { matlab::test_integer(n); - x.resize_save_values(n); + x.conservativeResize(n); }, - MATRIX_ADDONS_VECTORBASE_VOID_RESIZE_SAVE_VALUES_INDEX, + "Resize the vector while preserving the existing coefficients.", "n"_a) .def("put", [](S& x, Index_type start_id, const S& x1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 28fd1c4fc..c09f9a60a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,12 +69,21 @@ find_package(IBEX REQUIRED) + # codac-core links PUBLIC against Threads::Threads (see src/core/CMakeLists.txt), + # e.g. for peibos/threading code. That requirement doesn't come back through + # find_library() above (a plain path has no transitive usage requirements), + # so it has to be re-declared here -- otherwise any downstream target that + # actually pulls in that code (such as the 11_peibos example) fails to link + # with an undefined reference to pthread_create. + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + set(CODAC_VERSION ${PROJECT_VERSION}) - set(CODAC_LIBRARIES \${CODAC_CORE_LIBRARY} \${CODAC_GRAPHICS_LIBRARY} \${CODAC_UNSUPPORTED_LIBRARY} Ibex::ibex) + set(CODAC_LIBRARIES \${CODAC_CORE_LIBRARY} \${CODAC_GRAPHICS_LIBRARY} \${CODAC_UNSUPPORTED_LIBRARY} Ibex::ibex Threads::Threads) set(CODAC_INCLUDE_DIRS \${CODAC_CORE_INCLUDE_DIR}/../ \${CODAC_CORE_INCLUDE_DIR}/../eigen3/ \${CODAC_CORE_INCLUDE_DIR} \${CODAC_GRAPHICS_INCLUDE_DIR} \${CODAC_UNSUPPORTED_INCLUDE_DIR}) set(CODAC_C_FLAGS \"\") - set(CODAC_CXX_FLAGS \"\") + set(CODAC_CXX_FLAGS \"${CODAC_ARCH_CXX_FLAGS}\") ") if(WITH_PYTHON) @@ -104,7 +113,21 @@ PATH_SUFFIXES include/${PROJECT_NAME}-capd) find_library(CODAC_CAPD_LIBRARY NAMES ${PROJECT_NAME}-capd PATH_SUFFIXES lib) - + + # Fold CAPD into the general CODAC_INCLUDE_DIRS / CODAC_LIBRARIES too, so + # that any consumer using the common find_package(CODAC) + \${CODAC_INCLUDE_DIRS} + # / \${CODAC_LIBRARIES} pattern gets CAPD for free (headers, the codac-capd + # library, and the capd::capd target), without needing to separately + # reference CODAC_CAPD_INCLUDE_DIR / CODAC_CAPD_LIBRARY / capd::capd + # themselves -- unlike the -mfma and Threads::Threads gaps, this one + # doesn't corrupt anything if forgotten (see 10_lie_groups), it just + # fails to compile, but it's the same class of oversight. + set(CODAC_INCLUDE_DIRS \${CODAC_INCLUDE_DIRS} \${CODAC_CAPD_INCLUDE_DIR}) + set(CODAC_LIBRARIES \${CODAC_LIBRARIES} \${CODAC_CAPD_LIBRARY} capd::capd) + + # CODAC_CAPD_LIBRARY is kept as its own variable too, for any consumer + # that prefers to opt into CAPD explicitly rather than pull it in via + # the general CODAC_LIBRARIES. set(CODAC_CAPD_LIBRARY \${CODAC_CAPD_LIBRARY} \${CODAC_LIBRARIES}) ") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index db02e9526..5e8d08c6c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -293,12 +293,14 @@ # Create the target for libcodac-core ################################################################################ - #if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - #endif() - add_library(${PROJECT_NAME}-core ${CODAC_CORE_SRC}) + + set_target_properties(${PROJECT_NAME}-core PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + ) + target_include_directories(${PROJECT_NAME}-core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/actions ${CMAKE_CURRENT_SOURCE_DIR}/contractors @@ -337,21 +339,63 @@ set(CODAC_PKG_CONFIG_CFLAGS "${CODAC_PKG_CONFIG_CFLAGS} -I\${includedir}/${PROJECT_NAME}-core" PARENT_SCOPE) set(CODAC_PKG_CONFIG_LIBS "${CODAC_PKG_CONFIG_LIBS} -l${PROJECT_NAME}-core" PARENT_SCOPE) - + ################################################################################ -# Installation of libcodac-core files +# Installation / build include tree ################################################################################ - -# Getting header files from sources + + # Directory containing the generated/build-time public headers. + set(CODAC_BUILD_INCLUDE_DIR + ${CMAKE_CURRENT_BINARY_DIR}/../../include) + + file(MAKE_DIRECTORY ${CODAC_BUILD_INCLUDE_DIR}) + + + # Generate the build-time include tree using symbolic links. + # + # IMPORTANT: + # Do NOT copy headers here. + # + # Copying creates two physically different files: + # + # src/core/tools/codac2_math.h + # build/include/codac2_math.h + # + # which can bypass #pragma once and produce redefinition errors. + # + # Symbolic links preserve a single physical header. foreach(srcfile ${CODAC_CORE_SRC}) + if(srcfile MATCHES "\\.h$" OR srcfile MATCHES "\\.hpp$") + list(APPEND CODAC_CORE_HDR ${srcfile}) - file(COPY ${srcfile} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) + + get_filename_component(header_name + ${srcfile} + NAME) + + set(build_header + ${CODAC_BUILD_INCLUDE_DIR}/${header_name}) + + if(EXISTS ${build_header} OR IS_SYMLINK ${build_header}) + file(REMOVE ${build_header}) + endif() + + file( + CREATE_LINK + ${srcfile} + ${build_header} + SYMBOLIC + ) + endif() + endforeach() + # Generating the file codac-core.h +# ================================ set(CODAC_CORE_MAIN_HEADER ${CMAKE_CURRENT_BINARY_DIR}/codac-core.h) set(CODAC_MAIN_SUBHEADERS ${CODAC_MAIN_SUBHEADERS} "codac-core.h" PARENT_SCOPE) @@ -363,10 +407,29 @@ file(APPEND ${CODAC_CORE_MAIN_HEADER} "#include <${header_name}>\n") endif() endforeach() - file(COPY ${CODAC_CORE_MAIN_HEADER} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../include) + + + # The generated umbrella header is also exposed in the build include tree. + set(build_core_main_header + ${CODAC_BUILD_INCLUDE_DIR}/codac-core.h) + + if( + EXISTS ${build_core_main_header} + OR IS_SYMLINK ${build_core_main_header} + ) + file(REMOVE ${build_core_main_header}) + endif() + + file( + CREATE_LINK + ${CODAC_CORE_MAIN_HEADER} + ${build_core_main_header} + SYMBOLIC + ) # Install files in system directories +# ==================================== install(TARGETS ${PROJECT_NAME}-core DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${CODAC_CORE_HDR} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}-core) diff --git a/src/core/actions/codac2_OctaSym.h b/src/core/actions/codac2_OctaSym.h index 81cdb1d7c..3884486e3 100644 --- a/src/core/actions/codac2_OctaSym.h +++ b/src/core/actions/codac2_OctaSym.h @@ -23,7 +23,7 @@ namespace codac2 class SepBase; class SepAction; class SetExpr; - class OctaSymOp; + struct OctaSymOp; /** * \class Action diff --git a/src/core/contractors/codac2_CtcDist.h b/src/core/contractors/codac2_CtcDist.h index 6a55e6820..ad2dfacb2 100644 --- a/src/core/contractors/codac2_CtcDist.h +++ b/src/core/contractors/codac2_CtcDist.h @@ -37,6 +37,7 @@ namespace codac2 */ CtcDist(); + using CtcBase::contract; /** * \brief Applies \f$\mathcal{C}_{\textrm{dist}}\big([\mathbf{x}]\big)\f$. * @@ -55,4 +56,4 @@ namespace codac2 */ void contract(Interval& a1, Interval& a2, Interval& b1, Interval& b2, Interval& d) const; }; -} \ No newline at end of file +} diff --git a/src/core/contractors/codac2_CtcPolar.h b/src/core/contractors/codac2_CtcPolar.h index 23f3f704c..dc85ab764 100644 --- a/src/core/contractors/codac2_CtcPolar.h +++ b/src/core/contractors/codac2_CtcPolar.h @@ -42,6 +42,7 @@ namespace codac2 : Ctc(4) { } + using CtcBase::contract; /** * \brief Applies \f$\mathcal{C}_{\textrm{polar}}\big([\mathbf{x}]\big)\f$. * @@ -59,4 +60,4 @@ namespace codac2 */ void contract(Interval& x, Interval& y, Interval& rho, Interval& theta) const; }; -} \ No newline at end of file +} diff --git a/src/core/contractors/codac2_CtcProj.h b/src/core/contractors/codac2_CtcProj.h index 20fc5c91d..daa0d404b 100644 --- a/src/core/contractors/codac2_CtcProj.h +++ b/src/core/contractors/codac2_CtcProj.h @@ -39,7 +39,8 @@ namespace codac2 assert_release(size_of(c) >= (Index)_xi.size() && "cannot compute a projection of a set into a superset"); assert_release(default_eps > 0.); } - + + using CtcBase::contract; void contract(IntervalVector& x) const; void contract(IntervalVector& x, double eps) const; diff --git a/src/core/domains/paving/codac2_Paving.cpp b/src/core/domains/paving/codac2_Paving.cpp index c14c2142f..90f04774e 100644 --- a/src/core/domains/paving/codac2_Paving.cpp +++ b/src/core/domains/paving/codac2_Paving.cpp @@ -18,14 +18,17 @@ namespace codac2 // PavingOut class PavingOut::PavingOut(Index n) - : Paving(n) + : Paving() { assert_release(n > 0); + this->init_tree(IntervalVector(n)); } PavingOut::PavingOut(const IntervalVector& x) - : Paving(x) - { } + : Paving() + { + this->init_tree(x); + } std::list PavingOut::connected_subsets(const PavingOut::NodeValue_& node_value) const { @@ -83,14 +86,17 @@ namespace codac2 // PavingInOut class PavingInOut::PavingInOut(Index n) - : Paving(n) + : Paving() { assert_release(n > 0); + this->init_tree(IntervalVector(n)); } PavingInOut::PavingInOut(const IntervalVector& x) - : Paving(x) - { } + : Paving() + { + this->init_tree(x); + } std::list PavingInOut::connected_subsets(const PavingInOut::NodeValue_& node_value) const { @@ -141,4 +147,4 @@ namespace codac2 l.push_back(n->unknown()); return l; }; -} \ No newline at end of file +} diff --git a/src/core/domains/paving/codac2_Paving.h b/src/core/domains/paving/codac2_Paving.h index 25f5c8f39..7f55aa161 100644 --- a/src/core/domains/paving/codac2_Paving.h +++ b/src/core/domains/paving/codac2_Paving.h @@ -32,16 +32,19 @@ namespace codac2 using NodeValue_ = std::function(Node_)>; using ConnectedSubset_ = Subpaving

; - Paving(Index n) - : Paving(IntervalVector(n)) - { - assert_release(n > 0); - } - - Paving(const IntervalVector& x) - : _tree(std::make_shared>(*static_cast(this), x)) - { } - + protected: + // Paving(Index n) + // : Paving(IntervalVector(n)) + // { + // assert_release(n > 0); + // } + // Paving(const IntervalVector& x) + // : _tree(std::make_shared>(*static_cast(this), x)) + // { } + + Paving () { } + + public: inline Index size() const { return std::get<0>(_tree->boxes()).size(); @@ -128,6 +131,12 @@ namespace codac2 friend class PavingNode

; + inline void init_tree(const IntervalVector& x) + { + _tree = std::make_shared>(*static_cast(this), x); + } + + inline static NodeTuple_ init_tuple(const IntervalVector& x) { return std::make_tuple(((X)x)...); @@ -191,4 +200,4 @@ namespace codac2 static const NodeValue_ outer, outer_complem, inner, bound, all; }; -} \ No newline at end of file +} diff --git a/src/core/domains/paving/codac2_PavingNode.h b/src/core/domains/paving/codac2_PavingNode.h index e294e1bd7..aa69a0108 100644 --- a/src/core/domains/paving/codac2_PavingNode.h +++ b/src/core/domains/paving/codac2_PavingNode.h @@ -61,7 +61,7 @@ namespace codac2 std::shared_ptr> top() const { - return _top; + return _top.lock(); } std::shared_ptr> top() @@ -102,7 +102,7 @@ namespace codac2 void visit(std::function>)> visitor) const { - if(!_top && !_right && _left && left()->boxes() == _x) + if(_top.expired() && !_right && _left && left()->boxes() == _x) left()->visit(visitor); else if(visitor(this->shared_from_this())) @@ -114,7 +114,7 @@ namespace codac2 void visit(std::function>)> visitor) { - if(!_top && !_right && _left && left()->boxes() == _x) + if(_top.expired() && !_right && _left && left()->boxes() == _x) _left->visit(visitor); else if(visitor(this->shared_from_this())) @@ -157,7 +157,7 @@ namespace codac2 const P& _paving; typename P::NodeTuple_ _x; - std::shared_ptr> _top = nullptr; + std::weak_ptr> _top; std::shared_ptr> _left = nullptr, _right = nullptr; }; -} \ No newline at end of file +} diff --git a/src/core/domains/tube/codac2_Slice.h b/src/core/domains/tube/codac2_Slice.h index 4d74f0389..29c6af000 100644 --- a/src/core/domains/tube/codac2_Slice.h +++ b/src/core/domains/tube/codac2_Slice.h @@ -295,7 +295,7 @@ namespace codac2 * * No propagation is performed on adjacent slices. */ - inline void init() + inline void init() override { this->T::init(); // Nothing to propagate to adjacent codomains @@ -306,7 +306,7 @@ namespace codac2 * * Adjacent gates are updated accordingly. */ - inline void set_empty() + inline void set_empty() override { set_empty(true); } diff --git a/src/core/domains/tube/codac2_SlicedTube.h b/src/core/domains/tube/codac2_SlicedTube.h index f8dca4c91..d745eeda3 100644 --- a/src/core/domains/tube/codac2_SlicedTube.h +++ b/src/core/domains/tube/codac2_SlicedTube.h @@ -382,7 +382,7 @@ namespace codac2 { return eval_common(t, [this](auto it, const Interval& t_) { - return slice(it)->operator()(t_); + return this->slice(it)->operator()(t_); }); } @@ -398,7 +398,7 @@ namespace codac2 { return eval_common(t, [this,&v](auto it, const Interval& t_) { - return slice(it)->operator()(t_, *v.slice(it)); + return this->slice(it)->operator()(t_, *v.slice(it)); }); } @@ -783,7 +783,7 @@ namespace codac2 { return invert_common(y, t, [this,&y](auto it, const Interval& t_) { - return slice(it)->invert(y, t_); + return this->slice(it)->invert(y, t_); }); } @@ -812,7 +812,7 @@ namespace codac2 { return invert_common_subsets(y, v_t, t, [this,&y](auto it, const Interval& t_) { - return slice(it)->invert(y, t_); + return this->slice(it)->invert(y, t_); }); } @@ -849,7 +849,7 @@ namespace codac2 { return invert_common(y, t, [this,&v,&y](auto it, const Interval& t_) { - return slice(it)->invert(y, *v.slice(it), t_); + return this->slice(it)->invert(y, *v.slice(it), t_); }); } @@ -886,7 +886,7 @@ namespace codac2 { return invert_common_subsets(y, v_t, t, [this,&v,&y](auto it, const Interval& t_) { - return slice(it)->invert(y, *v.slice(it), t_); + return this->slice(it)->invert(y, *v.slice(it), t_); }); } @@ -1265,4 +1265,4 @@ namespace codac2 } } -#include "codac2_SlicedTube_integral_impl.h" \ No newline at end of file +#include "codac2_SlicedTube_integral_impl.h" diff --git a/src/core/domains/tube/codac2_TDomain.cpp b/src/core/domains/tube/codac2_TDomain.cpp index 3d6260949..16281ed98 100644 --- a/src/core/domains/tube/codac2_TDomain.cpp +++ b/src/core/domains/tube/codac2_TDomain.cpp @@ -244,7 +244,7 @@ namespace codac2 list::const_iterator it1 = tdom1->cbegin(), it2 = tdom2->cbegin(); while(it1 != tdom1->cend()) { - if(*it1 != *it2) return false; + if(!((*it1) == (*it2))) return false; it1++; it2++; } return true; diff --git a/src/core/domains/tube/codac2_TSlice.cpp b/src/core/domains/tube/codac2_TSlice.cpp index 0377e24ad..97410e475 100644 --- a/src/core/domains/tube/codac2_TSlice.cpp +++ b/src/core/domains/tube/codac2_TSlice.cpp @@ -35,4 +35,6 @@ namespace codac2 { return _slices; } + + TSlice::~TSlice() = default; } \ No newline at end of file diff --git a/src/core/domains/tube/codac2_TSlice.h b/src/core/domains/tube/codac2_TSlice.h index 81edf9c59..1b3952795 100644 --- a/src/core/domains/tube/codac2_TSlice.h +++ b/src/core/domains/tube/codac2_TSlice.h @@ -74,7 +74,13 @@ namespace codac2 const std::map>& slices() const; using Interval::operator==; - + + bool operator==(const TSlice& x) const { + return Interval::operator==(x); + } + + ~TSlice() override; + protected: /** diff --git a/src/core/functions/analytic/codac2_AnalyticExpr.h b/src/core/functions/analytic/codac2_AnalyticExpr.h index 7de355569..3c8ebeb8b 100644 --- a/src/core/functions/analytic/codac2_AnalyticExpr.h +++ b/src/core/functions/analytic/codac2_AnalyticExpr.h @@ -76,17 +76,17 @@ namespace codac2 : OperationExprBase...>(e) { } - std::shared_ptr copy() const + std::shared_ptr copy() const override { return std::make_shared>(*this); } - void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) + void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) override { return OperationExprBase...>::replace_arg(old_arg_id, new_expr); } - Y fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const + Y fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const override { return std::apply( [this,&v,total_input_size,natural_eval](auto &&... x) @@ -102,7 +102,7 @@ namespace codac2 this->_x); } - void bwd_eval(ValuesMap& v) const + void bwd_eval(ValuesMap& v) const override { auto y = AnalyticExpr::value(v); @@ -117,7 +117,7 @@ namespace codac2 }, this->_x); } - virtual std::string str(bool in_parentheses = false) const + virtual std::string str(bool in_parentheses = false) const override { std::string s = std::apply([](auto &&... x) { return C::str(x...); @@ -125,12 +125,12 @@ namespace codac2 return in_parentheses ? "(" + s + ")" : s; } - virtual bool is_str_leaf() const + virtual bool is_str_leaf() const override { return false; } - std::pair output_shape() const + std::pair output_shape() const override { std::pair s; std::apply([&s](auto &&... x) @@ -140,7 +140,7 @@ namespace codac2 return s; } - virtual bool belongs_to_args_list(const FunctionArgsList& args) const + virtual bool belongs_to_args_list(const FunctionArgsList& args) const override { bool b = true; diff --git a/src/core/functions/analytic/codac2_analytic_constants.h b/src/core/functions/analytic/codac2_analytic_constants.h index ae446bfda..a447f2f5f 100644 --- a/src/core/functions/analytic/codac2_analytic_constants.h +++ b/src/core/functions/analytic/codac2_analytic_constants.h @@ -28,12 +28,12 @@ namespace codac2 return _x; } - std::shared_ptr copy() const + std::shared_ptr copy() const override { return std::make_shared>(*this); } - T fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const + T fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const override { if(natural_eval) return AnalyticExpr::init_value(v, T( @@ -55,12 +55,12 @@ namespace codac2 )); } - void bwd_eval(ValuesMap& v) const + void bwd_eval(ValuesMap& v) const override { AnalyticExpr::value(v).a &= _x; } - std::pair output_shape() const + std::pair output_shape() const override { if constexpr(std::is_same_v) return {1,1}; @@ -74,17 +74,17 @@ namespace codac2 assert_release_constexpr(false && "unknow output shape for constant"); } - void replace_arg([[maybe_unused]] const ExprID& old_arg_id, [[maybe_unused]] const std::shared_ptr& new_expr) + void replace_arg([[maybe_unused]] const ExprID& old_arg_id, [[maybe_unused]] const std::shared_ptr& new_expr) override { } - virtual bool belongs_to_args_list([[maybe_unused]] const FunctionArgsList& args) const + virtual bool belongs_to_args_list([[maybe_unused]] const FunctionArgsList& args) const override { return true; } virtual std::string str(bool in_parentheses = false) const override; - virtual bool is_str_leaf() const + virtual bool is_str_leaf() const override { return true; } diff --git a/src/core/geometry/codac2_geometry.cpp b/src/core/geometry/codac2_geometry.cpp index 439de9a54..a8b98919f 100644 --- a/src/core/geometry/codac2_geometry.cpp +++ b/src/core/geometry/codac2_geometry.cpp @@ -62,15 +62,14 @@ namespace codac2 vector convex_hull(vector pts) { - // Removing duplicates - sort(pts.begin(), pts.end(), - [](const IntervalVector& a, const IntervalVector& b) { - return a[0].mid() < b[0].mid() || (a[0].mid() == b[0].mid() && a[1].lb() < b[1].lb()); - }); - pts.erase(unique(pts.begin(), pts.end()), pts.end()); + // Removing duplicates + sort(pts.begin(), pts.end(), + [](const IntervalVector& a, const IntervalVector& b) { + return a[0].mid() < b[0].mid() || (a[0].mid() == b[0].mid() && a[1].lb() < b[1].lb()); + }); + pts.erase(unique(pts.begin(), pts.end()), pts.end()); - if(pts.size() < 3) - return pts; + if(pts.size() < 3) return pts; // Implementation of a Graham scan method. // Based on some sources from OpenGenus Foundation. @@ -203,4 +202,4 @@ namespace codac2 } } while(merged_something); } -} \ No newline at end of file +} diff --git a/src/core/matrices/codac2_matrices.h b/src/core/matrices/codac2_matrices.h index a92cd6a1f..dab1c7ac2 100644 --- a/src/core/matrices/codac2_matrices.h +++ b/src/core/matrices/codac2_matrices.h @@ -30,7 +30,12 @@ namespace Eigen concept IsVectorOrRow = (C == 1 || R == 1); template - concept IsIntervalDomain = std::is_same_v; + concept IsIntervalDomain = codac2::is_interval_based::value; + //concept IsIntervalDomain = std::is_same_v; + + //template + //concept IsAffineDomain = codac2::is_affine_based::value; + } #define EIGEN_MATRIXBASE_PLUGIN "codac2_MatrixBase_addons_include.h" diff --git a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h index 64ef607a3..a53447794 100644 --- a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h +++ b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_IntervalMatrixBase.h @@ -141,7 +141,7 @@ inline bool operator==(const MatrixBase& x) const inline void set_empty() requires IsIntervalDomain { - this->init(codac2::Interval::empty()); + this->init(Scalar::empty()); } /** @@ -342,4 +342,4 @@ template inline auto bisect_largest(double ratio = 0.49, const std::vector& among_indices = {}) const { return bisect(this->max_diam_index(among_indices), ratio); -} \ No newline at end of file +} diff --git a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_MatrixBase.h b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_MatrixBase.h index b56b95309..c84a4317c 100644 --- a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_MatrixBase.h +++ b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_MatrixBase.h @@ -154,25 +154,25 @@ inline static Matrix random(Index r, Index c) return DenseBase>::Random(r,c); } -/** - * \brief Resizes the matrix to (``r``,``c``), preserving existing values where possible. - * - * \param r New number of rows. - * \param c New number of columns. - * - * \details - * This function resizes the matrix while preserving the data in the overlapping region - * of the old and new sizes. Unlike Eigen's ``resize()``, which discards old data, this - * function copies existing values into the resized matrix. - */ -template - requires (!IsVectorOrRow) -inline void resize_save_values(Index r, Index c) -{ - // With resize() of Eigen, the data is reallocated and all previous values are lost. - auto copy = *this; - this->resize(r,c); - for(Index i = 0 ; i < std::min((Index)copy.rows(),r) ; i++) - for(Index j = 0 ; j < std::min((Index)copy.cols(),c) ; j++) - (*this)(i,j) = copy(i,j); -} \ No newline at end of file +// /** +// * \brief Resizes the matrix to (``r``,``c``), preserving existing values where possible. +// * +// * \param r New number of rows. +// * \param c New number of columns. +// * +// * \details +// * This function resizes the matrix while preserving the data in the overlapping region +// * of the old and new sizes. Unlike Eigen's ``resize()``, which discards old data, this +// * function copies existing values into the resized matrix. +// */ +// template +// requires (!IsVectorOrRow) +// inline void conservativeResize(Index r, Index c) resize_save_values(Index r, Index c) +// { +// // With resize() of Eigen, the data is reallocated and all previous values are lost. +// auto copy = *this; +// this->resize(r,c); +// for(Index i = 0 ; i < std::min((Index)copy.rows(),r) ; i++) +// for(Index j = 0 ; j < std::min((Index)copy.cols(),c) ; j++) +// (*this)(i,j) = copy(i,j); +// } \ No newline at end of file diff --git a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_VectorBase.h b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_VectorBase.h index f5021ebf5..1e1c6a49e 100644 --- a/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_VectorBase.h +++ b/src/core/matrices/eigen/Matrix_addons/codac2_Matrix_addons_VectorBase.h @@ -195,24 +195,24 @@ inline void put(Index start_id, const MatrixBase& x) this->segment(start_id,x.size()) << x; } -/** - * \brief Resizes the vector or row matrix to size \p n, preserving existing values. - * - * \pre The matrix is a vector or row vector. - * - * \param n The new size. - * - * \details - * Eigen's ``resize()`` discards existing data, so this function copies existing - * values before resizing and restores them afterward. - */ -template - requires IsVectorOrRow -inline void resize_save_values(Index n) -{ - // With resize() of Eigen, the data is reallocated and all previous values are lost. - auto copy = *this; - this->resize(n); - for(Index i = 0 ; i < std::min((Index)copy.size(),n) ; i++) - (*this)[i] = copy[i]; -} \ No newline at end of file +// /** +// * \brief Resizes the vector or row matrix to size \p n, preserving existing values. +// * +// * \pre The matrix is a vector or row vector. +// * +// * \param n The new size. +// * +// * \details +// * Eigen's ``resize()`` discards existing data, so this function copies existing +// * values before resizing and restores them afterward. +// */ +// template +// requires IsVectorOrRow +// inline void resize_save_values(Index n) conservativeResize(Index n) +// { +// // With resize() of Eigen, the data is reallocated and all previous values are lost. +// auto copy = *this; +// this->resize(n); +// for(Index i = 0 ; i < std::min((Index)copy.size(),n) ; i++) +// (*this)[i] = copy[i]; +// } \ No newline at end of file diff --git a/src/core/operators/codac2_component.h b/src/core/operators/codac2_component.h index 16ce50dcc..5558209b0 100644 --- a/src/core/operators/codac2_component.h +++ b/src/core/operators/codac2_component.h @@ -71,17 +71,17 @@ namespace codac2 : OperationExprBase>(e), _i(e._i) { } - std::shared_ptr copy() const + std::shared_ptr copy() const override { return std::make_shared>(*this); } - void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) + void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) override { return OperationExprBase>::replace_arg(old_arg_id, new_expr); } - ScalarType fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const + ScalarType fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const override { if(natural_eval) return AnalyticExpr::init_value( @@ -91,29 +91,29 @@ namespace codac2 v, ComponentOp::fwd_centered(std::get<0>(this->_x)->fwd_eval(v, total_input_size, natural_eval), _i)); } - void bwd_eval(ValuesMap& v) const + void bwd_eval(ValuesMap& v) const override { ComponentOp::bwd(AnalyticExpr::value(v).a, std::get<0>(this->_x)->value(v).a, _i); std::get<0>(this->_x)->bwd_eval(v); } - std::pair output_shape() const + std::pair output_shape() const override { return ComponentOp::output_shape(std::get<0>(this->_x),_i); } - virtual bool belongs_to_args_list(const FunctionArgsList& args) const + virtual bool belongs_to_args_list(const FunctionArgsList& args) const override { return std::get<0>(this->_x)->belongs_to_args_list(args); } - std::string str(bool in_parentheses = false) const + std::string str(bool in_parentheses = false) const override { std::string s = ComponentOp::str(std::get<0>(this->_x), _i); return in_parentheses ? "(" + s + ")" : s; } - virtual bool is_str_leaf() const + virtual bool is_str_leaf() const override { return true; } @@ -146,17 +146,17 @@ namespace codac2 : OperationExprBase>(e), _i(e._i), _j(e._j) { } - std::shared_ptr copy() const + std::shared_ptr copy() const override { return std::make_shared>(*this); } - void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) + void replace_arg(const ExprID& old_arg_id, const std::shared_ptr& new_expr) override { return OperationExprBase>::replace_arg(old_arg_id, new_expr); } - ScalarType fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const + ScalarType fwd_eval(ValuesMap& v, Index total_input_size, bool natural_eval) const override { if(natural_eval) return AnalyticExpr::init_value( @@ -166,29 +166,29 @@ namespace codac2 v, ComponentOp::fwd_centered(std::get<0>(this->_x)->fwd_eval(v, total_input_size, natural_eval), _i, _j)); } - void bwd_eval(ValuesMap& v) const + void bwd_eval(ValuesMap& v) const override { ComponentOp::bwd(AnalyticExpr::value(v).a, std::get<0>(this->_x)->value(v).a, _i, _j); std::get<0>(this->_x)->bwd_eval(v); } - std::pair output_shape() const + std::pair output_shape() const override { return ComponentOp::output_shape(std::get<0>(this->_x),_i,_j); } - virtual bool belongs_to_args_list(const FunctionArgsList& args) const + virtual bool belongs_to_args_list(const FunctionArgsList& args) const override { return std::get<0>(this->_x)->belongs_to_args_list(args); } - std::string str(bool in_parentheses = false) const + std::string str(bool in_parentheses = false) const override { std::string s = ComponentOp::str(std::get<0>(this->_x), _i, _j); return in_parentheses ? "(" + s + ")" : s; } - virtual bool is_str_leaf() const + virtual bool is_str_leaf() const override { return true; } diff --git a/src/core/peibos/codac2_peibos.h b/src/core/peibos/codac2_peibos.h index 840421bae..d486997cd 100644 --- a/src/core/peibos/codac2_peibos.h +++ b/src/core/peibos/codac2_peibos.h @@ -17,7 +17,7 @@ namespace codac2 { // Forward declarations to reduce compilation load caused by heavy template use: - class AnalyticTypeBase; + struct AnalyticTypeBase; template requires std::is_base_of_v @@ -65,4 +65,4 @@ namespace codac2 * \return A vector of Parallelepipeds enclosing \f$\mathbf{f}(\sigma(\psi_0([-1,1]^m))+ offset)\f$ for each symmetry \f$\sigma\f$ in the set of symmetries \f$\Sigma\f$. */ std::vector PEIBOS(const AnalyticFunction& f, const AnalyticFunction& psi_0, const std::vector& Sigma, double epsilon, const Vector& offset, bool verbose = false); -} \ No newline at end of file +} diff --git a/src/core/tools/codac2_Approx.h b/src/core/tools/codac2_Approx.h index 721da8249..51f596087 100644 --- a/src/core/tools/codac2_Approx.h +++ b/src/core/tools/codac2_Approx.h @@ -37,38 +37,48 @@ namespace codac2 : _x(x.eval()), _eps(eps) { } - friend bool operator==(const T& x1, const Approx& x2) - { - if constexpr(std::is_same_v) - return std::fabs(x1-x2._x) < x2._eps; - - else if(x1.size() != x2._x.size()) - return false; - - else if(x1 == x2._x) - return true; - - else if constexpr(std::is_same_v) - { - if((x1.is_empty() && !x2._x.is_empty()) || (!x1.is_empty() && x2._x.is_empty())) - return false; - return (x1.lb() == x2._x.lb() || x1.lb() == Approx(x2._x.lb(),x2._eps)) - && (x1.ub() == x2._x.ub() || x1.ub() == Approx(x2._x.ub(),x2._eps)); - } - - else if constexpr(std::is_same_v - || std::is_same_v - || std::is_same_v - || std::is_same_v - || std::is_same_v - || std::is_same_v) - { - for(Index i = 0 ; i < x1.rows() ; i++) - for(Index j = 0 ; j < x1.cols() ; j++) - if(!(x1(i,j) == Approx(x2._x(i,j), x2._eps))) - return false; - return true; - } + friend bool operator==(const T& x1, const Approx& x2) { + if constexpr(std::is_same_v) { + if (std::isnan(x1) && std::isnan(x2._x)) { + return true; + } else if (x2._x>=std::numeric_limits::max()) { + return (x1>=std::numeric_limits::max()); + } else if (x2._x<=-std::numeric_limits::max()) { + return (x1<=-std::numeric_limits::max()); + } else if ((x2._x < 1.0) && (x2._x > -1.0)) { + return std::fabs(x1-x2._x) < x2._eps; //absolute error + } else { + return std::fabs(x1-x2._x) < x2._eps*std::max(std::fabs(x1),std::fabs(x2._x)); //relative error + } + } + else if(x1.size() != x2._x.size()) + return false; + + else if(x1 == x2._x) + return true; + + else if constexpr(std::is_same_v) + { + if((x1.is_empty() && !x2._x.is_empty()) || (!x1.is_empty() && x2._x.is_empty())) + return false; + return (x1.lb() == x2._x.lb() || x1.lb() == Approx(x2._x.lb(),x2._eps)) + && (x1.ub() == x2._x.ub() || x1.ub() == Approx(x2._x.ub(),x2._eps)); + } + + else if constexpr(std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v + || std::is_same_v) + { + for(Index i = 0 ; i < x1.rows() ; i++) + for(Index j = 0 ; j < x1.cols() ; j++) + if(!(x1(i,j) == Approx(x2._x(i,j), x2._eps))) + return false; + return true; + + } else { diff --git a/src/core/tools/codac2_TypeInfo.h b/src/core/tools/codac2_TypeInfo.h index 860c43474..977d94707 100644 --- a/src/core/tools/codac2_TypeInfo.h +++ b/src/core/tools/codac2_TypeInfo.h @@ -9,6 +9,8 @@ #pragma once +#include + namespace codac2 { template diff --git a/src/core/trajectory/codac2_SampledTraj.h b/src/core/trajectory/codac2_SampledTraj.h index 3aaaa0537..8fab89371 100644 --- a/src/core/trajectory/codac2_SampledTraj.h +++ b/src/core/trajectory/codac2_SampledTraj.h @@ -280,6 +280,7 @@ namespace codac2 SampledTraj primitive() const { T s = [this]() { + (void)this; if constexpr(std::is_same_v) return 0.; else diff --git a/src/core/trajectory/codac2_TrajBase.h b/src/core/trajectory/codac2_TrajBase.h index 46e12f645..11e345f83 100644 --- a/src/core/trajectory/codac2_TrajBase.h +++ b/src/core/trajectory/codac2_TrajBase.h @@ -27,6 +27,8 @@ namespace codac2 TrajBase() { } + virtual ~TrajBase() = default; // <-- ajout : corrige le warning sur toutes les classes filles + virtual Index size() const = 0; virtual std::pair shape() const = 0; virtual bool is_empty() const = 0; @@ -43,4 +45,4 @@ namespace codac2 SampledTraj sampled_as(const SampledTraj& x) const; SampledTraj primitive(double dt) const; }; -} \ No newline at end of file +} diff --git a/src/core/trajectory/codac2_TrajBase_impl.h b/src/core/trajectory/codac2_TrajBase_impl.h index 50d123e78..483845d67 100644 --- a/src/core/trajectory/codac2_TrajBase_impl.h +++ b/src/core/trajectory/codac2_TrajBase_impl.h @@ -55,6 +55,7 @@ namespace codac2 assert_release(!is_empty()); T s = [this]() { + (void)this; if constexpr(std::is_same_v) return 0.; else diff --git a/src/extensions/capd/codac2_peibos_capd.cpp b/src/extensions/capd/codac2_peibos_capd.cpp index 48eed323d..41e80c19b 100644 --- a/src/extensions/capd/codac2_peibos_capd.cpp +++ b/src/extensions/capd/codac2_peibos_capd.cpp @@ -19,12 +19,12 @@ using namespace std; namespace codac2 { - std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const vector& Sigma, double epsilon, bool verbose) + std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const vector& Sigma, double epsilon, bool verbose) { return PEIBOS(i_map, tf, dt, psi_0, Sigma, epsilon, Vector::zero(psi_0.output_size()), verbose); } - std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const vector& Sigma, double epsilon, const Vector& offset, bool verbose) + std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const vector& Sigma, double epsilon, const Vector& offset, bool verbose) { std::vector time_points; for (double t = 0.; t <= tf; t += dt) @@ -48,7 +48,7 @@ namespace codac2 double true_eps = split(Interval(-1.,1.)*IntervalVector::Ones(m), epsilon, boxes); int nthreads = nb_threads(); - std::vector>> thread_outputs(nthreads); + std::vector>> thread_outputs(nthreads); struct WorkItem { const OctaSym* sigma; const IntervalVector* box; }; std::vector work; @@ -109,7 +109,7 @@ namespace codac2 for (auto& th : threads) th.join(); - std::map> output; + std::map> output; for (auto& vec : thread_outputs) for (auto t : time_points) @@ -130,7 +130,7 @@ namespace codac2 return output; } - std::map> reach_set(const std::map>& peibos_output) + std::map> reach_set(const std::map>& peibos_output) { std::map> output; diff --git a/src/extensions/capd/codac2_peibos_capd.h b/src/extensions/capd/codac2_peibos_capd.h index 9aab7cfd0..3cfe4609d 100644 --- a/src/extensions/capd/codac2_peibos_capd.h +++ b/src/extensions/capd/codac2_peibos_capd.h @@ -36,7 +36,7 @@ namespace codac2 Vector offset; }; - using T = std::tuple; + using PEIBOS_CAPD_Result = std::tuple; /** * \brief PEIBOS algorithm using CAPD for guaranteed ODE propagation. @@ -54,7 +54,7 @@ namespace codac2 * \li The interval vector \f$\mathbf{z}\f$ containing the image \f$\bar{\mathbf{x}}(t))\f$ * \li The interval Jacobian matrix \f$\mathbf{J_f}\f$ containing \f$D\mathbf{\left[x\right]}(t)\f$ */ - std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const std::vector& Sigma, double epsilon, bool verbose = false); + std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const std::vector& Sigma, double epsilon, bool verbose = false); /** * \brief PEIBOS algorithm using CAPD for guaranteed ODE propagation. @@ -73,7 +73,7 @@ namespace codac2 * \li The interval vector \f$\mathbf{z}\f$ containing the image \f$\bar{\mathbf{x}}(t))\f$ * \li The interval Jacobian matrix \f$\mathbf{J_f}\f$ containing \f$D\mathbf{\left[x\right]}(t)\f$ */ - std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const std::vector& Sigma, double epsilon, const Vector& offset, bool verbose = false); + std::map> PEIBOS(const capd::IMap& i_map, double tf, double dt, const AnalyticFunction& psi_0, const std::vector& Sigma, double epsilon, const Vector& offset, bool verbose = false); /** @@ -84,5 +84,5 @@ namespace codac2 * \return A timed map of reach set parallelepipeds. At each time \f$t\f$, the value is a vector of Parallelepipeds enclosing the reach set at time \f$t\f$. * The function \ref parallelepiped_inclusion is used to compute each Parallelepiped from the PEIBOS CAPD output. */ - std::map> reach_set(const std::map>& peibos_output); + std::map> reach_set(const std::map>& peibos_output); } \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4ea53368d..921be8c24 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,16 +18,30 @@ else() endif() # Adds Catch2::Catch2WithMain + +# Test sources +# ================================================================== + list(APPEND SRC_TESTS # listing files without extension + # ---------------------------------------------------------------- # 3rd - - # Core + # ---------------------------------------------------------------- core/3rd/codac2_tests_eigen + + # ---------------------------------------------------------------- + # Actions + # ---------------------------------------------------------------- + core/actions/codac2_tests_OctaSym + + # ---------------------------------------------------------------- + # Contractors + # ---------------------------------------------------------------- + core/contractors/codac2_tests_CtcAction core/contractors/codac2_tests_CtcCartProd core/contractors/codac2_tests_CtcCtcBoundary @@ -43,10 +57,16 @@ list(APPEND SRC_TESTS # listing files without extension core/contractors/codac2_tests_CtcSegment core/contractors/codac2_tests_CtcVisible core/contractors/codac2_tests_linear_ctc + ../doc/manual/manual/contractors/geometric/src ../doc/manual/manual/contractors/analytic/src ../doc/manual/manual/contractors/set/src + + # ---------------------------------------------------------------- + # Domains + # ---------------------------------------------------------------- + core/domains/codac2_tests_BoolInterval core/domains/ellipsoid/codac2_tests_Ellipsoid core/domains/interval/codac2_tests_Interval @@ -64,15 +84,30 @@ list(APPEND SRC_TESTS # listing files without extension core/domains/tube/codac2_tests_SlicedTube_integral ../doc/manual/manual/tubes/src + + # ---------------------------------------------------------------- + # Functions + # ---------------------------------------------------------------- + core/functions/analytic/codac2_tests_AnalyticFunction ../doc/manual/manual/functions/analytic/src + + # ---------------------------------------------------------------- + # Geometry + # ---------------------------------------------------------------- + core/geometry/codac2_tests_ConvexPolygon core/geometry/codac2_tests_geometry core/geometry/codac2_tests_Polygon core/geometry/codac2_tests_Segment ../doc/manual/manual/geometry/src + + # ---------------------------------------------------------------- + # Matrices + # ---------------------------------------------------------------- + core/matrices/codac2_tests_arithmetic_add core/matrices/codac2_tests_arithmetic_div core/matrices/codac2_tests_arithmetic_mul @@ -86,10 +121,25 @@ list(APPEND SRC_TESTS # listing files without extension core/matrices/codac2_tests_GaussJordan ../doc/manual/manual/linear/src + + # ---------------------------------------------------------------- + # Operators + # ---------------------------------------------------------------- + core/operators/codac2_tests_operators + + # ---------------------------------------------------------------- + # Peibos + # ---------------------------------------------------------------- + core/peibos/codac2_tests_peibos + + # ---------------------------------------------------------------- + # Separators + # ---------------------------------------------------------------- + core/separators/codac2_tests_SepCartProd core/separators/codac2_tests_SepCtcBoundary core/separators/codac2_tests_SepInverse @@ -97,20 +147,39 @@ list(APPEND SRC_TESTS # listing files without extension core/separators/codac2_tests_SepProj core/separators/codac2_tests_SepTransform core/separators/codac2_tests_SepVisible - + + + # ---------------------------------------------------------------- + # Tools + # ---------------------------------------------------------------- + core/tools/codac2_tests_Approx core/tools/codac2_tests_serialization core/tools/codac2_tests_transformations core/tools/codac2_tests_trunc core/tools/ibex/codac2_tests_ibex ../doc/manual/manual/tools/src - + + + # ---------------------------------------------------------------- + # Trajectory + # ---------------------------------------------------------------- + core/trajectory/codac2_tests_AnalyticTraj core/trajectory/codac2_tests_SampledTraj + + # ---------------------------------------------------------------- + # Graphics + # ---------------------------------------------------------------- + graphics/styles/codac2_tests_Color ) + +# Python files required by tests +# ================================================================== + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/core/domains/tube/codac2_tests_predefined_tubes.py # Add here other Python files that are not tests but need to be exported to Python package @@ -120,8 +189,15 @@ file(COPY ${CMAKE_BINARY_DIR}/python/python_package/codac/tests ) + +# Libraries +# ================================================================== + set(CODAC_LIBRARIES ${PROJECT_NAME}-core ${PROJECT_NAME}-graphics) +# Sympy +# ================================================================== + option(BUILD_SYMPY_EMBED_TESTS "Build C++ tests that require embedded Python for Sympy" ON) if(WITH_PYTHON AND DEFINED PYBIND11_FINDPYTHON AND NOT PYBIND11_FINDPYTHON) message(STATUS "Disabling Sympy C++ embed tests because PYBIND11_FINDPYTHON=OFF") @@ -136,7 +212,9 @@ if (WITH_PYTHON AND BUILD_SYMPY_EMBED_TESTS) ) endif() -# CAPD test + +# CAPD +# ================================================================== if (WITH_CAPD) list(APPEND SRC_TESTS extensions/capd/codac2_tests_capd @@ -153,26 +231,218 @@ endif() #message(STATUS "Found IBEX version ${IBEX_VERSION}") #endif() + + +# Common Codac include directories +# ================================================================== +# +# IMPORTANT: +# Use the source tree as the canonical header tree. +# +# The build/include directory contains symbolic links to these +# headers, generated by src/core/CMakeLists.txt. +# +# Therefore there is only one physical copy of each header. + +set(CODAC_CORE_SOURCE_INCLUDE_DIRS + + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/actions + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/contractors + + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/ellipsoid + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/interval + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/interval/eigen + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/affine + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/paving + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/zonotope + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/domains/tube + + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/functions + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/functions/analytic + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/functions/set + + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/geometry + + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/matrices + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/matrices/eigen/Matrix_addons + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/matrices/eigen/MatrixBase_addons + + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/operators + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/paver + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/peibos + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/proj + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/separators + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/tools + ${CMAKE_CURRENT_SOURCE_DIR}/../src/core/trajectory +) + + +set( + CODAC_HEADERS_DIR + ${CMAKE_CURRENT_BINARY_DIR}/../include +) + + + +# ------------------------------------------------------------------ +# Sanitizer runtime preload for Python tests (Debug builds only) +# ------------------------------------------------------------------ +# +# In Debug mode, _core.so (the pybind11 module) is compiled with +# -fsanitize=address,undefined -shared-libsan (see src/core/CMakeLists.txt), +# which makes it depend on Clang's shared sanitizer runtime instead of +# embedding it statically. Since python3 itself has no sanitizer runtime, +# that shared runtime must be preloaded before Python imports codac, +# otherwise symbols like __ubsan_vptr_type_cache stay unresolved. + +set(PYTHON_TEST_ENV_ARGS "PYTHONPATH=${PYTHON_PACKAGE_DIR}") + +if(WITH_PYTHON AND CMAKE_BUILD_TYPE STREQUAL "Debug" + AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") + + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} --print-runtime-dir + OUTPUT_VARIABLE CLANG_RT_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(EXISTS "${CLANG_RT_DIR}") + file(GLOB SANITIZER_RT_LIBS + "${CLANG_RT_DIR}/libclang_rt.asan-x86_64.so" + "${CLANG_RT_DIR}/libclang_rt.ubsan_standalone-x86_64.so" + ) + endif() + + if(SANITIZER_RT_LIBS) + list(JOIN SANITIZER_RT_LIBS ":" SANITIZER_RT_LIBS_JOINED) + list(APPEND PYTHON_TEST_ENV_ARGS "LD_PRELOAD=${SANITIZER_RT_LIBS_JOINED}") + + # LeakSanitizer (part of ASan) flags many long-lived, process-lifetime + # allocations made by the Python interpreter and by pybind11 module + # init (interned strings, type objects, strdup'd docstrings...) as + # "leaks", since it can't trace Python's own object graph. This is a + # well-known false-positive source for embedded Python interpreters, + # not something codac's code controls. UBSan and ASan's other checks + # (use-after-free, buffer overflow, UB...) remain fully active. + list(APPEND PYTHON_TEST_ENV_ARGS "ASAN_OPTIONS=detect_leaks=0") + list(APPEND PYTHON_TEST_ENV_ARGS "UBSAN_OPTIONS=print_stacktrace=1") + message(STATUS "Python tests: LD_PRELOAD=${SANITIZER_RT_LIBS_JOINED}") + else() + message(WARNING + "Debug build with WITH_PYTHON: no shared ASan/UBSan runtime found " + "in '${CLANG_RT_DIR}'. Make sure the _core target is linked with " + "-shared-libsan, otherwise Python tests will fail with " + "'undefined symbol' errors.") + endif() + +endif() + +# Build every test +# ================================================================== + foreach(SRC_TEST ${SRC_TESTS}) string(REPLACE "/" "_" TEST_NAME ${SRC_TEST}) string(REPLACE "codac2_tests_" "" TEST_NAME ${TEST_NAME}) set(TEST_NAME codac2_tests_${TEST_NAME}) + + # --------------------------------------------------------------- # C++ test - add_executable(${TEST_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_TEST}.cpp) - set(CODAC_HEADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/../include) - target_include_directories(${TEST_NAME} SYSTEM PUBLIC ${CODAC_HEADERS_DIR}) - target_link_libraries(${TEST_NAME} PUBLIC Ibex::ibex ${CODAC_LIBRARIES} PRIVATE Catch2::Catch2WithMain) + # --------------------------------------------------------------- + + add_executable( + ${TEST_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_TEST}.cpp + ) + + + # Use the same canonical source headers as codac-core. + # + # CODAC_HEADERS_DIR is kept as well because it contains generated + # umbrella headers such as codac-core.h. + # set(CODAC_HEADERS_DIR ${CMAKE_CURRENT_BINARY_DIR}/../include) + + target_include_directories( + ${TEST_NAME} + SYSTEM PUBLIC + + ${CODAC_HEADERS_DIR} + + ${CODAC_CORE_SOURCE_INCLUDE_DIRS} + ) + + + target_link_libraries( + ${TEST_NAME} + PUBLIC + + Ibex::ibex + ${CODAC_LIBRARIES} + + PRIVATE + + Catch2::Catch2WithMain + ) + + + # Sympy embedded tests + if( + WITH_PYTHON + AND SRC_TEST MATCHES "extensions/sympy/" + ) + + target_link_libraries( + ${TEST_NAME} + PRIVATE + + ${PROJECT_NAME}-sympy + pybind11::embed + ) - if(WITH_PYTHON AND SRC_TEST MATCHES "extensions/sympy/") - target_link_libraries(${TEST_NAME} PRIVATE ${PROJECT_NAME}-sympy pybind11::embed) endif() - add_dependencies(check ${TEST_NAME}) - add_test(NAME ${TEST_NAME}_cpp COMMAND ${TEST_NAME}) + + # C++20 + set_target_properties( + ${TEST_NAME} + PROPERTIES + + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + CXX_EXTENSIONS OFF + ) + + + add_dependencies( + check + ${TEST_NAME} + ) + + add_test( + NAME ${TEST_NAME}_cpp + COMMAND ${TEST_NAME} + ) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set_tests_properties(${TEST_NAME}_cpp PROPERTIES + ENVIRONMENT "UBSAN_OPTIONS=print_stacktrace=1" + ) + endif() + + # --------------------------------------------------------------- # Python test - if(WITH_PYTHON) - add_test(NAME ${TEST_NAME}_py COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_TEST}.py) + # --------------------------------------------------------------- + + if(WITH_PYTHON AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SRC_TEST}.py") + add_test( + NAME ${TEST_NAME}_py + COMMAND + ${CMAKE_COMMAND} -E env + ${PYTHON_TEST_ENV_ARGS} + ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/${SRC_TEST}.py + ) endif() endforeach() diff --git a/tests/core/contractors/codac2_tests_CtcInverse.cpp b/tests/core/contractors/codac2_tests_CtcInverse.cpp index 952b8a57c..627a4f934 100644 --- a/tests/core/contractors/codac2_tests_CtcInverse.cpp +++ b/tests/core/contractors/codac2_tests_CtcInverse.cpp @@ -19,6 +19,63 @@ using namespace std; using namespace codac2; + +// ============================================================================ +// 1. Paving: tree lifecycle and parent/child reference-counting regression +// ============================================================================ + +TEST_CASE("Paving tree structure after bisection", "[Paving]") +{ + PavingOut p(IntervalVector({{-1.,1.},{-1.,1.}})); + auto root = p.tree(); + + REQUIRE(root->is_leaf()); + CHECK(root->top() == nullptr); + + root->bisect(); + REQUIRE_FALSE(root->is_leaf()); + REQUIRE(root->left() != nullptr); + REQUIRE(root->right() != nullptr); + CHECK(root->left()->top() == root); + CHECK(root->right()->top() == root); + CHECK(root->left()->is_leaf()); + CHECK(root->right()->is_leaf()); + + root->left()->bisect(); + REQUIRE(root->left()->left() != nullptr); + CHECK(root->left()->left()->top() == root->left()); +} + +TEST_CASE("Paving nodes do not leak through a parent/child reference cycle", "[Paving][regression]") +{ + std::weak_ptr w_root, w_left, w_left_left; + + { + PavingOut p(IntervalVector({{-1.,1.},{-1.,1.}})); + auto root = p.tree(); + root->bisect(); + root->left()->bisect(); + + w_root = root; + w_left = root->left(); + w_left_left = root->left()->left(); + + // Sanity check: while 'p' (and so the whole tree) is alive, every + // node must still resolve through its weak_ptr. + REQUIRE_FALSE(w_root.expired()); + REQUIRE_FALSE(w_left.expired()); + REQUIRE_FALSE(w_left_left.expired()); + } + // 'p' is destroyed here. Before the fix, every node below the root + // would still be kept alive by its child's (owning) _top pointing back + // up through the cycle, so none of the weak_ptr's below would expire. + + CHECK(w_root.expired()); + CHECK(w_left.expired()); + CHECK(w_left_left.expired()); +} + + TEST_CASE("CtcInverse") { { diff --git a/tests/core/domains/interval/codac2_tests_IntervalMatrix.cpp b/tests/core/domains/interval/codac2_tests_IntervalMatrix.cpp index f54c0825f..491cb0354 100644 --- a/tests/core/domains/interval/codac2_tests_IntervalMatrix.cpp +++ b/tests/core/domains/interval/codac2_tests_IntervalMatrix.cpp @@ -304,7 +304,7 @@ TEST_CASE("IntervalMatrix") IntervalVector r2({{-1,0},{-2,0}}); m1.row(0) = r1.transpose().eval(); m1.row(1) = r2.transpose().eval(); - m1.resize_save_values(2,3); + m1.conservativeResize(2,3); m1(0,2) = Interval(0,3); m1(1,2) = Interval(-3,0); @@ -315,7 +315,7 @@ TEST_CASE("IntervalMatrix") IntervalMatrix m1(1,3); IntervalVector r1({{0,1},{0,2},{0,3}}); m1.row(0) = r1.transpose().eval(); - m1.resize_save_values(2,3); + m1.conservativeResize(2,3); m1(1,0) = Interval(-1,0); m1(1,1) = Interval(-2,0); m1(1,2) = Interval(-3,0); @@ -325,7 +325,7 @@ TEST_CASE("IntervalMatrix") { IntervalMatrix e(IntervalMatrix::empty(1,1)); - e.resize_save_values(2,3); + e.conservativeResize(2,3); CHECK(e.is_empty()); } diff --git a/tests/core/domains/interval/codac2_tests_IntervalMatrix.py b/tests/core/domains/interval/codac2_tests_IntervalMatrix.py index f68cc037b..6af49f47b 100644 --- a/tests/core/domains/interval/codac2_tests_IntervalMatrix.py +++ b/tests/core/domains/interval/codac2_tests_IntervalMatrix.py @@ -262,7 +262,7 @@ def test_intervalmatrix(self): r2 = IntervalVector([[-1,0],[-2,0]]) m1.set_row(0,r1.transpose()) m1.set_row(1,r2.transpose()) - m1.resize_save_values(2,3) + m1.conservativeResize(2,3) m1[0,2] = Interval(0,3) m1[1,2] = Interval(-3,0) @@ -271,7 +271,7 @@ def test_intervalmatrix(self): m1 = IntervalMatrix(1,3) r1 = IntervalVector([[0,1],[0,2],[0,3]]) m1.set_row(0,r1.transpose()) - m1.resize_save_values(2,3) + m1.conservativeResize(2,3) m1[1,0] = Interval(-1,0) m1[1,1] = Interval(-2,0) m1[1,2] = Interval(-3,0) @@ -279,7 +279,7 @@ def test_intervalmatrix(self): self.assertTrue(m1 == self.M1()) e = IntervalMatrix.empty(1,1) - e.resize_save_values(2,3) + e.conservativeResize(2,3) self.assertTrue(e.is_empty()) m1 = IntervalMatrix(self.M1()) diff --git a/tests/core/domains/interval/codac2_tests_IntervalVector.cpp b/tests/core/domains/interval/codac2_tests_IntervalVector.cpp index 04e4be366..738e5fa60 100644 --- a/tests/core/domains/interval/codac2_tests_IntervalVector.cpp +++ b/tests/core/domains/interval/codac2_tests_IntervalVector.cpp @@ -112,7 +112,7 @@ TEST_CASE("IntervalVector") { IntervalVector x(1); x[0] = Interval(1,2); - x.resize_save_values(3); + x.conservativeResize(3); CHECK(x.size() == 3); CHECK(x[0] == Interval(1,2)); CHECK(x[1] == Interval(-oo,oo)); @@ -122,7 +122,7 @@ TEST_CASE("IntervalVector") { IntervalVector x(1); x[0] = Interval(1,2); - x.resize_save_values(1); + x.conservativeResize(1); CHECK(x.size() == 1); CHECK(x[0] == Interval(1,2)); } @@ -131,7 +131,7 @@ TEST_CASE("IntervalVector") IntervalVector x(2); x[0] = Interval(1,2); x.set_empty(); - x.resize_save_values(3); + x.conservativeResize(3); CHECK(x.size() == 3); CHECK(x.is_empty()); CHECK(x[2] == Interval(-oo,oo)); @@ -141,7 +141,7 @@ TEST_CASE("IntervalVector") IntervalVector x(5); x[0] = Interval(1,2); x[1] = Interval(3,4); - x.resize_save_values(2); + x.conservativeResize(2); CHECK(x.size() == 2); CHECK(x[0] == Interval(1,2)); CHECK(x[1] == Interval(3,4)); diff --git a/tests/core/domains/interval/codac2_tests_IntervalVector.py b/tests/core/domains/interval/codac2_tests_IntervalVector.py index 168b4254a..05d22f692 100644 --- a/tests/core/domains/interval/codac2_tests_IntervalVector.py +++ b/tests/core/domains/interval/codac2_tests_IntervalVector.py @@ -92,7 +92,7 @@ def test_intervalvector(self): x = IntervalVector(1) x[0] = Interval(1,2) - x.resize_save_values(3) + x.conservativeResize(3) self.assertTrue(x.size() == 3) self.assertTrue(x[0] == Interval(1,2)) self.assertTrue(x[1] == Interval(-oo,oo)) @@ -100,14 +100,14 @@ def test_intervalvector(self): x = IntervalVector(1) x[0] = Interval(1,2) - x.resize_save_values(1) + x.conservativeResize(1) self.assertTrue(x.size() == 1) self.assertTrue(x[0] == Interval(1,2)) x = IntervalVector(2) x[0] = Interval(1,2) x.set_empty() - x.resize_save_values(3) + x.conservativeResize(3) self.assertTrue(x.size() == 3) self.assertTrue(x.is_empty()) self.assertTrue(x[2] == Interval(-oo,oo)) @@ -115,7 +115,7 @@ def test_intervalvector(self): x = IntervalVector(5) x[0] = Interval(1,2) x[1] = Interval(3,4) - x.resize_save_values(2) + x.conservativeResize(2) self.assertTrue(x.size() == 2) self.assertTrue(x[0] == Interval(1,2)) self.assertTrue(x[1] == Interval(3,4)) diff --git a/tests/core/domains/tube/codac2_tests_SlicedTube_integral.cpp b/tests/core/domains/tube/codac2_tests_SlicedTube_integral.cpp index 4cba879ab..c5bd5294b 100644 --- a/tests/core/domains/tube/codac2_tests_SlicedTube_integral.cpp +++ b/tests/core/domains/tube/codac2_tests_SlicedTube_integral.cpp @@ -151,8 +151,8 @@ TEST_CASE("Computing integration from 0, interval argument") CHECK(Approx(x.integral(Interval(12.5))) == Interval(6.5,20.5)); CHECK(Approx(x.integral(Interval(14.5))) == Interval(7,23.5)); auto p_intv = x.partial_integral(Interval(12.5,14.5)); - CHECK(p_intv.first == Interval(6.,7.)); - CHECK(p_intv.second == Interval(20.5,23.5)); + CHECK(Approx(p_intv.first) == Interval(6.,7.)); + CHECK(Approx(p_intv.second) == Interval(20.5,23.5)); CHECK(Approx(x.integral(Interval(12.5,14.5))) == Interval(6.0,23.5)); CHECK(Approx(x.integral(Interval(0))) == Interval(0)); CHECK(Approx(x.integral(Interval(10.2))) == Interval(9.3,19.7)); diff --git a/tests/core/operators/codac2_tests_operators.cpp b/tests/core/operators/codac2_tests_operators.cpp index 029463b82..b0de4fd13 100644 --- a/tests/core/operators/codac2_tests_operators.cpp +++ b/tests/core/operators/codac2_tests_operators.cpp @@ -22,7 +22,7 @@ using namespace std; using namespace codac2; -const double MAX_DOUBLE = std::numeric_limits::max(); +//const double MAX_DOUBLE = std::numeric_limits::max(); void CHECK_bwd_trigo(const Interval& y, const Interval& x, const Interval& expected_x) {