diff --git a/CMakeLists.txt b/CMakeLists.txt index f65251466..cb078228f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,26 @@ #set(CMAKE_C_COMPILER "gcc-7") #set(CMAKE_CXX_COMPILER "/usr/bin/g++-7") - project(codac VERSION ${VERSION} LANGUAGES CXX) + # CMP0091 (CMake >=3.15) makes the CMAKE_MSVC_RUNTIME_LIBRARY variable set + # below actually control MSVC's runtime library selection, instead of + # CMake's older behavior of baking a hardcoded /MD.../MDd into + # CMAKE__FLAGS_. It must be set to NEW before project() -- + # afterwards is too late -- for that variable to have any effect at all. + if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) + endif() + + # C as well as C++: the codac sources are all C++, but mathlib, which GAOL + # depends on, is C, and codac_gaol_build() builds it when no GAOL is installed + # (see scripts/CMakeModules/codac_gaol.cmake). Enabled here, the C compiler + # and its flags are settled -- and cached -- by the first configuration, so + # that what is handed to that build is the same at every later one. Enabled + # further down by a dependency instead, as happened when only CXX was listed, + # the compiler went from the name it was given to its full path between the + # first configuration and the second, the build of GAOL took that for a + # change and was configured again, and everything in codac that includes an + # interval was recompiled. + project(codac VERSION ${VERSION} LANGUAGES C CXX) if(NOT VERSION_ID) set(PROJECT_VERSION_FULL ${PROJECT_VERSION}) @@ -84,6 +103,30 @@ add_compile_options(-Wall -Wextra -Wpedantic) endif() + if(MSVC) + # The gaol interval library is only ever linked as a Release binary: + # codac_gaol_build() (scripts/CMakeModules/codac_gaol.cmake) builds it in + # Release whatever the configuration of codac, and a gaol found on the + # system comes prebuilt that way too. gaol.lib is thus compiled with the + # Release CRT (/MD). MSVC's linker refuses + # to mix object files built against different CRT variants in the same + # binary -- linking that Release-CRT gaol.lib together with codac's own + # object files, which without this override would default to the Debug + # CRT (/MDd) in a Debug build, fails with error LNK2038 ("mismatch + # detected for 'RuntimeLibrary'/'_ITERATOR_DEBUG_LEVEL'") followed by + # fatal error LNK1319. + # + # Forcing every configuration onto the Release CRT keeps codac's own + # Debug build link-compatible with that Release gaol.lib without + # having to build gaol in Debug as well. Release + # itself already defaulted to /MD, so in practice this only changes + # what the Debug configuration uses. Debug builds still get no + # optimization (/Od) and full debug symbols (/Zi) -- what is traded away + # is only the *separate* debug-CRT extras that /MDd would otherwise add + # on top of that (its own heap debugging and iterator-debug-level checks). + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") + endif() + # Temporary attempts to fix errors similar to: # _ number of sections exceeded object file format limit. # _ out of memory allocating XXX bytes. @@ -118,12 +161,47 @@ ################################################################################ -# Looking for IBEX +# Looking for GAOL ################################################################################ - find_package(IBEX REQUIRED) - ibex_init_common() # IBEX should have installed this function - message(STATUS "Found IBEX version ${IBEX_VERSION}") + # GAOL is the interval arithmetic library codac2::Interval is built upon, and + # mathlib (libultim) the library GAOL computes its elementary functions with. + # Both used to come from IBEX, which bundles them, and they were all Codac + # needed IBEX for: they are now found, or built, by Codac itself. What comes + # from IBEX in doing so, and who wrote it, is said at the top of + # scripts/CMakeModules/codac_gaol.cmake and of FindGAOL.cmake next to it. + include(codac_gaol) + + # GAOL, found or built, as the imported target Codac::gaol: its include + # directories, its libraries, and the flags floating-point rounding depends + # on, which ibex_init_common() used to put on the command line -- without + # -frounding-math and its companions, the compiler is free to constant-fold in + # the wrong rounding mode, and an interval is no longer a bound. They are + # GAOL's to say: codac_gaol_find() takes them from the CMake package of GAOL + # or from its gaol.pc, and determines them itself only for a GAOL found by its + # files alone. The Codac libraries link Codac::gaol PUBLIC, so that every + # target linking them is compiled with those flags -- the tests, the examples + # and the Python bindings included. This comes before Codac's own flags are + # added to CMAKE_CXX_FLAGS below: a GAOL built by codac_gaol_build() is handed + # CMAKE_CXX_FLAGS as it is given to this build, and chooses its flags itself. + option(ENABLE_FIND_PACKAGE_GAOL "ENABLE_FIND_PACKAGE_GAOL" ON) + codac_gaol_find() + + # Consumers need those flags as much as this build does. What Codac::gaol + # gives is kept as lists, handed over through CODAC_CXX_FLAGS in + # codac-config.cmake, and through the Cflags and Libs lines of codac.pc, which + # cannot link a target (see src/CMakeLists.txt). + codac_gaol_usage(CODAC_GAOL_INCLUDE_DIRS CODAC_INTERVAL_CXX_FLAGS CODAC_GAOL_LINK_ITEMS) + message(STATUS "Interval arithmetic flags: ${CODAC_INTERVAL_CXX_FLAGS}") + message(STATUS "GAOL include directories: ${CODAC_GAOL_INCLUDE_DIRS}") + message(STATUS "GAOL libraries: ${CODAC_GAOL_LINK_ITEMS}") + + # The flags of ibex_init_common() that are not those of interval arithmetic, + # where ibex_init_common() put them, in CMAKE_CXX_FLAGS, so that every target + # is compiled with them; handed over to consumers with the others. + codac_gaol_portability_flags(CODAC_PORTABILITY_CXX_FLAGS) + string(REPLACE ";" " " _codac_portability_cxx_flags "${CODAC_PORTABILITY_CXX_FLAGS}") + string(APPEND CMAKE_CXX_FLAGS " ${_codac_portability_cxx_flags}") ################################################################################ diff --git a/doc/manual/development/info_dev.rst b/doc/manual/development/info_dev.rst index e99bf5a24..4f18a4548 100644 --- a/doc/manual/development/info_dev.rst +++ b/doc/manual/development/info_dev.rst @@ -40,7 +40,7 @@ If you simply want to use the latest Codac release in Python, you can download t .. code-block:: bash - sudo apt-get install -y g++ gcc cmake git flex bison + sudo apt-get install -y g++ gcc cmake git - a supported version of Python (>=3.8). - a recent `Doxygen `_ version (for instance, release 1.16.1 or newest). On Linux systems, latest releases are not available as Debian packages, so we advise to install Doxygen from the sources: @@ -60,24 +60,7 @@ If you simply want to use the latest Codac release in Python, you can download t Doxygen software extracts C++ documentation from header files into XML format. We then convert this data into docstring format before embedding it into the binding binaries. In this way, the writing of the documentation is centralized in a single location in the C++ header files. -2. **Configure IBEX prior to compiling Codac**: - - We recall that IBEX sources can be obtained with: - - .. code-block:: bash - - git clone https://github.com/lebarsfa/ibex-lib.git $HOME/ibex-lib - cd $HOME/ibex-lib - - You will need to compile both IBEX and Codac using the ``-fPIC`` options. This can be done with the following CMake configuration: - - .. code-block:: bash - - mkdir build ; cd build - cmake -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Release .. - make ; make install - -3. **Compile Codac with Python binding**: +2. **Compile Codac with Python binding**: We recall that Codac sources can be obtained with: @@ -96,15 +79,15 @@ If you simply want to use the latest Codac release in Python, you can download t Note that you will then have to ``import codac2`` instead of ``import codac`` in your Python scripts. - In addition to the ``-fPIC`` options, you will have to configure ``WITH_PYTHON=ON`` and ``PYBIND11_FINDPYTHON=OFF``. Note that CMake will automatically get the `pybind11 `_ files required for the binding. Also, you will have to configure ``BUILD_TESTS=ON`` if you want to run the unit tests. + You will need to compile Codac using the ``-fPIC`` options (a GAOL built by CMake along with Codac is compiled that way on its own; a GAOL installed on your system has to have been as well), and to configure ``WITH_PYTHON=ON`` and ``PYBIND11_FINDPYTHON=OFF``. Note that CMake will automatically get the `pybind11 `_ files required for the binding. Also, you will have to configure ``BUILD_TESTS=ON`` if you want to run the unit tests. .. code-block:: bash mkdir build ; cd build - cmake -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DWITH_PYTHON=ON -DPYBIND11_FINDPYTHON=OFF -DBUILD_TESTS=ON -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH="$HOME/ibex-lib/build_install;$HOME/doxygen/build_install" -DCMAKE_BUILD_TYPE=Release .. + cmake -DCMAKE_CXX_FLAGS="-fPIC" -DCMAKE_C_FLAGS="-fPIC" -DWITH_PYTHON=ON -DPYBIND11_FINDPYTHON=OFF -DBUILD_TESTS=ON -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH="$HOME/doxygen/build_install" -DCMAKE_BUILD_TYPE=Release .. make ; make install -4. **Configure your Python environment**: +3. **Configure your Python environment**: Finally, you need to configure your system so that Python can find access to your Codac binding binaries: @@ -119,7 +102,7 @@ If you simply want to use the latest Codac release in Python, you can download t export PYTHONPATH="${PYTHONPATH}:$HOME/codac/build/python/python_package/" -5. **Verify the installation** (optional): +4. **Verify the installation** (optional): To ensure that the installation has worked properly, the unit tests of the library can be run: @@ -127,7 +110,7 @@ If you simply want to use the latest Codac release in Python, you can download t python -m unittest discover codac.tests -6. **Try an example** (optional): +5. **Try an example** (optional): You may want to try Codac in Python by running one of the proposed examples. After the installation, you can run the following commands: diff --git a/doc/manual/manual/contractors/dynamic/index.rst b/doc/manual/manual/contractors/dynamic/index.rst new file mode 100644 index 000000000..397c8c488 --- /dev/null +++ b/doc/manual/manual/contractors/dynamic/index.rst @@ -0,0 +1,12 @@ +:orphan: + +Dynamic contractors +=================== + +.. toctree:: + + ctclohner.rst + CtcDeriv + CtcEval + CtcPicard + CtcDiffInclusion diff --git a/doc/manual/manual/contractors/index.rst b/doc/manual/manual/contractors/index.rst index 9b2537821..989543dad 100644 --- a/doc/manual/manual/contractors/index.rst +++ b/doc/manual/manual/contractors/index.rst @@ -21,6 +21,7 @@ Contractors, separators .. analytic/index.rst .. geometric/index.rst .. shape/index.rst +.. dynamic/index.rst .. temporal/index.rst diff --git a/doc/manual/manual/extensions/capd/capd.rst b/doc/manual/manual/extensions/capd/capd.rst index 590fe4c98..20569f5e3 100644 --- a/doc/manual/manual/extensions/capd/capd.rst +++ b/doc/manual/manual/extensions/capd/capd.rst @@ -20,7 +20,7 @@ To install the ``codac-capd`` extension, you need to install the Codac library f .. code-block:: bash - cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Release -DWITH_CAPD=ON .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_BUILD_TYPE=Release -DWITH_CAPD=ON .. We highly recommend to test the installation of the library with the provided tests. To do so, you can use the following command: @@ -61,7 +61,6 @@ Furthermore, you need to link the extension to your project, for instance by upd ${CODAC_LIBRARIES} ${CODAC_CAPD_LIBRARY} # linking to the codac-capd extension capd::capd # linking to CAPD - Ibex::ibex ) You can use the functions ``to_capd`` and ``to_codac`` to convert between CAPD and Codac objects as follows: diff --git a/doc/manual/manual/extensions/sympy/index.rst b/doc/manual/manual/extensions/sympy/index.rst index c1e6b890c..e26be00f4 100644 --- a/doc/manual/manual/extensions/sympy/index.rst +++ b/doc/manual/manual/extensions/sympy/index.rst @@ -54,7 +54,6 @@ In C++ however, you need to link the extension to your project, for instance by ${CODAC_LIBRARIES} ${CODAC_SYMPY_LIBRARY} # linking to the codac-sympy extension pybind11::embed # linking to pybind11 - Ibex::ibex ) Finally, in order to include the features of the extension: diff --git a/doc/manual/manual/installation/cpp.rst b/doc/manual/manual/installation/cpp.rst index d92dc75b9..6cce8a6b1 100644 --- a/doc/manual/manual/installation/cpp.rst +++ b/doc/manual/manual/installation/cpp.rst @@ -39,7 +39,7 @@ Linux Installation .. .. code-block:: bash -.. sudo apt remove libcodac-dev libibex-dev +.. sudo apt remove libcodac-dev .. sudo rm -f /etc/apt/sources.list.d/ensta-bretagne.list .. sudo apt update @@ -74,63 +74,46 @@ Steps sudo apt-get install -y build-essential cmake git -2. **Install the IBEX dependency**: - - Codac still uses some features of the `IBEX library `_ that you have to install first (currently, the only thing Codac uses from IBEX is a wrapper of the `GAOL library `_). The last version of IBEX is maintained on `this unofficial development repository `_: - - .. code-block:: bash + .. admonition:: The Eigen dependency - # Requirements to compile IBEX - sudo apt-get install -y flex bison + | Codac is built on `Eigen `_ (version 3.4 or newer), but you do not have to install it: by default the CMake configuration downloads and builds the version Codac is tested against, so nothing has to be added to the command above. + | To build against an Eigen already installed on your system instead, configure Codac with ``-DENABLE_FIND_PACKAGE_EIGEN3=ON``; CMake then looks for it with ``find_package(Eigen3 3.4.0)``, and ``Eigen3_DIR`` or ``CMAKE_PREFIX_PATH`` can point at a custom installation path. This is what the ``choco install eigen`` of the Windows instructions below provides. Note that the Eigen headers are part of Codac's public interface, so a program using Codac compiles them too: this is why they are installed next to Codac's own headers, and why :ref:`the configuration of a user project ` lists them among its include directories. - # Download IBEX sources from GitHub - git clone -b master https://github.com/lebarsfa/ibex-lib.git $HOME/ibex-lib + .. admonition:: The GAOL dependency - # Configure IBEX before installation - cd $HOME/ibex-lib - mkdir build ; cd build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Release .. + | The intervals of Codac are built upon `GAOL `_, the interval arithmetic library written by `Frédéric Goualard `_, which computes its elementary functions with the IBM Accurate Portable Mathematical Library (mathlib). You do not have to install them either: CMake first looks for a GAOL installed on your system and, when it finds none, downloads GAOL from the master branch of `the fork of Jordan Ninin `_ while Codac is configured (brought up to date at each configuration when Git is installed), builds it with its CMake build, which downloads mathlib from `Frédéric Goualard's site `_, and installs both with its CMake installer, in the build directory and along with Codac. The fork adds to GAOL a CMake build, taken from the one of `IBEX `_ (which Codac used to depend on, and no longer does), the changes Codac depends on or which Visual Studio, MinGW and ARM processors need, and tests of the bounds it computes. Its README lists and explains them. + | If you install GAOL yourself, install the version of `the fork of Jordan Ninin `_, 4.3.2 or later: Codac does not use the original sources, of an older version, which lack the fixes it relies on. The fork comes with the CMake installer, which builds and installs GAOL and mathlib together (``cmake -S . -B build -DCMAKE_INSTALL_PREFIX=``, ``cmake --build build --config Release``, then ``cmake --install build --config Release``), with the CMake package from which Codac takes the compilation flags and the libraries GAOL needs, while the original sources only have autotools and meson builds. Bugs of GAOL are also fixed there: bounds that did not enclose the exact results (numbers such as ``interval("0.1")`` read with the C runtime of Windows or with musl on 64-bit ARM processors; hyperbolic functions with the libm of glibc 2.31, musl or MinGW-w64; square roots with Visual C++ for 32-bit x86), and powers with a real exponent (``pow([4], 0.5)`` returned ``[1]``, ``pow([-4,-1], [0.5])`` returned ``[-1, 2]``). + | CMake looks for an installed GAOL in three ways, in this order: its CMake package (``gaolConfig.cmake``, which the CMake build of the fork installs), its ``gaol.pc`` through ``pkg-config`` (which the autotools and meson builds of the fork install), then its files (``gaol/gaol.h``, ``MathLib.h`` and the ``gaol`` and ``ultim`` libraries). Codac is compiled with the flags and linked with the libraries the package or ``gaol.pc`` of GAOL gives; only for a GAOL found by its files does Codac determine the flags itself. A ``gaol.pc`` whose flags lack ``-frounding-math``, as the one of the meson build of the original sources, is not used. Nor is a GAOL older than version 4.3.2, the first version of the fork with all the fixes Codac relies on (the last one: the intersection of disjoint intervals is the empty set), or whose version cannot be told: CMake says so, and builds the fork instead. The projects using the installed Codac need GAOL 4.3.2 or later too, since they compile the interval operations of Codac, inline in its headers, against their GAOL. To use a GAOL installed in a custom location, add its installation prefix to ``CMAKE_PREFIX_PATH``, which the three searches read, or give ``-Dgaol_DIR=/lib/cmake/gaol`` for its CMake package, ``PKG_CONFIG_PATH`` for its ``gaol.pc``, or ``-DGAOL_DIR=`` (and ``-DMATHLIB_DIR=`` for mathlib, if it is installed elsewhere) for its files. To build the GAOL Codac is tested against even where another one is installed, configure Codac with ``-DENABLE_FIND_PACKAGE_GAOL=OFF``. + | On a 32-bit x86 processor, Codac, GAOL and mathlib are compiled with ``-msse2 -mfpmath=sse``, except by Visual Studio, which computes in SSE2 already: computed on the x87 FPU, GAOL's bounds and mathlib's results are only right while its precision stays set to 53 bits, which nothing guarantees. A processor with SSE2 is therefore required there. GAOL is not built with the compilers that do not compute its intervals right, or much too slowly, and its build stops with a message naming the ones to use instead: Clang for 32-bit ARM processors (use GCC), the compilers that say they do not honour the rounding direction, such as Clang 14 for 64-bit ARM processors, and the mingw-w64 runtimes older than version 13 (those of the MinGW-w64 GCC 11 to 14 of Chocolatey, for instance): before version 12, their math library is not accurate enough, and the ``fesetround()`` of version 12 makes the elementary functions of GAOL some 20 times slower. - # Building + installing - make - make install - cd ../.. - - For further CMake options, please refer to the IBEX documentation. - - .. warning:: - - **GAOL prerequisite:** On some platforms, you might need to install manually `MathLib `_ and `GAOL `_ with CMake and `specify where they are `_ in order to build IBEX successfully and have accurate computations. - -3. **Install the Codac library**: +2. **Install the Codac library**: .. code-block:: bash - # The codac directory can be placed in your home, same level as IBEX + # The codac directory can be placed in your home git clone https://github.com/codac-team/codac $HOME/codac # Configure Codac before installation cd $HOME/codac mkdir build ; cd build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_PREFIX_PATH=$HOME/ibex-lib/build_install -DCMAKE_BUILD_TYPE=Release .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/codac/build_install -DCMAKE_BUILD_TYPE=Release .. # Building + installing make make install cd ../.. -4. **Configure your system to find Codac**: +3. **Configure your system to find Codac**: In case Codac and its dependencies have been installed locally on your system, you will have to configure your environment variables. This can be done temporarily with: .. code-block:: bash - export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/ibex-lib/build_install export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install ... or permanently by updating your ``.bashrc`` file by appending the above commands. -5. **Verify the installation** (optional): +4. **Verify the installation** (optional): To ensure that the installation has worked properly, the unit tests of the library can be run. For this, you have to configure CMake using the ``-DBUILD_TESTS=ON`` option, before compilation. Then, from the ``$HOME/codac/build`` directory: @@ -138,7 +121,7 @@ Steps make test -6. **Try an example** (optional): +5. **Try an example** (optional): You may want to try Codac by running one of the proposed examples. After the installation, you can run the following commands: @@ -157,6 +140,13 @@ Steps You should obtain a graphical output corresponding to a curious set inversion. +6. **Start your own project**: + + ``examples/01_batman/CMakeLists.txt`` is also the shortest possible template + for a project of your own. :ref:`sec-start-cpp-project` goes through it line + by line, says which paths CMake needs and where it looks for them, and covers + the ``pkg-config`` alternative. + Windows Installation -------------------- @@ -169,7 +159,7 @@ Using MinGW .. Check https://community.chocolatey.org/packages/codac. -Install `Chocolatey package manager `_, run `choco install -y ibex cmake make qtcreator` in PowerShell and then download and extract *e.g.* ``codac_standalone_x64_mingw13.zip`` (for MinGW 13) from https://github.com/codac-team/codac/releases/latest, launch Qt Creator and choose Open Project, open ``example\CMakelists.txt``, ensure Desktop is selected and click Configure Project (might be hidden behind notifications at the bottom-right), wait 10 s then click on the big bottom-left green Run button, and finally check that the graphical output appears. +Install `Chocolatey package manager `_, run `choco install -y cmake make qtcreator` in PowerShell and then download and extract *e.g.* ``codac_standalone_x64_mingw15.zip`` (for MinGW 15) from https://github.com/codac-team/codac/releases/latest, launch Qt Creator and choose Open Project, open ``example\CMakelists.txt``, ensure Desktop is selected and click Configure Project (might be hidden behind notifications at the bottom-right), wait 10 s then click on the big bottom-left green Run button, and finally check that the graphical output appears. Note that in order to obtain graphical outputs, you will have to download and run https://github.com/ENSTABretagneRobotics/VIBES/releases/latest/download/VIBes-viewer_x86.exe before running the project. @@ -189,10 +179,10 @@ You will probably need to install these prerequisites (assuming you already inst .. code-block:: bash - choco install cmake git make patch winflexbison + choco install cmake git make choco install eigen -Then, install the desired compiler (*e.g.* ``choco install mingw --version=11.2.0.07112021``). +Then, install the desired compiler (*e.g.* ``choco install mingw --version=15.2.0``; MinGW-w64 older than version 13, which the MinGW-w64 GCC 11 to 14 packages come with, is not supported). Optionally, for Python binding (*e.g.* ``choco install python --version=3.10.4``) and documentation: @@ -259,7 +249,7 @@ Optionally, for Python binding and documentation: wget https://github.com/Homebrew/homebrew-core/raw/d2267b9f2ad247bc9c8273eb755b39566a474a70/Formula/doxygen.rb ; brew reinstall ./doxygen.rb ; brew pin doxygen brew install graphviz python -m pip install --upgrade pip - pip install --upgrade wheel setuptools sphinx sphinx_rtd_theme furo sphinx-math-dollar sphinx_tabs sphinx_togglebutton + pip install --upgrade wheel setuptools sphinx sphinx_rtd_theme furo sphinx-math-dollar sphinx_tabs breathe sphinx_togglebutton Troubleshooting diff --git a/doc/manual/manual/installation/index.rst b/doc/manual/manual/installation/index.rst index fcc2f6326..bfb997099 100644 --- a/doc/manual/manual/installation/index.rst +++ b/doc/manual/manual/installation/index.rst @@ -11,6 +11,7 @@ Codac is available in both C++20 and Python3, as well as MATLAB `through its Pyt python.rst cpp.rst matlab.rst + projects.rst performances.rst | diff --git a/doc/manual/manual/installation/projects.rst b/doc/manual/manual/installation/projects.rst new file mode 100644 index 000000000..24ae3e9c1 --- /dev/null +++ b/doc/manual/manual/installation/projects.rst @@ -0,0 +1,329 @@ +.. _sec-start-cpp-project: + +Using Codac in my own C++ or Python project +=========================================== + +The previous pages explain how to *install* Codac. This one explains how to *use* +the installation you have just made from a project of your own: which files CMake +or ``pkg-config`` look for, which paths have to be given to them, and what a +minimal project looks like in C++ and in Python. + +Everything below assumes an installation prefix, referred to as +``$CODAC_PREFIX``. If you followed :ref:`the installation from sources +` that is the directory you passed to +``-DCMAKE_INSTALL_PREFIX``, typically ``$HOME/codac/build_install``; if you +installed a Debian package or a standalone archive it is ``/usr`` or the +directory you extracted the archive into. Its layout is: + +.. code-block:: text + + $CODAC_PREFIX/ + ├── include/ + │ ├── codac <- the umbrella header, for #include + │ ├── codac-core/ <- one directory per module + │ ├── codac-graphics/ + │ ├── codac-unsupported/ + │ ├── codac-3rd/ <- GAOL and mathlib, when Codac built them + │ └── eigen3/ <- Eigen, part of Codac's public interface + ├── lib/ + │ ├── libcodac-core.a, libcodac-graphics.a, libcodac-unsupported.a + │ └── codac-3rd/ <- libgaol.a, libultim.a and GAOL's CMake package, when Codac built them + └── share/ + ├── codac/cmake/ + │ ├── codac-config.cmake <- what find_package(CODAC) reads + │ └── codac-targets.cmake <- the exported Codac:: targets + └── pkgconfig/ + └── codac.pc <- what pkg-config reads + + +.. _sec-start-cpp-project-cmake: + +A C++ project with CMake +------------------------ + +This is the recommended route, and the one every project under ``examples/`` +takes. Consider a project made of two files: + +.. code-block:: text + + my_project/ + ├── CMakeLists.txt + └── main.cpp + +``main.cpp`` includes the single umbrella header and uses the ``codac2`` +namespace: + +.. code-block:: c++ + + #include + + using namespace codac2; + + int main() + { + VectorVar x(2); + AnalyticFunction f({x}, x[0]*cos(x[0]-x[1])+x[1]); + SepInverse sep(f, {-oo,0}); + DefaultFigure::pave({{-10,10},{-10,10}}, sep, 0.1); + } + +and ``CMakeLists.txt`` is: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.14) + project(my_project LANGUAGES CXX) + + # Codac is a C++20 library, so a project using it is compiled as C++20 too. + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + + # Where to look for Codac. Leave this out if it is installed in a system + # directory, and prefer passing it on the command line (see below) to + # hard-coding it here. + # set(CMAKE_PREFIX_PATH "$ENV{HOME}/codac/build_install") + + find_package(CODAC REQUIRED) + message(STATUS "Found Codac version ${CODAC_VERSION}") + + add_executable(${PROJECT_NAME} main.cpp) + # CODAC_CXX_FLAGS holds the interval arithmetic flags (-frounding-math and + # the rest). Without them the rounding modes Codac relies on are not + # guaranteed, and the results lose the very property the library is for. + 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}) + +Which paths to give to CMake +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``find_package(CODAC REQUIRED)`` looks for ``codac-config.cmake`` under the +prefixes listed in ``CMAKE_PREFIX_PATH``, in ``share/codac/cmake/`` — which is +exactly where Codac installs it. Nothing else is needed: GAOL, the interval +arithmetic library Codac is built upon, is either installed along with Codac, +with its CMake package, which ``codac-config.cmake`` finds there, or found by +``codac-config.cmake`` where Codac found it. There are three ways to give that +prefix: + +.. code-block:: bash + + # 1. On the configuration command line (recommended: nothing in the sources + # then depends on where a given machine happens to keep its libraries) + cmake -DCMAKE_PREFIX_PATH="$HOME/codac/build_install" .. + + # 2. Through the environment, once and for all, e.g. in your .bashrc + export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:$HOME/codac/build_install + + # 3. In the CMakeLists.txt itself, as in the commented line above + # (several prefixes are separated by ";": CMAKE_PREFIX_PATH is a CMake + # list, not a PATH) + +.. note:: + + Projects written for earlier versions of Codac, which depended on IBEX, call + ``ibex_init_common()`` after ``find_package(CODAC)``. That call is no longer + needed, the flags it applied coming with ``CODAC_CXX_FLAGS``, and can be + removed; until it is, ``codac-config.cmake`` provides an ``ibex_init_common()`` + that does nothing, so that such projects still configure. + +.. admonition:: ``CODAC_DIR`` rather than ``CMAKE_PREFIX_PATH`` + + ``-DCODAC_DIR=$CODAC_PREFIX/share/codac/cmake`` points CMake straight at the + configuration file, bypassing the prefix search. It is useful to disambiguate + between two installed versions, but it says nothing about CAPD, which + ``codac-config.cmake`` looks for when that module was built, and which still + has to be found through ``CMAKE_PREFIX_PATH`` or ``CAPD_DIR``. + +What ``find_package(CODAC)`` defines +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Variable + - Contents + + * - ``CODAC_VERSION`` + - The version of the installation that was found, *e.g.* ``2.1.1``. + + * - ``CODAC_INCLUDE_DIRS`` + - The include directories: the header root (through which ```` is + reached), ``include/eigen3``, and one directory per module. + + * - ``CODAC_LIBRARIES`` + - The libraries to link against. These are *imported targets* + (``Codac::codac-core``, ``Codac::codac-graphics``, + ``Codac::codac-unsupported``, plus ``Codac::gaol``, which brings GAOL, + mathlib and the flags GAOL needs along -- through ``gaol::gaol``, of the + CMake package of GAOL, when GAOL has one -- and ``Threads::Threads``) + rather than library paths, which is what lets CMake work out the link + order for itself. + + * - ``CODAC_CXX_FLAGS`` + - The flags GAOL gives, with which Codac was compiled: those of interval + arithmetic (``-frounding-math`` and the rest), and those of the fused + multiply-add instructions (``-mfma``, and so on). They have to be applied + to your own translation units too: without the former, the rounding modes + Codac relies on are not guaranteed; and Eigen's headers are compiled by + your project, and compiling them under different alignment and + instruction-set assumptions than the installed archives were built with is + an ODR/ABI mismatch. + + * - ``CODAC_CAPD_LIBRARY``, ``CODAC_SYMPY_LIBRARY`` + - Defined only when the corresponding optional module was built. They are + already part of ``CODAC_LIBRARIES``; use them to opt into one module + explicitly. + +Building it +^^^^^^^^^^^ + +.. code-block:: bash + + cd my_project + mkdir build ; cd build + cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$HOME/codac/build_install" .. + cmake --build . + ./my_project + +On Visual Studio, replace the last two commands with +``cmake --build . --config Release``. If your program produces a graphical +output, remember to launch :ref:`the VIBes viewer ` first. + + +.. _sec-start-cpp-project-pkgconfig: + +A C++ project with pkg-config +----------------------------- + +Codac also installs ``share/pkgconfig/codac.pc``, for projects built with a +plain ``Makefile``, with Autotools, or compiled by hand. Since it is installed in +``share/pkgconfig`` rather than ``lib/pkgconfig``, and since an installation in +your home directory is not on the default search path either, ``pkg-config`` has +to be told where to look: + +.. code-block:: bash + + export PKG_CONFIG_PATH=$CODAC_PREFIX/share/pkgconfig:$PKG_CONFIG_PATH + +Check that it is found, and look at what it answers: + +.. code-block:: bash + + pkg-config --modversion codac # 2.1.1 + pkg-config --cflags codac + pkg-config --libs codac + +Compiling with it +^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + g++ -std=c++20 -O3 -DNDEBUG $(pkg-config --cflags codac) \ + main.cpp -o my_project $(pkg-config --libs codac) + +The language standard and the optimisation level are deliberately **not** part of +``codac.pc``: they are your project's business, exactly as ``CMAKE_CXX_STANDARD`` +and ``CMAKE_BUILD_TYPE`` are on the CMake side. Everything else is carried by the +file — the include directories (Codac's, Eigen's and GAOL's), the interval +arithmetic and architecture flags, the Codac and GAOL libraries in a working link +order, and the thread library where one is needed. + +The same thing in a ``Makefile``: + +.. code-block:: make + + CXXFLAGS := -std=c++20 -O3 -DNDEBUG $(shell pkg-config --cflags codac) + LDLIBS := $(shell pkg-config --libs codac) + + my_project: main.cpp + $(CXX) $(CXXFLAGS) $< -o $@ $(LDLIBS) + +.. admonition:: ``-frounding-math``, and why it is in ``codac.pc`` + + A CMake consumer gets the interval arithmetic flags through + ``CODAC_CXX_FLAGS``. A ``pkg-config`` consumer has no such variable, so + ``codac.pc`` carries those flags in its ``Cflags:`` line itself. This matters: + compiling Codac's headers without ``-frounding-math`` silently gives up the + guarantee the whole library rests on, and nothing in the build would report it. + Use the flags ``pkg-config`` gives you rather than a subset of them. + +.. warning:: + + ``pkg-config`` speaks ``-I``/``-L``/``-l``, which ``cl.exe`` does not + understand. **On Visual Studio, use the CMake route**; ``codac.pc`` is of no + use there. It works as expected with MinGW. + + +.. _sec-start-py-project: + +A Python project +---------------- + +If Codac was :ref:`installed with pip `, there is nothing to +configure: the package is in your interpreter's ``site-packages`` and a plain +``import`` finds it. A complete script is then: + +.. code-block:: py + + from codac import * + + x = VectorVar(2) + f = AnalyticFunction([x], x[0]*cos(x[0]-x[1])+x[1]) + sep = SepInverse(f, [-oo,0]) + DefaultFigure.pave([[-10,10],[-10,10]], sep, 0.1) + +Pointing Python at a local build +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If instead you :ref:`built the binding yourself `, the +package is left in the build tree, at +``$HOME/codac/build/python/python_package/codac``. Python has to be told about +the directory *containing* the package — that is, ``python_package``, not +``python_package/codac``. Three ways, in decreasing order of preference: + +.. code-block:: bash + + # 1. Register the build tree with your interpreter, once. The installation + # stays editable: rebuilding Codac is enough, there is nothing to reinstall. + cd $HOME/codac/build/python/python_package + pip install --user -e . + + # 2. Through the environment, e.g. in your .bashrc + export PYTHONPATH="${PYTHONPATH}:$HOME/codac/build/python/python_package/" + +The third way is from the script itself, which is useful for a one-off test or +when several builds of Codac coexist on the same machine. The path has to be +added **before** the import: + +.. code-block:: py + + import sys + # The directory that contains the "codac" package, not the package itself: + sys.path.insert(0, "/home/me/codac/build/python/python_package") + + from codac import * + + x = VectorVar(2) + f = AnalyticFunction([x], x[0]*cos(x[0]-x[1])+x[1]) + sep = SepInverse(f, [-oo,0]) + DefaultFigure.pave([[-10,10],[-10,10]], sep, 0.1) + +Checking which Codac you are actually using +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When a pip-installed Codac and a locally built one are both present, this is the +quickest way to see which one won: + +.. code-block:: bash + + python -c "import codac ; print(codac.__version__) ; print(codac.__file__)" + +``__file__`` is the path of the package that was imported, so it tells you +whether you are running the release from PyPI or your own build. + +.. admonition:: MATLAB + + The MATLAB interface is the same Python package under another name, + ``codac4matlab``, so the paths above apply to it as well. See + :ref:`sec-install-matlab`. diff --git a/doc/manual/manual/installation/python.rst b/doc/manual/manual/installation/python.rst index 940281bdf..7cd358053 100644 --- a/doc/manual/manual/installation/python.rst +++ b/doc/manual/manual/installation/python.rst @@ -41,6 +41,8 @@ You can also update and test your Codac Python package: pip install numpy sympy python -m unittest discover codac.tests +Once this works, :ref:`sec-start-py-project` shows what a script of your own looks like, and how to point Python at a locally built Codac rather than at the release from PyPI. + Troubleshooting --------------- diff --git a/doc/manual/manual/introduction/index.rst b/doc/manual/manual/introduction/index.rst index fc5cef30f..451a0a619 100644 --- a/doc/manual/manual/introduction/index.rst +++ b/doc/manual/manual/introduction/index.rst @@ -75,6 +75,45 @@ Codac already provides a catalog a contractors that one can use to deal with man Another main distinction lies in the way things are computed: with set-membership methods, estimations are not randomly performed. **Computations are deterministic**: given a set of parameters or inputs, algorithms will always output the same result. +.. _sec-intro-separators: + +Separators +---------- + +A contractor answers one question: *which part of this box can be discarded, because it surely contains no solution?* It says nothing about the part it keeps, which may hold solutions, non-solutions, or both. + +A **separator** answers the two symmetrical questions at once. Applied to a box :math:`[\mathbf{x}]`, a separator :math:`\mathcal{S}` associated with a set :math:`\mathbb{S}` returns a pair of boxes: + +- an **inner** box, obtained by removing from :math:`[\mathbf{x}]` what is certainly *inside* :math:`\mathbb{S}`; +- an **outer** box, obtained by removing from :math:`[\mathbf{x}]` what is certainly *outside* :math:`\mathbb{S}`. + +A separator is therefore the pair made of a contractor for :math:`\mathbb{S}` and a contractor for its complement :math:`\overline{\mathbb{S}}`, and what falls outside both boxes is the part of :math:`[\mathbf{x}]` that has been *proved* to belong to :math:`\mathbb{S}`. This is the essential gain over a contractor alone: besides eliminating, we can now certify. + +In Codac, this is the ``Sep`` interface: its ``separate()`` method takes a box and returns a ``BoxPair``, whose ``inner`` and ``outer`` members are the two boxes described above. Most contractors of the catalog have a separator counterpart, ``CtcInverse`` and ``SepInverse`` for instance, and the two families are listed side by side in :ref:`sec-ctc`. + +The practical consequence is visible on the two examples of the home page. The first one is solved with the contractor ``CtcInverse``: the blue boxes are guaranteed to be solution-free, and nothing is claimed about the rest. The second one is solved with the separator ``SepInverse``: the blue boxes are again guaranteed to have no solution, but *in addition* any vector taken in a green box is a solution of the inequality. + + +.. _sec-intro-pavings: + +Pavings +------- + +Contractors and separators reduce a box, but a single box is rarely a satisfactory description of a solution set: as soon as the set is not box-shaped, its interval enclosure is a coarse over-approximation — the *pessimism* mentioned below. + +The way out is to **bisect**. A box that can no longer be contracted is cut in two, and each half is contracted in turn; the process is repeated until the remaining boxes are smaller than a precision :math:`\epsilon` given by the user. The resulting collection of non-overlapping boxes is called a **paving**, and the algorithm producing it is known as **SIVIA** (*Set Inversion Via Interval Analysis*). + +The boxes of a paving are of three kinds: + +- those proved to contain no solution; +- those proved to contain only solutions — which only a separator, or a direct inclusion test, can establish; +- those still undecided, which are the ones the bisection stops on when they become smaller than :math:`\epsilon`. They form the boundary of the solution set, and their total volume is the price paid for the guarantee. + +In Codac, the paving of a box is obtained with a single call, ``pave(x0, c, eps)``, which accepts a contractor or a separator and returns a ``PavingOut`` or a ``PavingInOut`` accordingly. This is what both examples of the home page do. The function ``sivia(x0, f, y, eps)`` provides the same service directly from a function :math:`\mathbf{f}` and a target domain :math:`[\mathbf{y}]`, without an explicit contractor. + +The choice of :math:`\epsilon` is the usual compromise: the finer it is, the thinner the undecided boundary, and the more boxes to compute. + + Reliable outputs ---------------- diff --git a/doc/manual/manual/visualization/colors.rst b/doc/manual/manual/visualization/colors.rst index aaba29598..589aa72a9 100644 --- a/doc/manual/manual/visualization/colors.rst +++ b/doc/manual/manual/visualization/colors.rst @@ -129,6 +129,7 @@ Additionnal methods are available for any useful purpose: .. _subsec-graphics-colors-style-properties: + StyleProperties --------------- @@ -209,6 +210,7 @@ For more information, see :ref:`subsec-graphics-colors-optional-arguments`. % Red edge, dotted line, line width of 0.1, z-value of 1.5 and on layer1 .. _subsec-graphics-colors-paving-style: + Paving style ------------ @@ -342,6 +344,7 @@ You can also create your own color map : Note that you can add RGB and HSV colors to the same color map. The model of the color map will define the interpolation space. .. _subsec-graphics-colors-style-gradient-properties: + StyleGradientProperties ----------------------- diff --git a/doc/manual/tuto/cp_robotics/src/CMakeLists.txt b/doc/manual/tuto/cp_robotics/src/CMakeLists.txt index 51161a85e..ad8a88fc7 100644 --- a/doc/manual/tuto/cp_robotics/src/CMakeLists.txt +++ b/doc/manual/tuto/cp_robotics/src/CMakeLists.txt @@ -8,16 +8,6 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Adding IBEX - - # In case you installed IBEX in a local directory, you need - # to specify its path with the CMAKE_PREFIX_PATH option. - # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") - - find_package(IBEX REQUIRED) - ibex_init_common() # IBEX should have installed this function - message(STATUS "Found IBEX version ${IBEX_VERSION}") - # Adding Codac # In case you installed Codac in a local directory, you need @@ -38,4 +28,4 @@ ) 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) \ No newline at end of file + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES}) \ No newline at end of file diff --git a/examples/00_graphics/CMakeLists.txt b/examples/00_graphics/CMakeLists.txt index cf4fae020..95b125893 100644 --- a/examples/00_graphics/CMakeLists.txt +++ b/examples/00_graphics/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/01_batman/CMakeLists.txt b/examples/01_batman/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/01_batman/CMakeLists.txt +++ b/examples/01_batman/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/02_centered_form/CMakeLists.txt b/examples/02_centered_form/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/02_centered_form/CMakeLists.txt +++ b/examples/02_centered_form/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/03_sivia/CMakeLists.txt b/examples/03_sivia/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/03_sivia/CMakeLists.txt +++ b/examples/03_sivia/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/04_explored_area/CMakeLists.txt b/examples/04_explored_area/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/04_explored_area/CMakeLists.txt +++ b/examples/04_explored_area/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/05_capd_solver/CMakeLists.txt b/examples/05_capd_solver/CMakeLists.txt index 4d720d238..2d3daae24 100644 --- a/examples/05_capd_solver/CMakeLists.txt +++ b/examples/05_capd_solver/CMakeLists.txt @@ -8,16 +8,6 @@ project(codac_example LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Adding IBEX - -# In case you installed IBEX in a local directory, you need -# to specify its path with the CMAKE_PREFIX_PATH option. -# set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") - -find_package(IBEX REQUIRED) -ibex_init_common() # IBEX should have installed this function -message(STATUS "Found IBEX version ${IBEX_VERSION}") - # Adding Codac # In case you installed Codac in a local directory, you need @@ -41,5 +31,4 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${CODAC_LIBRARIES} ${CODAC_CAPD_LIBRARY} capd::capd - Ibex::ibex ) \ No newline at end of file diff --git a/examples/06_graphics_3D/CMakeLists.txt b/examples/06_graphics_3D/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/06_graphics_3D/CMakeLists.txt +++ b/examples/06_graphics_3D/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/07_centered_2D/CMakeLists.txt b/examples/07_centered_2D/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/07_centered_2D/CMakeLists.txt +++ b/examples/07_centered_2D/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/08_centered_3D/CMakeLists.txt b/examples/08_centered_3D/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/08_centered_3D/CMakeLists.txt +++ b/examples/08_centered_3D/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/09_robot_simu/CMakeLists.txt b/examples/09_robot_simu/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/09_robot_simu/CMakeLists.txt +++ b/examples/09_robot_simu/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/10_lie_groups/CMakeLists.txt b/examples/10_lie_groups/CMakeLists.txt index 41d3ccba3..87b2ee865 100644 --- a/examples/10_lie_groups/CMakeLists.txt +++ b/examples/10_lie_groups/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/11_peibos/CMakeLists.txt b/examples/11_peibos/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/11_peibos/CMakeLists.txt +++ b/examples/11_peibos/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/12_peibos_capd/CMakeLists.txt b/examples/12_peibos_capd/CMakeLists.txt index 9091ca50a..efb53fdfa 100644 --- a/examples/12_peibos_capd/CMakeLists.txt +++ b/examples/12_peibos_capd/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) @@ -35,5 +31,4 @@ ${CODAC_LIBRARIES} ${CODAC_CAPD_LIBRARY} capd::capd - Ibex::ibex ) \ No newline at end of file diff --git a/examples/13_qinter/CMakeLists.txt b/examples/13_qinter/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/13_qinter/CMakeLists.txt +++ b/examples/13_qinter/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/14_lohner/CMakeLists.txt b/examples/14_lohner/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/14_lohner/CMakeLists.txt +++ b/examples/14_lohner/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/15_sympy/CMakeLists.txt b/examples/15_sympy/CMakeLists.txt index 52197a061..336db6b62 100644 --- a/examples/15_sympy/CMakeLists.txt +++ b/examples/15_sympy/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) @@ -37,5 +33,4 @@ ${CODAC_LIBRARIES} ${CODAC_SYMPY_LIBRARY} # linking to the codac-sympy extension pybind11::embed # linking to pybind11 - Ibex::ibex ) \ No newline at end of file diff --git a/examples/16_visibility/CMakeLists.txt b/examples/16_visibility/CMakeLists.txt index cf9ef35ce..23d13fc6e 100644 --- a/examples/16_visibility/CMakeLists.txt +++ b/examples/16_visibility/CMakeLists.txt @@ -17,10 +17,6 @@ find_package(CODAC REQUIRED) message(STATUS "Found Codac version ${CODAC_VERSION}") -# Initializating Ibex - - ibex_init_common() - # Compilation if(FAST_RELEASE) diff --git a/examples/ellipsoid_example/CMakeLists.txt b/examples/ellipsoid_example/CMakeLists.txt index ce82f2152..ad0a314c7 100644 --- a/examples/ellipsoid_example/CMakeLists.txt +++ b/examples/ellipsoid_example/CMakeLists.txt @@ -8,16 +8,6 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Adding IBEX - - # In case you installed IBEX in a local directory, you need - # to specify its path with the CMAKE_PREFIX_PATH option. - # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") - - find_package(IBEX REQUIRED) - ibex_init_common() # IBEX should have installed this function - message(STATUS "Found IBEX version ${IBEX_VERSION}") - # Adding Eigen3 # In case you installed Eigen3 in a local directory, you need @@ -41,4 +31,4 @@ 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} Eigen3::Eigen) diff --git a/python/src/core/CMakeLists.txt b/python/src/core/CMakeLists.txt index 9d83a210d..3507d6d5c 100644 --- a/python/src/core/CMakeLists.txt +++ b/python/src/core/CMakeLists.txt @@ -157,7 +157,7 @@ ) target_link_libraries(_core - PRIVATE ${PROJECT_NAME}-core ${PROJECT_NAME}-sympy ${LIBS} Ibex::ibex + PRIVATE ${PROJECT_NAME}-core ${PROJECT_NAME}-sympy ${LIBS} Codac::gaol ) # Copy the generated library in the package folder diff --git a/python/src/graphics/CMakeLists.txt b/python/src/graphics/CMakeLists.txt index 7b180a814..0b4386d75 100644 --- a/python/src/graphics/CMakeLists.txt +++ b/python/src/graphics/CMakeLists.txt @@ -28,7 +28,7 @@ ) target_link_libraries(_graphics - PRIVATE ${PROJECT_NAME}-graphics ${LIBS} Ibex::ibex + PRIVATE ${PROJECT_NAME}-graphics ${LIBS} Codac::gaol ) # Copy the generated library in the package folder diff --git a/python/src/unsupported/CMakeLists.txt b/python/src/unsupported/CMakeLists.txt index a413804dd..861175754 100644 --- a/python/src/unsupported/CMakeLists.txt +++ b/python/src/unsupported/CMakeLists.txt @@ -16,7 +16,7 @@ ) target_link_libraries(_unsupported - PRIVATE ${PROJECT_NAME}-unsupported ${LIBS} Ibex::ibex + PRIVATE ${PROJECT_NAME}-unsupported ${LIBS} Codac::gaol ) # Copy the generated library in the package folder diff --git a/scripts/CMakeModules/FindGAOL.cmake b/scripts/CMakeModules/FindGAOL.cmake new file mode 100644 index 000000000..a8f44ae50 --- /dev/null +++ b/scripts/CMakeModules/FindGAOL.cmake @@ -0,0 +1,208 @@ +# ================================================================== +# Codac - cmake module looking for an installed GAOL +# ================================================================== +# +# Looks for GAOL, the interval arithmetic library Codac is built upon +# (Frederic Goualard, https://frederic.goualard.net, +# https://github.com/goualard-f/GAOL), and for mathlib, the IBM Accurate +# Portable Mathematical Library GAOL computes its elementary functions with, +# which installs itself as libultim. +# +# Where to look, when GAOL is not installed in a directory CMake searches by +# default, is given by two cache entries -- or by CMAKE_PREFIX_PATH: +# +# GAOL_DIR prefix holding include/gaol/gaol.h and lib/ +# MATHLIB_DIR prefix holding include/MathLib.h and lib/; +# GAOL_DIR is searched for mathlib as well, the two being +# usually installed together +# +# What is found is written to the cache entries GAOL_INCDIR, GAOL_LIB, +# MATHLIB_INCDIR and MATHLIB_LIB, which can also be set by hand to bypass the +# search, and summarised in: +# +# GAOL_FOUND +# GAOL_VERSION when gaol/gaol_configuration.h states it +# GAOL_INCLUDE_DIRS +# GAOL_LIBRARIES gaol, then ultim, in link order +# +# No target is defined here. codac_gaol_find() (scripts/CMakeModules/codac_gaol.cmake) +# makes Codac::gaol out of these variables, with the flags of interval +# arithmetic it determines itself, for a GAOL that has neither a CMake package +# nor a gaol.pc, which it looks for first. +# +# Origin +# ------ +# This is the search IBEX runs before building a GAOL of its own, taken out of +# IBEX (https://github.com/ibex-team/ibex-lib, GNU LGPL v3) so that Codac can +# find GAOL without IBEX: +# +# - codac_find_header_custom() and codac_find_library_custom() are the +# functions find_header_custom() and find_library_custom() of IBEX's +# cmake.utils/IbexUtils.cmake, written by Cyril Bouvier for the CMake build +# of IBEX. They are renamed with a codac_ prefix so as not to clash with +# IBEX's own definitions in a project that loads both, and differ in +# two respects: they stay silent when find_package(GAOL QUIET) asks for it, +# and a header found on the default search path is returned as it is, +# rather than wrapped in $ -- that wrapping is how IBEX +# keeps such a directory out of the export files it generates, and Codac +# writes its configuration file by other means (see src/CMakeLists.txt). +# +# - The four calls below, the GAOL_DIR and MATHLIB_DIR hints and the names of +# the result variables are those of IBEX's +# interval_lib_wrapper/gaol/CMakeLists.txt, by Cyril Bouvier as well. +# +# - Reading the version out of gaol/gaol_configuration.h comes from +# interval_lib_wrapper/gaol/FindGaol.cmake of IBEX (Cyril Bouvier). It +# reads the GAOL_MAJOR_VERSION, GAOL_MINOR_VERSION and GAOL_MICRO_VERSION +# macros here, which both the autotools and the CMake builds of GAOL write. + + +################################################################################ +# Functions from IBEX (see above) +################################################################################ + + function(codac_find_header_custom prefix hdrname) + set(opt "") + set(oneArgs "") + set(multiArgs PATHS) + + cmake_parse_arguments(FHC "${opt}" "${oneArgs}" "${multiArgs}" ${ARGN}) + + if(FHC_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to codac_find_header_custom(): \"${FHC_UNPARSED_ARGUMENTS}\"") + endif() + + set(MSG "Looking for ${hdrname}") + if(NOT GAOL_FIND_QUIETLY) + message(STATUS "${MSG}") + endif() + + # First look only in PATHS if given + if(FHC_PATHS) + find_path(${prefix}_INCDIR ${hdrname} PATHS ${FHC_PATHS} + DOC "Set to exact include directory to bypass internal test" + PATH_SUFFIXES include NO_DEFAULT_PATH) + endif() + + if(NOT ${prefix}_INCDIR) + # Now look with system and cmake paths + find_path(${prefix}_INCDIR ${hdrname} + DOC "Set to exact include directory to bypass internal test" + PATH_SUFFIXES include) + endif() + + if(NOT GAOL_FIND_QUIETLY) + if(${prefix}_INCDIR) + message(STATUS "${MSG} -- found at ${${prefix}_INCDIR}") + else() + message(STATUS "${MSG} -- not found") + endif() + endif() + + set(${prefix}_INCDIR ${${prefix}_INCDIR} PARENT_SCOPE) + mark_as_advanced(${prefix}_INCDIR) + endfunction() + + + function(codac_find_library_custom prefix libname) + set(opt "") + set(oneArgs "") + set(multiArgs PATHS) + + cmake_parse_arguments(FLC "${opt}" "${oneArgs}" "${multiArgs}" ${ARGN}) + + if(FLC_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to codac_find_library_custom(): \"${FLC_UNPARSED_ARGUMENTS}\"") + endif() + + set(MSG "Looking for ${libname}") + if(NOT GAOL_FIND_QUIETLY) + message(STATUS "${MSG}") + endif() + + # First look only in PATHS if given + if(FLC_PATHS) + find_library(${prefix}_LIB ${libname} PATHS ${FLC_PATHS} + DOC "Set to exact lib directory to bypass internal test" + PATH_SUFFIXES lib NO_DEFAULT_PATH) + endif() + + if(NOT ${prefix}_LIB) + # Now look with system and cmake paths + find_library(${prefix}_LIB ${libname} + DOC "Set to exact lib directory to bypass internal test" + PATH_SUFFIXES lib) + endif() + + if(NOT GAOL_FIND_QUIETLY) + if(${prefix}_LIB) + message(STATUS "${MSG} -- found at ${${prefix}_LIB}") + else() + message(STATUS "${MSG} -- not found") + endif() + endif() + + set(${prefix}_LIB ${${prefix}_LIB} PARENT_SCOPE) + mark_as_advanced(${prefix}_LIB) + endfunction() + + +################################################################################ +# Options +################################################################################ + + set(MATHLIB_DIR "" CACHE PATH "Path to the Mathlib/ultim lib and include directories") + set(GAOL_DIR "" CACHE PATH "Path to the Gaol lib and include directories") + + +################################################################################ +# Looking for Mathlib/libultim +################################################################################ + + # Looking for MathLib.h, result is written in MATHLIB_INCDIR + codac_find_header_custom(MATHLIB "MathLib.h" PATHS "${MATHLIB_DIR}" "${GAOL_DIR}") + # Looking for ultim library, result is written in MATHLIB_LIB + codac_find_library_custom(MATHLIB "ultim" PATHS "${MATHLIB_DIR}" "${GAOL_DIR}") + + +################################################################################ +# Looking for Gaol +################################################################################ + + # Looking for gaol/gaol.h, result is written in GAOL_INCDIR + codac_find_header_custom(GAOL "gaol/gaol.h" PATHS "${GAOL_DIR}") + # Looking for gaol library, result is written in GAOL_LIB + codac_find_library_custom(GAOL "gaol" PATHS "${GAOL_DIR}") + + set(GAOL_VERSION "") + if(GAOL_INCDIR AND EXISTS "${GAOL_INCDIR}/gaol/gaol_configuration.h") + file(STRINGS "${GAOL_INCDIR}/gaol/gaol_configuration.h" _gaol_version_lines + REGEX "^#define GAOL_(MAJOR|MINOR|MICRO)_VERSION[ \t]+[0-9]+") + set(_gaol_version_parts "") + foreach(_gaol_part MAJOR MINOR MICRO) + string(REGEX MATCH "GAOL_${_gaol_part}_VERSION[ \t]+([0-9]+)" _gaol_match "${_gaol_version_lines}") + if(_gaol_match) + list(APPEND _gaol_version_parts ${CMAKE_MATCH_1}) + endif() + endforeach() + list(LENGTH _gaol_version_parts _gaol_version_length) + if(_gaol_version_length EQUAL 3) + string(REPLACE ";" "." GAOL_VERSION "${_gaol_version_parts}") + endif() + endif() + + +################################################################################ +# Result +################################################################################ + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(GAOL + REQUIRED_VARS GAOL_LIB GAOL_INCDIR MATHLIB_LIB MATHLIB_INCDIR + VERSION_VAR GAOL_VERSION) + + if(GAOL_FOUND) + set(GAOL_INCLUDE_DIRS ${GAOL_INCDIR} ${MATHLIB_INCDIR}) + list(REMOVE_DUPLICATES GAOL_INCLUDE_DIRS) + set(GAOL_LIBRARIES ${GAOL_LIB} ${MATHLIB_LIB}) + endif() diff --git a/scripts/CMakeModules/codac_gaol.cmake b/scripts/CMakeModules/codac_gaol.cmake new file mode 100644 index 000000000..631b63c8d --- /dev/null +++ b/scripts/CMakeModules/codac_gaol.cmake @@ -0,0 +1,799 @@ +# ================================================================== +# Codac - cmake module for GAOL, the interval arithmetic library +# ================================================================== +# +# codac2::Interval derives from gaol::interval (see +# src/core/domains/interval/codac2_Interval.h). GAOL is written by Frederic +# Goualard (https://frederic.goualard.net, https://github.com/goualard-f/GAOL) +# and distributed under the GNU LGPL v2. It computes its elementary functions +# with mathlib, the IBM Accurate Portable Mathematical Library (libultim), which +# Frederic Goualard distributes along with it, under the GNU GPL v2 or later. +# +# Codac builds the GAOL of Jordan Ninin's fork, https://github.com/Jordan08/GAOL, +# which adds to GAOL a CMake build, the fixes Codac depends on (those of the +# patch IBEX applies to GAOL, and those Visual C++, MinGW and ARM processors +# need) and tests of its bounds. Fixes of GAOL go into the fork rather than into +# Codac. The fork's CMake build downloads mathlib from Frederic Goualard's site, +# and refuses the compilers that do not compute GAOL's intervals right (see its +# README.md). +# +# GAOL's interval operations are inline: the code including its headers, Codac's +# and that of Codac's users, has to be compiled with the flags of interval +# arithmetic, and linked with GAOL and mathlib. Which flags and which libraries +# is GAOL's to say, and codac_gaol_find() takes them from the GAOL it finds or +# builds, in this order: +# +# 1. the CMake package of GAOL, which the CMake build of the fork installs: +# find_package(gaol CONFIG) gives gaol::gaol, which carries the include +# directory, the flags (PUBLIC), __GAOL_PUBLIC__= for Visual C++, and +# mathlib (gaol::ultim); +# 2. pkg-config: the gaol.pc the autotools and meson builds of the fork install +# carries the flags in its Cflags, and GAOL and mathlib in its Libs; +# 3. the files themselves (FindGAOL.cmake), for a GAOL installed with neither, +# whose flags Codac then determines itself (codac_gaol_interval_flags()); +# 4. when none is found, or with ENABLE_FIND_PACKAGE_GAOL OFF, codac_gaol_build() +# builds the fork with its CMake build and installs it with its installer, +# and find_package(gaol CONFIG) takes the package it installed, as in 1. +# +# In 1 to 3, a GAOL older than CODAC_GAOL_MIN_VERSION (see below), or whose +# version cannot be told, is passed over, with a message saying so, and the next +# way is tried, down to building the fork. In 4, a GAOL built too old stops the +# configuration. +# +# All four end in Codac::gaol, the imported target the Codac libraries link +# PUBLIC, and which codac-config.cmake defines again for their users +# (codac_gaol_config_snippet()). +# +# Origin +# ------ +# What follows comes from IBEX (https://github.com/ibex-team/ibex-lib, GNU +# LGPL v3): +# +# - codac_gaol_portability_flags() and codac_gaol_interval_flags() are, divided +# in two, the part of ibex_init_common() (cmake.utils/ibex-config-utils.cmake) +# that sets the floating-point flags, in the version of the IBEX fork +# maintained by Fabrice Le Bars, https://github.com/lebarsfa/ibex-lib, tag +# ibex-2.8.9.20260819, which Codac was built against until now. +# ibex_init_common() was written by Cyril Bouvier for the CMake build of IBEX; +# the flags it sets -- -frounding-math and the others that IEEE 754 double +# support depends on, and their Visual Studio counterparts -- were added to it +# by Fabrice Le Bars. The condition under which -ffloat-store is added is the +# one of the CMake build of the fork of GAOL (its CMakeLists.txt). +# +# - codac_gaol_build() is the "not found, install it" branch of +# interval_lib_wrapper/gaol/CMakeLists.txt, written by Cyril Bouvier: build +# mathlib and GAOL, and install the result next to the library it serves, as +# IBEX does in include/ibex/3rd and lib/ibex/3rd. The CMake build of GAOL and +# mathlib it runs is the fork's, which says where it comes from: from IBEX +# (Cyril Bouvier, Gilles Chabert), with the portability fixes for Visual +# C++, MinGW and ARM of the forks of GAOL and mathlib by Fabrice Le Bars. +# +# - codac_gaol_config_snippet() does, for Codac, what +# create_target_import_and_export() of cmake.utils/IbexUtils.cmake (Cyril +# Bouvier) does for IBEX: the lines that define the imported target of GAOL +# again for a consumer of the installed library. +# +# What differs from IBEX +# ---------------------- +# IBEX extracts GAOL and mathlib from archives kept in its own repository, and +# builds them as a part of itself (add_subdirectory()). Here GAOL is downloaded +# from the fork, at the head of its master branch, and built as a project of its +# own when Codac is configured, for the reasons given at codac_gaol_build(); the +# fork's build downloads mathlib, checked against the SHA256 of its archive. The +# other differences are explained where they occur. + + +# Where a GAOL built by codac_gaol_build() is installed, under the installation +# prefix. IBEX keeps the libraries it builds for itself in include/ibex/3rd and +# lib/ibex/3rd, out of the way of a GAOL installed separately; include/codac +# cannot play that part here, being the name of Codac's umbrella header. GAOL's +# CMake package goes in lib/codac-3rd/cmake/gaol, where the search of +# find_package() does not look: only codac-config.cmake finds it there. +set(CODAC_INSTALL_INCLUDEDIR_3RD "${CMAKE_INSTALL_INCLUDEDIR}/codac-3rd") +set(CODAC_INSTALL_LIBDIR_3RD "${CMAKE_INSTALL_LIBDIR}/codac-3rd") + +# Where codac_gaol_build() downloads, builds and installs GAOL in the build tree +set(CODAC_GAOL_WORK_DIR "${CMAKE_BINARY_DIR}/_deps/gaol") + +# The oldest GAOL Codac accepts: version 4.3.2 of the fork, the first one with all +# the fixes Codac relies on. Among them, the last one: the intersection of disjoint +# intervals is the canonical empty set [NaN, NaN], where GAOL gave reversed bounds, +# [3, 2] for [1, 2] & [3, 4], which is_empty() took for empty but the operations +# computing on the bounds did not ([3, 2] + [0, 1] gave [3, 3]). With an older +# GAOL, the results of Codac could be wrong without any error. The requirement +# holds for the GAOL Codac finds or builds (codac_gaol_find()), and for the users +# of the installed Codac too (codac_gaol_config_snippet()): Codac's interval +# operations are inline in its headers, so they are compiled against the GAOL of +# the program that includes them. The CMake package of the fork declares its +# version compatible with a requested one of the same major version +# (SameMajorVersion): find_package(gaol 4.3.2) accepts 4.3.2 up to, excluding, 5. +set(CODAC_GAOL_MIN_VERSION 4.3.2) + + +################################################################################ +# codac_gaol_portability_flags() +################################################################################ +# +# Returns in the flags of ibex_init_common() (see the top of this file) +# that are not those of interval arithmetic, and that Codac keeps whatever GAOL +# says: the top-level CMakeLists.txt puts them in CMAKE_CXX_FLAGS, where +# ibex_init_common() put them. They are returned as a list, one flag per +# element, because Codac also hands them over to its consumers, through +# CODAC_CXX_FLAGS and codac.pc; for the same reason, the Visual Studio "/D NAME" +# pairs of IBEX are spelt "/DNAME", which target_compile_options() cannot +# mistake for two duplicated "/D" and merge. The rest of ibex_init_common() is +# left out: the installation directories, the build type, the C++ standard and +# the uninstall target are Codac's own decisions, and the Debug-only -Wall +# -DDEBUG (/D DEBUG) have no use here, Codac choosing its own warnings and none +# of its code reading DEBUG. +# +# The two flags IBEX added for filib, which is not GAOL, are kept all the same, +# so that Codac goes on being compiled with the flags it had. +function(codac_gaol_portability_flags outvar) + + include(CheckCXXCompilerFlag) + set(flags "") + + if(MSVC) + list(APPEND flags /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /Zc:__cplusplus /Zc:strictStrings-) + elseif(APPLE) + # Due to warnings on macOS with filib + check_cxx_compiler_flag("-Wno-undefined-var-template" COMPILER_SUPPORTS_WNO_UNDEFINED_VAR_TEMPLATE) + if(COMPILER_SUPPORTS_WNO_UNDEFINED_VAR_TEMPLATE) + list(APPEND flags -Wno-undefined-var-template) + endif() + endif() + # Claim IEEE 754 double compatibility, for filib + list(APPEND flags -D__STDC_IEC_559__=1) + + set(${outvar} ${flags} PARENT_SCOPE) +endfunction() + + +################################################################################ +# codac_gaol_interval_flags() +################################################################################ +# +# Returns in the compilation flags interval arithmetic depends on, for +# a GAOL found by its files alone, without a CMake package or a gaol.pc to say +# them (see codac_gaol_find()). Without -frounding-math and its companions, the +# compiler is free to evaluate a floating-point expression at compile time in +# the default rounding mode, or to contract it, and a bound computed that way no +# longer encloses anything. +# +# From ibex_init_common() (see the top of this file), which appends the same +# flags to CMAKE_C_FLAGS and CMAKE_CXX_FLAGS, but for -ffloat-store, added only +# where the builds of GAOL add it. +function(codac_gaol_interval_flags outvar) + + include(CheckCXXCompilerFlag) + include(CheckCXXSourceCompiles) + set(flags "") + + if(MSVC) + list(APPEND flags /fp:strict) + else() + # Each flag is kept only where the compiler takes it. The check results are + # cached under the names ibex_init_common() gives them, e.g. + # COMPILER_SUPPORTS_FROUNDING_MATH for -frounding-math. + foreach(flag -frounding-math -fno-fast-math -ffp-contract=off + -ffp-mode=full -fp-model=strict -fp:strict -mpc64) + string(MAKE_C_IDENTIFIER "${flag}" _flag_id) + string(TOUPPER "COMPILER_SUPPORTS${_flag_id}" _flag_var) + check_cxx_compiler_flag("${flag}" ${_flag_var}) + if(${_flag_var}) + list(APPEND flags ${flag}) + endif() + endforeach() + + # Not from ibex_init_common(), but Codac's own: on a 32-bit x86 processor, + # doubles are computed in SSE2 rather than on the x87 FPU. Visual Studio, + # in the branch above, computes them in SSE2 already. + # + # Computed on the x87, GAOL's bounds and mathlib's results are only right + # while the precision of the x87 is set to 53 bits, and nothing keeps it + # so: mathlib's Init_Lib() sets it where mathlib has a version for 32-bit x86 + # (see cmake/mathlib/mathlib_configuration.h.in in the fork of GAOL), but + # GAOL, initialised right after, restores the default floating-point + # environment (gaol::init()), whose precision is 64 bits on Linux and + # with MinGW. Built for an i686 computing on the x87 (Clang 21 with + # -mcpu=i686), GAOL returned [1.99975, 1.99975] for exp([1,1]), and the + # bounds of exp, sin and cos missed the exact value for 4000, 3302 and 3913 + # of 4000 random arguments; built with the two flags below, the same + # program gave all of its 48000 bounds bit for bit as on x86_64. In SSE2, + # the precision is not a setting. SSE2 asks nothing more of the processor + # than GAOL's own builds do: they compile GAOL for SSE2 on these systems. + check_cxx_source_compiles(" + #if !defined(__i386__) + #error not a 32-bit x86 target + #endif + int main() { return 0; }" + CODAC_TARGET_IS_X86_32) + if(CODAC_TARGET_IS_X86_32) + check_cxx_compiler_flag("-msse2 -mfpmath=sse" COMPILER_SUPPORTS_MSSE2_MFPMATH_SSE) + if(COMPILER_SUPPORTS_MSSE2_MFPMATH_SSE) + list(APPEND flags -msse2 -mfpmath=sse) + endif() + endif() + + # -ffloat-store only where doubles are still computed in extended precision + # with the flags above (FLT_EVAL_METHOD not 0: a 32-bit x86 target computing + # on the x87), whose 80-bit registers keep more digits than a double: the + # condition under which the CMake, autotools and meson builds of the fork of + # GAOL add it. ibex_init_common() added it wherever the compiler took it. + # Where doubles are computed in double precision (SSE2, ARM and the other + # processors), it brings nothing to the bounds, and makes GCC store every + # floating-point variable in memory rather than in a register: with it, on + # x86_64 with GCC 9.4, x + y took 9.1 ns rather than 3.0, and sqrt(x) 41 ns + # rather than 8.2. + string(REPLACE ";" " " CMAKE_REQUIRED_FLAGS "${flags}") + check_cxx_source_compiles(" + #include + #if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD == 0 + #error doubles are computed in double precision + #endif + int main() { return 0; }" + CODAC_DOUBLES_IN_EXTENDED_PRECISION) + unset(CMAKE_REQUIRED_FLAGS) + if(CODAC_DOUBLES_IN_EXTENDED_PRECISION) + check_cxx_compiler_flag(-ffloat-store COMPILER_SUPPORTS_FFLOAT_STORE) + if(COMPILER_SUPPORTS_FFLOAT_STORE) + list(APPEND flags -ffloat-store) + endif() + endif() + + # Not from ibex_init_common() either: a warning when the compiler takes + # -frounding-math but says it does not honour the rounding direction on the + # target, as Clang does for 32-bit ARM processors ("overriding currently + # unsupported rounding mode on this target"). It then optimises the + # negations by which GAOL rounds downward with the rounding direction set + # upward, and no flag or change to GAOL can prevent it. Built by Clang 21 for + # 32-bit ARM, 4556 of 16000 random products, squares and cubes computed by + # GAOL did not enclose their exact value; built by GCC 15, none. The builds + # of the fork of GAOL refuse such a compiler, and so do its headers; the + # warning remains for a GAOL found without its CMake package or its gaol.pc. + if(COMPILER_SUPPORTS_FROUNDING_MATH) + set(CMAKE_REQUIRED_FLAGS "-frounding-math") + check_cxx_source_compiles("int main() { return 0; }" CODAC_COMPILER_HONOURS_ROUNDING_MATH + FAIL_REGEX "unsupported rounding mode") + unset(CMAKE_REQUIRED_FLAGS) + if(NOT CODAC_COMPILER_HONOURS_ROUNDING_MATH) + message(WARNING "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} does not honour the rounding " + "direction on this target (-frounding-math): the intervals computed by Codac may not " + "enclose the values they should. Use a compiler that does, such as GCC.") + endif() + endif() + endif() + + set(${outvar} ${flags} PARENT_SCOPE) +endfunction() + + +################################################################################ +# codac_gaol_step( ...) +################################################################################ +# +# Runs one step of codac_gaol_build(), its output written to , and +# stops the configuration with that output when the step fails, as +# LOG_OUTPUT_ON_FAILURE does for an ExternalProject. +function(codac_gaol_step description log) + execute_process(COMMAND ${ARGN} + OUTPUT_FILE "${log}" ERROR_FILE "${log}" + RESULT_VARIABLE result) + if(NOT result EQUAL 0) + file(READ "${log}" output) + message(FATAL_ERROR "GAOL: ${description} failed (${result}). Its output, in ${log}:\n${output}") + endif() +endfunction() + + +################################################################################ +# codac_gaol_build() +################################################################################ +# +# Downloads GAOL, builds it and mathlib in Release with the CMake build of the +# fork, and installs them in the build tree with its installer, all while Codac +# is configured; sets CODAC_GAOL_INSTALL_TREE, where they are installed, in the +# caller's scope, for codac_gaol_find() to find the CMake package of GAOL there. +# GAOL and mathlib are also installed with Codac, by the same installer, in +# CODAC_INSTALL_INCLUDEDIR_3RD and CODAC_INSTALL_LIBDIR_3RD, since the Codac +# libraries are of no use without them. +# +# While Codac is configured, and not while it is built, as with an +# ExternalProject: the flags and the libraries of GAOL are read from the CMake +# package it installs, which has to exist for find_package() to read it. +# +# A project of its own, as in IBEX's build of GAOL with its autotools, rather +# than the FetchContent that Eigen and Catch2 are brought in with, which would +# build GAOL as a part of this project. Kept apart, GAOL is compiled with the +# flags it chooses, and not with Codac's warnings, and it is always built in +# Release, whatever the configuration of Codac, which is what the MSVC runtime +# choice of the top-level CMakeLists.txt counts on. CMAKE_CXX_FLAGS and +# CMAKE_C_FLAGS are handed over as they are when this is called, before Codac +# adds its own flags to them. +# +# GAOL comes from the head of the master branch of the fork (version 4.3.1 of +# GAOL), so that the fixes pushed to the fork reach Codac without a change here. +# Cloned with Git, the sources are brought up to date with the branch at each +# configuration of Codac, which needs network access (without it, the sources +# already downloaded are built, with a warning), and only what a new commit +# changes is compiled again. Without Git, the archive GitHub makes of the +# branch is downloaded instead, only once per build directory: a new commit +# reaches such a build once _deps/gaol is deleted from it. The fork's CMake +# build downloads mathlib 2.1.1 from Frederic Goualard's site, checks its +# checksum, and builds and installs it along with GAOL. +function(codac_gaol_build) + + set(_work "${CODAC_GAOL_WORK_DIR}") + set(_source "${_work}/src") + set(_binary "${_work}/build") + set(_install "${_work}/install") + file(MAKE_DIRECTORY "${_work}") + + message(STATUS "GAOL: downloading the fork of GAOL, building it and installing it in ${_work}") + + # The sources + find_package(Git QUIET) + if(GIT_FOUND) + if(NOT EXISTS "${_source}/.git") + file(REMOVE_RECURSE "${_source}") + # The files as they are in the repository, whatever core.autocrlf says + # (true on the Windows runners of GitHub Actions). + codac_gaol_step("cloning https://github.com/Jordan08/GAOL.git" "${_work}/download.log" + "${GIT_EXECUTABLE}" clone --depth 1 --branch master --config core.autocrlf=false + https://github.com/Jordan08/GAOL.git "${_source}") + else() + execute_process(COMMAND "${GIT_EXECUTABLE}" -C "${_source}" fetch --depth 1 origin master + OUTPUT_FILE "${_work}/update.log" ERROR_FILE "${_work}/update.log" + RESULT_VARIABLE _fetch_result) + if(_fetch_result EQUAL 0) + codac_gaol_step("updating the sources with the master branch" "${_work}/update.log" + "${GIT_EXECUTABLE}" -C "${_source}" reset --hard FETCH_HEAD) + else() + message(WARNING "GAOL could not be brought up to date with the master branch of " + "https://github.com/Jordan08/GAOL.git (see ${_work}/update.log): " + "the sources downloaded before are built.") + endif() + endif() + elseif(NOT EXISTS "${_source}/CMakeLists.txt") + file(DOWNLOAD https://github.com/Jordan08/GAOL/archive/refs/heads/master.zip "${_work}/GAOL-master.zip" + STATUS _download_status LOG _download_log) + list(GET _download_status 0 _download_code) + if(NOT _download_code EQUAL 0) + message(FATAL_ERROR "GAOL: downloading https://github.com/Jordan08/GAOL/archive/refs/heads/master.zip " + "failed (${_download_status}):\n${_download_log}") + endif() + file(REMOVE_RECURSE "${_work}/GAOL-master" "${_source}") + codac_gaol_step("extracting GAOL-master.zip" "${_work}/download.log" + "${CMAKE_COMMAND}" -E chdir "${_work}" "${CMAKE_COMMAND}" -E tar xf GAOL-master.zip) + file(RENAME "${_work}/GAOL-master" "${_source}") + endif() + + # The configuration of GAOL, written as an initial cache (-C) rather than on + # the command line, where the ";" of a list such as CMAKE_OSX_ARCHITECTURES + # would split an argument in two. FORCE, so that a value changed since the + # last configuration of Codac replaces the one in GAOL's cache. + # + # Every value below has to come out the same at each configuration of Codac + # that changes nothing: GAOL is configured again as soon as one of them + # differs, and everything in Codac that includes an interval may then be + # recompiled. This is why the top-level CMakeLists.txt enables C in project() + # rather than leaving it to a dependency, which changed CMAKE_C_COMPILER + # between the first configuration and the second. + set(_cache "") + macro(codac_gaol_cache_entry name type value) + string(APPEND _cache "set(${name} [==[${value}]==] CACHE ${type} \"\" FORCE)\n") + endmacro() + + codac_gaol_cache_entry(CMAKE_BUILD_TYPE STRING Release) + # Codac's Python modules link these archives into shared libraries. + codac_gaol_cache_entry(CMAKE_POSITION_INDEPENDENT_CODE BOOL ON) + # Where Codac is installed, which the gaol.pc GAOL installs with Codac names; + # the installation in the build tree gives its own prefix. + codac_gaol_cache_entry(CMAKE_INSTALL_PREFIX PATH "${CMAKE_INSTALL_PREFIX}") + codac_gaol_cache_entry(CMAKE_INSTALL_INCLUDEDIR PATH "${CODAC_INSTALL_INCLUDEDIR_3RD}") + codac_gaol_cache_entry(CMAKE_INSTALL_LIBDIR PATH "${CODAC_INSTALL_LIBDIR_3RD}") + # The fork's continuous integration runs its tests + codac_gaol_cache_entry(GAOL_BUILD_TESTS BOOL OFF) + # The mathlib the fork downloads and builds, never one installed on this + # machine: the installation of Codac counts on it + codac_gaol_cache_entry(GAOL_FIND_MATHLIB BOOL OFF) + # Without the fused multiply-add instructions of the processor, whose flags + # would reach every target linking GAOL: a library for any processor of the + # architecture + codac_gaol_cache_entry(GAOL_FMA BOOL OFF) + codac_gaol_cache_entry(CMAKE_CXX_FLAGS STRING "${CMAKE_CXX_FLAGS}") + codac_gaol_cache_entry(CMAKE_C_FLAGS STRING "${CMAKE_C_FLAGS}") + + # The generator is handed over below, with its platform (-A) and toolset (-T), + # but nothing else is: the compilers, the flags and the target have to be + # given explicitly, or GAOL would be built for another machine than the one + # Codac is built for. The Visual Studio and Xcode generators take their + # compilers from the toolset and ignore these. + if(NOT CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") + codac_gaol_cache_entry(CMAKE_CXX_COMPILER FILEPATH "${CMAKE_CXX_COMPILER}") + codac_gaol_cache_entry(CMAKE_C_COMPILER FILEPATH "${CMAKE_C_COMPILER}") + codac_gaol_cache_entry(CMAKE_MAKE_PROGRAM FILEPATH "${CMAKE_MAKE_PROGRAM}") + endif() + foreach(_var CMAKE_TOOLCHAIN_FILE CMAKE_MSVC_RUNTIME_LIBRARY CMAKE_GENERATOR_INSTANCE + CMAKE_OSX_DEPLOYMENT_TARGET CMAKE_OSX_SYSROOT CMAKE_OSX_ARCHITECTURES) + # Quoted: a list, as CMAKE_OSX_ARCHITECTURES can be, would give if() as many + # arguments as elements + if(NOT "${${_var}}" STREQUAL "") + codac_gaol_cache_entry(${_var} STRING "${${_var}}") + endif() + endforeach() + # Only when this build is itself a cross-compilation (as the macOS jobs of + # .github/workflows are, by setting CMAKE_SYSTEM_NAME explicitly): passing + # the host's own name would make GAOL's build believe it cross-compiles. + if(CMAKE_CROSSCOMPILING) + codac_gaol_cache_entry(CMAKE_SYSTEM_NAME STRING "${CMAKE_SYSTEM_NAME}") + if(CMAKE_SYSTEM_PROCESSOR) + codac_gaol_cache_entry(CMAKE_SYSTEM_PROCESSOR STRING "${CMAKE_SYSTEM_PROCESSOR}") + endif() + endif() + file(WRITE "${_work}/initial-cache.cmake" "${_cache}") + + set(_generator -G "${CMAKE_GENERATOR}") + if(CMAKE_GENERATOR_PLATFORM) + list(APPEND _generator -A "${CMAKE_GENERATOR_PLATFORM}") + endif() + if(CMAKE_GENERATOR_TOOLSET) + list(APPEND _generator -T "${CMAKE_GENERATOR_TOOLSET}") + endif() + + codac_gaol_step("configuring" "${_work}/configure.log" + "${CMAKE_COMMAND}" ${_generator} -C "${_work}/initial-cache.cmake" -S "${_source}" -B "${_binary}") + # Release whatever the configuration of Codac: --config is what a + # multi-configuration generator reads, and what the others ignore. In + # parallel as CMAKE_BUILD_PARALLEL_LEVEL says, when it is set. + codac_gaol_step("building" "${_work}/build.log" + "${CMAKE_COMMAND}" --build "${_binary}" --config Release) + # The installer of GAOL's build (cmake_install.cmake, which cmake --install + # runs from CMake 3.15 on), with the prefix of the build tree + codac_gaol_step("installing" "${_work}/install.log" + "${CMAKE_COMMAND}" "-DCMAKE_INSTALL_PREFIX=${_install}" -DCMAKE_INSTALL_CONFIG_NAME=Release + -P "${_binary}/cmake_install.cmake") + + # And with Codac, by the same installer, with the prefix Codac is installed + # under, as the installation of Codac gives it (cmake --install --prefix, + # CPack), and into DESTDIR when it is set, which it inherits. + install(CODE " + # GAOL and mathlib, installed by the installer of their CMake build (see + # codac_gaol_build() in scripts/CMakeModules/codac_gaol.cmake) + execute_process(COMMAND \"${CMAKE_COMMAND}\" \"-DCMAKE_INSTALL_PREFIX=\${CMAKE_INSTALL_PREFIX}\" + -DCMAKE_INSTALL_CONFIG_NAME=Release -P \"${_binary}/cmake_install.cmake\" + RESULT_VARIABLE _codac_gaol_install_result) + if(NOT _codac_gaol_install_result EQUAL 0) + message(FATAL_ERROR \"The installation of GAOL failed (\${_codac_gaol_install_result})\") + endif() + ") + + set(CODAC_GAOL_INSTALL_TREE "${_install}" PARENT_SCOPE) +endfunction() + + +################################################################################ +# codac_gaol_find() +################################################################################ +# +# Finds GAOL in the order given at the top of this file, or builds it, and +# defines Codac::gaol, the imported target through which the Codac libraries, +# and the targets linking them, get GAOL's include directories, flags and +# libraries. Sets in the caller's scope: +# +# CODAC_GAOL_FROM "package", "pkg-config" or "files" +# CODAC_GAOL_BUILT_HERE TRUE when codac_gaol_build() built it +# CODAC_GAOL_INSTALL_TREE where codac_gaol_build() installed it +# GAOL_VERSION +# +# An INTERFACE library rather than GAOL's own target, whatever provided GAOL: +# CODAC_LIBRARIES names it, and codac-config.cmake defines it again +# (codac_gaol_config_snippet()), which it could not do under the name gaol::gaol +# without clashing with a find_package(gaol) of the consumer. +function(codac_gaol_find) + + include(CheckCXXCompilerFlag) + set(_from "") + set(_built_here FALSE) + set(_version "") + + if(ENABLE_FIND_PACKAGE_GAOL) + + # 1. The CMake package of GAOL (gaol_DIR, or CMAKE_PREFIX_PATH). A gaol_DIR + # left by an earlier configuration of this build, naming the GAOL + # codac_gaol_build() installed in it, is not a GAOL of this machine: it is + # forgotten, and that GAOL is built again, up to date, if none is found. + string(FIND "${gaol_DIR}" "${CODAC_GAOL_WORK_DIR}/" _in_work_dir) + if(_in_work_dir EQUAL 0) + unset(gaol_DIR CACHE) + endif() + # The version is checked by the package's gaolConfigVersion.cmake, before the + # package is loaded: an older one defines no target, which would clash with + # the gaol::gaol of the GAOL found or built next. + find_package(gaol ${CODAC_GAOL_MIN_VERSION} CONFIG QUIET) + if(gaol_FOUND) + set(_from package) + set(_version "${gaol_VERSION}") + message(STATUS "Found GAOL ${gaol_VERSION}, CMake package in ${gaol_DIR}") + elseif(gaol_CONSIDERED_VERSIONS) + message(STATUS "Found GAOL ${gaol_CONSIDERED_VERSIONS} (CMake package ${gaol_CONSIDERED_CONFIGS}), " + "older than ${CODAC_GAOL_MIN_VERSION}: not used") + endif() + + # 2. pkg-config (PKG_CONFIG_PATH, or CMAKE_PREFIX_PATH). Not with Visual C++, + # whose libraries pkg-config does not name. A gaol.pc whose Cflags lack + # -frounding-math, as the one the meson build of Frederic Goualard's GAOL + # installs, does not describe the flags GAOL needs, and is not used. + if(NOT _from AND NOT MSVC) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + pkg_check_modules(CODAC_GAOL_PC QUIET IMPORTED_TARGET gaol) + if(CODAC_GAOL_PC_FOUND) + check_cxx_compiler_flag(-frounding-math COMPILER_SUPPORTS_FROUNDING_MATH) + if(NOT CODAC_GAOL_PC_VERSION OR CODAC_GAOL_PC_VERSION VERSION_LESS CODAC_GAOL_MIN_VERSION) + message(STATUS "Found gaol.pc ${CODAC_GAOL_PC_VERSION} in ${CODAC_GAOL_PC_PREFIX}, " + "older than ${CODAC_GAOL_MIN_VERSION}: not used") + elseif(COMPILER_SUPPORTS_FROUNDING_MATH AND NOT "-frounding-math" IN_LIST CODAC_GAOL_PC_CFLAGS_OTHER) + message(STATUS "Found gaol.pc in ${CODAC_GAOL_PC_PREFIX}, whose Cflags lack -frounding-math: not used") + else() + set(_from pkg-config) + set(_version "${CODAC_GAOL_PC_VERSION}") + message(STATUS "Found GAOL ${CODAC_GAOL_PC_VERSION}, gaol.pc in ${CODAC_GAOL_PC_PREFIX}") + endif() + endif() + endif() + endif() + + # 3. The files (GAOL_DIR and MATHLIB_DIR, or CMAKE_PREFIX_PATH) + if(NOT _from) + find_package(GAOL MODULE QUIET) + # FindGAOL.cmake reads the version in gaol/gaol_configuration.h; a GAOL that + # does not state it there is not known to be recent enough, and not used. + if(GAOL_FOUND AND (NOT GAOL_VERSION OR GAOL_VERSION VERSION_LESS CODAC_GAOL_MIN_VERSION)) + message(STATUS "Found GAOL ${GAOL_VERSION} in ${GAOL_INCDIR}, older than ${CODAC_GAOL_MIN_VERSION} " + "or of unknown version: not used") + elseif(GAOL_FOUND) + set(_from files) + set(_version "${GAOL_VERSION}") + message(STATUS "Found GAOL ${GAOL_VERSION} in ${GAOL_INCDIR}, without a CMake package or a gaol.pc: " + "compiled with the flags of interval arithmetic Codac determines") + endif() + endif() + endif() + + # 4. Built, and found as in 1 + if(NOT _from) + codac_gaol_build() + unset(gaol_DIR CACHE) + # The version is checked here too: without network access, codac_gaol_build() + # builds the sources it downloaded at an earlier configuration, which may be + # those of a version older than CODAC_GAOL_MIN_VERSION. Codac is then not + # configured, rather than configured on a GAOL it cannot trust. + find_package(gaol ${CODAC_GAOL_MIN_VERSION} CONFIG QUIET NO_DEFAULT_PATH + PATHS "${CODAC_GAOL_INSTALL_TREE}/${CODAC_INSTALL_LIBDIR_3RD}/cmake/gaol") + if(NOT gaol_FOUND AND gaol_CONSIDERED_VERSIONS) + message(FATAL_ERROR "The GAOL built in ${CODAC_GAOL_WORK_DIR} is version ${gaol_CONSIDERED_VERSIONS}, older " + "than ${CODAC_GAOL_MIN_VERSION}, the oldest Codac accepts: its sources could not be " + "brought up to date with the master branch of the fork (see the warning above). " + "Configure Codac again with network access (without Git, after deleting " + "${CODAC_GAOL_WORK_DIR}: the archive of the fork is only downloaded once per build " + "directory).") + elseif(NOT gaol_FOUND) + message(FATAL_ERROR "The CMake package of the GAOL built in ${CODAC_GAOL_WORK_DIR} was not found under " + "${CODAC_GAOL_INSTALL_TREE}/${CODAC_INSTALL_LIBDIR_3RD}/cmake/gaol.") + endif() + set(_from package) + set(_built_here TRUE) + set(_version "${gaol_VERSION}") + message(STATUS "GAOL ${gaol_VERSION} built and installed, CMake package in ${gaol_DIR}") + endif() + + add_library(Codac::gaol INTERFACE IMPORTED) + if(_from STREQUAL "package") + set_target_properties(Codac::gaol PROPERTIES INTERFACE_LINK_LIBRARIES gaol::gaol) + elseif(_from STREQUAL "pkg-config") + set_target_properties(Codac::gaol PROPERTIES INTERFACE_LINK_LIBRARIES PkgConfig::CODAC_GAOL_PC) + else() + codac_gaol_interval_flags(_interval_flags) + set_target_properties(Codac::gaol PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${GAOL_INCLUDE_DIRS}" + INTERFACE_COMPILE_OPTIONS "${_interval_flags}" + INTERFACE_LINK_LIBRARIES "${GAOL_LIBRARIES}") + # For Visual C++, GAOL declares its classes and functions + # __declspec(dllimport), as for a DLL, unless __GAOL_PUBLIC__ is defined + # (gaol/gaol_config.h). None of GAOL's builds -- its autotools and meson + # builds, and the CMake build of the fork -- defines + # _COMPILING__GAOL_PUBLIC__, with which GAOL would export a DLL: the GAOL + # Visual C++ links is a static library, which every file including its + # headers has to be told. gaol::gaol, of the CMake package, tells it the + # same way. + if(MSVC) + set_target_properties(Codac::gaol PROPERTIES INTERFACE_COMPILE_DEFINITIONS "__GAOL_PUBLIC__=") + endif() + endif() + + set(CODAC_GAOL_FROM "${_from}" PARENT_SCOPE) + set(CODAC_GAOL_BUILT_HERE ${_built_here} PARENT_SCOPE) + set(CODAC_GAOL_INSTALL_TREE "${CODAC_GAOL_INSTALL_TREE}" PARENT_SCOPE) + set(GAOL_VERSION "${_version}" PARENT_SCOPE) +endfunction() + + +################################################################################ +# codac_gaol_usage( ) +################################################################################ +# +# Returns what Codac::gaol gives the targets linking it, walked out of it and of +# the targets it links: its include directories, its compilation flags (with +# its definitions, as -D or /D) and what goes on the link line (library files, +# and flags). This is for what cannot link Codac::gaol: CODAC_CXX_FLAGS, codac.pc, +# and codac-config.cmake when GAOL has no CMake package. Generator expressions +# are left out, but for $, which is unwrapped: GAOL's targets +# have no other. +function(codac_gaol_usage include_dirs_var flags_var link_var) + + set(include_dirs "") + set(flags "") + set(link "") + set(queue Codac::gaol) + set(seen "") + + while(queue) + list(GET queue 0 item) + list(REMOVE_AT queue 0) + if(item MATCHES "^\\$$") + set(item "${CMAKE_MATCH_1}") + endif() + if(item IN_LIST seen OR item MATCHES "\\$<") + continue() + endif() + list(APPEND seen "${item}") + + if(NOT TARGET "${item}") + # A library file, a flag, or the name of a library of the system + if(item MATCHES "^-" OR IS_ABSOLUTE "${item}") + list(APPEND link "${item}") + else() + list(APPEND link "-l${item}") + endif() + continue() + endif() + + get_target_property(_value "${item}" INTERFACE_INCLUDE_DIRECTORIES) + if(_value) + list(APPEND include_dirs ${_value}) + endif() + get_target_property(_value "${item}" INTERFACE_COMPILE_OPTIONS) + if(_value) + list(APPEND flags ${_value}) + endif() + get_target_property(_value "${item}" INTERFACE_COMPILE_DEFINITIONS) + if(_value) + foreach(_definition ${_value}) + if(MSVC) + list(APPEND flags "/D${_definition}") + else() + list(APPEND flags "-D${_definition}") + endif() + endforeach() + endif() + + # An imported archive names its file either outright or per configuration. + # An INTERFACE library has no file at all, and before CMake 3.19 merely + # asking one for IMPORTED_LOCATION is a fatal error rather than an empty + # answer ("INTERFACE_LIBRARY targets may only have whitelisted + # properties") -- which is what Debian Bullseye, on CMake 3.18.4, reported. + # TYPE is whitelisted, so it can be asked first, as can the INTERFACE_ + # properties read above and below. + get_target_property(_type "${item}" TYPE) + if(NOT _type STREQUAL "INTERFACE_LIBRARY") + get_target_property(_location "${item}" IMPORTED_LOCATION) + if(NOT _location) + get_target_property(_configurations "${item}" IMPORTED_CONFIGURATIONS) + if(_configurations) + list(GET _configurations 0 _configuration) + get_target_property(_location "${item}" IMPORTED_LOCATION_${_configuration}) + endif() + endif() + if(_location) + list(APPEND link "${_location}") + endif() + endif() + + get_target_property(_value "${item}" INTERFACE_LINK_LIBRARIES) + if(_value) + list(APPEND queue ${_value}) + endif() + get_target_property(_value "${item}" INTERFACE_LINK_OPTIONS) + if(_value) + list(APPEND link ${_value}) + endif() + endwhile() + + foreach(_list include_dirs flags link) + list(FILTER ${_list} EXCLUDE REGEX "\\$<") + endforeach() + if(include_dirs) + list(REMOVE_DUPLICATES include_dirs) + endif() + if(flags) + list(REMOVE_DUPLICATES flags) + endif() + + set(${include_dirs_var} ${include_dirs} PARENT_SCOPE) + set(${flags_var} ${flags} PARENT_SCOPE) + set(${link_var} ${link} PARENT_SCOPE) +endfunction() + + +################################################################################ +# codac_gaol_pkg_config_path() +################################################################################ +# +# When is a path of the GAOL codac_gaol_build() installed in the build +# tree, turns it into the path GAOL is installed at with Codac, under the +# ${prefix} of codac.pc. +function(codac_gaol_pkg_config_path var) + if(CODAC_GAOL_BUILT_HERE) + string(FIND "${${var}}" "${CODAC_GAOL_INSTALL_TREE}/" _position) + if(_position EQUAL 0) + string(LENGTH "${CODAC_GAOL_INSTALL_TREE}/" _length) + string(SUBSTRING "${${var}}" ${_length} -1 _rest) + set(${var} "\${prefix}/${_rest}" PARENT_SCOPE) + endif() + endif() +endfunction() + + +################################################################################ +# codac_gaol_config_snippet() +################################################################################ +# +# Returns the lines of codac-config.cmake that define Codac::gaol for a consumer +# of the installed Codac, which CODAC_LIBRARIES names -- the +# counterpart of the ibex-config-gaol.cmake and ibex-config-ultim.cmake files +# that create_target_import_and_export() writes for IBEX. +# +# - A GAOL with a CMake package is found again by find_package(gaol CONFIG): +# the GAOL Codac built under the prefix Codac is installed under, by a path +# relative to the configuration file, since the prefix may be moved; a GAOL +# found on this machine where it was found, before the usual search. +# - A GAOL found by pkg-config or by its files is named by the paths it was +# found at, with the flags it was compiled with. +# - find_package(gaol) asks for CODAC_GAOL_MIN_VERSION or later, as when Codac +# was built: the consumer compiles Codac's inline interval operations against +# the GAOL it finds, and a package search path of the consumer could lead to +# an older one. +function(codac_gaol_config_snippet outvar) + + if(CODAC_GAOL_FROM STREQUAL "package") + if(CODAC_GAOL_BUILT_HERE) + file(RELATIVE_PATH _to_prefix "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_CMAKE}" "${CMAKE_INSTALL_PREFIX}") + set(_find "get_filename_component(_codac_prefix \"\${CMAKE_CURRENT_LIST_DIR}/${_to_prefix}\" ABSOLUTE) + find_package(gaol ${CODAC_GAOL_MIN_VERSION} CONFIG REQUIRED NO_DEFAULT_PATH + PATHS \"\${_codac_prefix}/${CODAC_INSTALL_LIBDIR_3RD}/cmake/gaol\")") + else() + set(_find "find_package(gaol ${CODAC_GAOL_MIN_VERSION} CONFIG REQUIRED HINTS \"${gaol_DIR}\")") + endif() + set(_properties "INTERFACE_LINK_LIBRARIES gaol::gaol") + else() + codac_gaol_usage(_include_dirs _flags _link) + set(_find "") + set(_properties "INTERFACE_INCLUDE_DIRECTORIES \"${_include_dirs}\" + INTERFACE_COMPILE_OPTIONS \"${_flags}\" + INTERFACE_LINK_LIBRARIES \"${_link}\"") + endif() + + set(${outvar} " + # GAOL, the interval arithmetic library Codac is built upon, with mathlib + # (libultim) and the flags of interval arithmetic, as Codac::gaol, which + # CODAC_LIBRARIES names. + ${_find} + if(NOT TARGET Codac::gaol) + add_library(Codac::gaol INTERFACE IMPORTED) + set_target_properties(Codac::gaol PROPERTIES + ${_properties}) + endif() +" PARENT_SCOPE) +endfunction() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 28fd1c4fc..b8b0ab390 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,9 +24,45 @@ set(CODAC_PKG_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc) - set(CODAC_PKG_CONFIG_CFLAGS "-I\${includedir}/ibex") + set(CODAC_PKG_CONFIG_CFLAGS "") set(CODAC_PKG_CONFIG_LIBS "-L\${libdir}") + # Codac's own flags, which this build puts in CMAKE_CXX_FLAGS + # (codac_gaol_portability_flags() in scripts/CMakeModules/codac_gaol.cmake), + # and the interval arithmetic flags GAOL gives through Codac::gaol, + # CODAC_INTERVAL_CXX_FLAGS, set in the top-level CMakeLists.txt. A CMake + # consumer gets them through CODAC_CXX_FLAGS; a pkg-config consumer has no + # equivalent, and compiling Codac's headers without -frounding-math silently + # gives up the guarantee the whole library rests on. + foreach(_codac_flags CODAC_PORTABILITY_CXX_FLAGS CODAC_INTERVAL_CXX_FLAGS) + if(${_codac_flags}) + string(REPLACE ";" " " _codac_pkg_config_flags "${${_codac_flags}}") + string(APPEND CODAC_PKG_CONFIG_CFLAGS " ${_codac_pkg_config_flags}") + endif() + endforeach() + + # GAOL's include directories and libraries, as Codac::gaol gives them + # (codac_gaol_usage(), in the top-level CMakeLists.txt): where the GAOL found + # on this machine is, or, for the GAOL codac_gaol_build() built, where it is + # installed with Codac, under ${prefix}. They take the place of the + # "Requires: ibex" and -I\${includedir}/ibex of the time Codac reached GAOL + # through IBEX. GAOL itself is not named in "Requires:": a GAOL built with + # Codac installs its gaol.pc under lib/codac-3rd, where pkg-config does not + # look, and a GAOL found by its files has none. The directories the compiler + # searches of its own accord are left out, as CMake leaves them out of its + # command lines; the libraries are named by their full path, gaol before + # ultim, which it depends on. + foreach(dir ${CODAC_GAOL_INCLUDE_DIRS}) + if(NOT dir IN_LIST CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES) + codac_gaol_pkg_config_path(dir) + string(APPEND CODAC_PKG_CONFIG_CFLAGS " -I${dir}") + endif() + endforeach() + foreach(item ${CODAC_GAOL_LINK_ITEMS}) + codac_gaol_pkg_config_path(item) + string(APPEND CODAC_PKG_CONFIG_LIBS " ${item}") + endforeach() + file(GENERATE OUTPUT ${CODAC_PKG_CONFIG_FILE} CONTENT " prefix=${CMAKE_INSTALL_PREFIX} includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR} @@ -36,7 +72,7 @@ Description: ${PROJECT_DESCRIPTION} Url: ${PROJECT_HOMEPAGE_URL} Version: ${PROJECT_VERSION} - Requires: ibex + Requires: Cflags: ${CODAC_PKG_CONFIG_CFLAGS} Libs: ${CODAC_PKG_CONFIG_LIBS} ") @@ -49,6 +85,18 @@ set(CODAC_CMAKE_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake) + # The definition of Codac::gaol, written by + # scripts/CMakeModules/codac_gaol.cmake, which knows where GAOL came from. + codac_gaol_config_snippet(CODAC_GAOL_CONFIG_SNIPPET) + + # Codac's own flags (codac_gaol_portability_flags()) and the interval + # arithmetic flags GAOL gives (-frounding-math, etc.), for external projects. + # In that order, which is the order this build hands them to the compiler in + # -- the first through CMAKE_CXX_FLAGS, the second through Codac::gaol, whose + # options come after a target's own -- and decides which of two conflicting + # flags wins. + set(CODAC_CXX_FLAGS ${CODAC_PORTABILITY_CXX_FLAGS} ${CODAC_INTERVAL_CXX_FLAGS}) + file(WRITE ${CODAC_CMAKE_CONFIG_FILE} "# Try to find Codac # This file has been generated by CMake @@ -67,14 +115,24 @@ find_library(CODAC_UNSUPPORTED_LIBRARY NAMES ${PROJECT_NAME}-unsupported PATH_SUFFIXES lib) - find_package(IBEX REQUIRED) +${CODAC_GAOL_CONFIG_SNIPPET} + # Projects written for a Codac that depended on IBEX call ibex_init_common() + # after find_package(CODAC), as the manual used to tell them to, for the + # interval arithmetic flags. Those flags now come with CODAC_CXX_FLAGS, and + # this ibex_init_common() does nothing: it is only there so that such + # projects still configure. A project that finds IBEX itself, before or + # after Codac, gets IBEX's own function instead. + if(NOT COMMAND ibex_init_common) + function(ibex_init_common) + endfunction() + endif() 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} Codac::gaol) 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_CXX_FLAGS}\") ") if(WITH_PYTHON) @@ -111,7 +169,7 @@ endif() file(APPEND ${CODAC_CMAKE_CONFIG_FILE} " - set(CODAC_LIBRARIES \${CODAC_LIBRARIES} \${CODAC_GRAPHICS_LIBRARY} \${CODAC_CORE_LIBRARY}) + set(CODAC_LIBRARIES \${CODAC_LIBRARIES} \${CODAC_GRAPHICS_LIBRARY} \${CODAC_CORE_LIBRARY} Codac::gaol) ") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index db02e9526..3337a364b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -327,7 +327,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_CURRENT_SOURCE_DIR}/trajectory ) - target_link_libraries(${PROJECT_NAME}-core PUBLIC Ibex::ibex Eigen3::Eigen Threads::Threads) + target_link_libraries(${PROJECT_NAME}-core PUBLIC Codac::gaol Eigen3::Eigen Threads::Threads) ################################################################################ diff --git a/src/core/tools/codac2_Approx.h b/src/core/tools/codac2_Approx.h index 721da8249..ef0583af5 100644 --- a/src/core/tools/codac2_Approx.h +++ b/src/core/tools/codac2_Approx.h @@ -40,7 +40,25 @@ namespace codac2 friend bool operator==(const T& x1, const Approx& x2) { if constexpr(std::is_same_v) - return std::fabs(x1-x2._x) < x2._eps; + { + 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(std::isinf(x2._x)) + return x1 == x2._x; + + 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; diff --git a/src/extensions/capd/CMakeLists.txt b/src/extensions/capd/CMakeLists.txt index 0214cb955..d5a8b2bd4 100644 --- a/src/extensions/capd/CMakeLists.txt +++ b/src/extensions/capd/CMakeLists.txt @@ -22,7 +22,7 @@ list(APPEND CODAC_CAPD_SRC #endif() add_library(${PROJECT_NAME}-capd ${CODAC_CAPD_SRC}) - target_link_libraries(${PROJECT_NAME}-capd PUBLIC ${PROJECT_NAME}-core Ibex::ibex Eigen3::Eigen capd::capd) + target_link_libraries(${PROJECT_NAME}-capd PUBLIC ${PROJECT_NAME}-core Codac::gaol Eigen3::Eigen capd::capd) ################################################################################ diff --git a/src/extensions/sympy/CMakeLists.txt b/src/extensions/sympy/CMakeLists.txt index d92bae572..0bc1d9be7 100644 --- a/src/extensions/sympy/CMakeLists.txt +++ b/src/extensions/sympy/CMakeLists.txt @@ -31,7 +31,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(${PROJECT_NAME}-sympy ${CODAC_SYMPY_SRC}) - target_link_libraries(${PROJECT_NAME}-sympy PUBLIC ${PROJECT_NAME}-core Ibex::ibex Eigen3::Eigen) + target_link_libraries(${PROJECT_NAME}-sympy PUBLIC ${PROJECT_NAME}-core Codac::gaol Eigen3::Eigen) target_link_libraries(${PROJECT_NAME}-sympy PRIVATE pybind11::pybind11) ################################################################################ diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index 453a547d1..f6d79ef6d 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -53,7 +53,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/paver # deprecated, to be removed ${CMAKE_CURRENT_SOURCE_DIR}/styles ) - target_link_libraries(${PROJECT_NAME}-graphics PUBLIC ${PROJECT_NAME}-core Ibex::ibex Eigen3::Eigen ${PROJECT_NAME}-core) + target_link_libraries(${PROJECT_NAME}-graphics PUBLIC ${PROJECT_NAME}-core Codac::gaol Eigen3::Eigen ${PROJECT_NAME}-core) ################################################################################ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index dff12a6f4..8dabdd6ad 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -107,7 +107,9 @@ list(APPEND SRC_TESTS # listing files without extension core/tools/codac2_tests_serialization core/tools/codac2_tests_transformations core/tools/codac2_tests_trunc - core/tools/ibex/codac2_tests_ibex + # core/tools/ibex/codac2_tests_ibex is not built: it includes the headers of + # IBEX, which Codac no longer depends on (the conversions it tests are only + # compiled when a program includes IBEX before them) ../doc/manual/manual/tools/src core/trajectory/codac2_tests_AnalyticTraj @@ -167,7 +169,7 @@ foreach(SRC_TEST ${SRC_TESTS}) 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) + target_link_libraries(${TEST_NAME} PUBLIC ${CODAC_LIBRARIES} PRIVATE Catch2::Catch2WithMain) if(WITH_PYTHON AND SRC_TEST MATCHES "extensions/sympy/") target_link_libraries(${TEST_NAME} PRIVATE ${PROJECT_NAME}-sympy pybind11::embed) 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/test_codac/CMakeLists.txt b/tests/test_codac/CMakeLists.txt index 0f4479bc9..c94d7dfb7 100644 --- a/tests/test_codac/CMakeLists.txt +++ b/tests/test_codac/CMakeLists.txt @@ -8,17 +8,6 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Adding IBEX - - # In case you installed IBEX in a local directory, you need - # to specify its path with the CMAKE_PREFIX_PATH option. - # set(CMAKE_PREFIX_PATH "~/ibex-lib/build_install") - set(CMAKE_PREFIX_PATH "../ibex") - - find_package(IBEX REQUIRED) - ibex_init_common() # IBEX should have installed this function - message(STATUS "Found IBEX version ${IBEX_VERSION}") - # Adding Codac # In case you installed Codac in a local directory, you need @@ -39,4 +28,4 @@ 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) + target_link_libraries(${PROJECT_NAME} PUBLIC ${CODAC_LIBRARIES})