File: CgalNumTraits.hpp

package info (click to toggle)
yade 2025.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,308 kB
  • sloc: cpp: 93,298; python: 50,409; sh: 577; makefile: 162
file content (199 lines) | stat: -rw-r--r-- 9,995 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*************************************************************************
*  2019 Janek Kozicki                                                    *
*                                                                        *
*  This program is free software; it is licensed under the terms of the  *
*  GNU General Public License v2 or later. See file LICENSE for details. *
*************************************************************************/

#if (((not defined(YADE_DISABLE_REAL_MULTI_HP)) or (YADE_REAL_BIT > 64)) and defined(YADE_CGAL) and (not defined(CGAL_NUM_TRAITS_HPP)))
#define CGAL_NUM_TRAITS_HPP

#ifndef YADE_REAL_MATH_NAMESPACE
#error "This file cannot be included alone, include Real.hpp instead"
#endif

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Interval_nt.h>
#include <CGAL/NT_converter.h>
#include <CGAL/number_type_basic.h>
// cmake already checks for GMP presence when checking for CGAL
#include <CGAL/gmpxx.h>
// newer CGAL uses boost multiprecision
#include <boost/multiprecision/gmp.hpp>

// The traits are those listed in documentation:
//    https://doc.cgal.org/latest/Algebraic_foundations/index.html
//    https://doc.cgal.org/latest/Algebraic_foundations/group__PkgAlgebraicFoundationsRef.html
//
// The functions used by CGAL are on top of https://doc.cgal.org/latest/Algebraic_foundations/group__PkgAlgebraicFoundationsRef.html
// And are implemented below. If some more are necessary they can be added by redirecting to ::yade::math.
// But then a check for them must be also added to:
//    py/high-precision/_math.cpp
//    py/tests/testMath.py
// To make sure they work correctly for all precision types represented by Real.
//
// There is some extra check #ifdef CGAL_CFG_IEEE_754_BUG for long double case in /usr/include/CGAL/long_double.h.
// So #if defined(CGAL_CFG_IEEE_754_BUG) and (Real == long double) then it falls back to CGAL's long double specific functions.

namespace CGAL {

template <int levelHP> class RealHP_Is_valid : public CGAL::cpp98::unary_function<::yade::RealHP<levelHP>, bool> {
public:
	bool operator()(const ::yade::RealHP<levelHP>& x) const
	{
// When CGAL detects IEEE bug, then long double needs special treatment:
#ifdef CGAL_CFG_IEEE_754_BUG // and (YADE_REAL_BIT == 80)
		if (std::is_same<::yade::RealHP<levelHP>, long double>::value) { return Is_valid<long double>()(static_cast<long double>(x)); }
#endif
		return not ::yade::math::isnan(x);
	}
};

template <int levelHP> class RealHP_Algebraic_structure_traits : public Algebraic_structure_traits_base<::yade::RealHP<levelHP>, Field_with_kth_root_tag> {
public:
	typedef Tag_false               Is_exact;
	typedef Tag_true                Is_numerical_sensitive;
	typedef ::yade::RealHP<levelHP> Type;

	/* if they become necessary add tests in py/tests/testMath.py, py/high-precision/_math.cpp
	class IsZero : public CGAL::cpp98::unary_function<Type, bool> {
	public:
		bool operator()(const Type& x) const { return x == 0; }
	};

	class IsOne : public CGAL::cpp98::unary_function<Type, bool> {
	public:
		bool operator()(const Type& x) const { return x == 1; }
	};
*/

	class Square : public CGAL::cpp98::unary_function<Type, Type> {
	public:
		Type operator()(const Type& x) const { return ::yade::math::pow(x, 2); }
	};

	class Sqrt : public CGAL::cpp98::unary_function<Type, Type> {
	public:
		Type operator()(const Type& x) const
		{
			return ::yade::math::sqrt(x);
			// note: that's what would be called for long double case:
			// return Algebraic_structure_traits<long double>::Sqrt()(static_cast<long double>(x));
		}
	};

	class Kth_root : public CGAL::cpp98::binary_function<int, Type, Type> {
	public:
		Type operator()(int k, const Type& x) const
		{
			CGAL_precondition_msg(k > 0, "'k' must be positive for k-th roots");
			return ::yade::math::pow(x, static_cast<::yade::RealHP<levelHP>>(1.0) / static_cast<::yade::RealHP<levelHP>>(k));
			// note: that's what would be called for long double case:
			// return Algebraic_structure_traits<long double>::Kth_root()(k, static_cast<long double>(x));
		};
	};
};


template <int levelHP> class RealHP_embeddable_traits : public INTERN_RET::Real_embeddable_traits_base<::yade::RealHP<levelHP>, CGAL::Tag_true> {
public:
	typedef ::yade::RealHP<levelHP> Type;
	class To_interval : public CGAL::cpp98::unary_function<Type, std::pair<double, double>> {
	public:
		std::pair<double, double> operator()(const Type& x) const { return (Interval_nt<>(static_cast<double>(x)) + Interval_nt<>::smallest()).pair(); }
	};

	/* if they become necessary add tests in py/tests/testMath.py, py/high-precision/_math.cpp
	class Abs : public CGAL::cpp98::unary_function<Type, Type> {
	public:
		Type operator()(const Type& x) const { return ::yade::math::abs(x); }
	};
*/

	class Sgn : public CGAL::cpp98::unary_function<Type, CGAL::Sign> {
	public:
		CGAL::Sign operator()(const Type& x) const
		{
			// see /usr/include/CGAL/enum.h
			return CGAL::Sign(::yade::math::sign(x));
		}
	};

	class Is_finite : public CGAL::cpp98::unary_function<Type, bool> {
	public:
		bool operator()(const Type& x) const
		{
#if defined(CGAL_CFG_IEEE_754_BUG) and (YADE_REAL_BIT == 80)
			// use CGAL workaround for long double if there is an IEEE compiler bug
			return Real_embeddable_traits<long double>::Is_finite()(static_cast<long double>(x));
#else
			return ::yade::math::isfinite(x);
#endif
		}
	};
};

// There are two ways to avoid this macro (hint: the best is to use C++20). See file lib/high-precision/RealHPEigenCgal.hpp for details.
#define YADE_CGAL_SUPPORT_REAL_HP(levelHP)                                                                                                                     \
	template <> struct Is_valid<::yade::RealHP<levelHP>> : public RealHP_Is_valid<levelHP> {                                                               \
	};                                                                                                                                                     \
	template <> struct Algebraic_structure_traits<::yade::RealHP<levelHP>> : public RealHP_Algebraic_structure_traits<levelHP> {                           \
	};                                                                                                                                                     \
	template <> struct Real_embeddable_traits<::yade::RealHP<levelHP>> : public RealHP_embeddable_traits<levelHP> {                                        \
	};                                                                                                                                                     \
	namespace internal {                                                                                                                                   \
		template <> struct Exact_field_selector<::yade::RealHP<levelHP>> {                                                                             \
			using Type = boost::multiprecision::mpq_rational;                                                                                      \
		};                                                                                                                                             \
		template <> struct Exact_ring_selector<::yade::RealHP<levelHP>> {                                                                              \
			using Type = boost::multiprecision::mpq_rational;                                                                                      \
		};                                                                                                                                             \
	}

// When faster CGAL computations are needed, we might want to use and specialize converter for /usr/include/CGAL/Lazy_exact_nt.h

// TODO: - make sure that RealHP<…> works correctly with all possible GMP mpq_rational combinations.
//         Before yade switches to C++20 it will requiree the same explicit template specializations macros as for the others in file RealHPEigenCgal.hpp
// NOTE: it passes all the tests so far. So if a mistake is discovered here, then please make sure to add that case to the tests.
template <typename GMP1, typename GMP2>
struct NT_converter<::yade::Real, __gmp_expr<GMP1, GMP2>>
        : public CGAL::cpp98::unary_function<::yade::Real, NT_converter<::yade::Real, __gmp_expr<GMP1, GMP2>>> {
	__gmp_expr<GMP1, GMP2> operator()(const ::yade::Real& a) const
	{
		mpq_t ret;
		mpq_init(ret);
		mpq_set(ret, boost::multiprecision::mpq_rational(static_cast<::yade::math::UnderlyingReal>(a)).backend().data());
		return __gmp_expr<GMP1, GMP2>(ret);
	}
};

template <>
struct NT_converter<::yade::Real, boost::multiprecision::mpq_rational>
        : public CGAL::cpp98::unary_function<::yade::Real, NT_converter<::yade::Real, boost::multiprecision::mpq_rational>> {
	boost::multiprecision::mpq_rational operator()(const ::yade::Real& a) const
	{
		return boost::multiprecision::mpq_rational(static_cast<::yade::math::UnderlyingReal>(a));
	}
};

} // namespace CGAL

namespace std {

// BUG in CGAL: file /usr/include/CGAL/PCA_util_Eigen.h is calling std::sqrt !
// They should call functions without qualification to let ADL take care of that, like boost does this:
//  → https://www.boost.org/doc/libs/1_72_0/libs/math/doc/html/math_toolkit/float128_hints.html
//  → https://www.boost.org/doc/libs/1_60_0/libs/multiprecision/doc/html/boost_multiprecision/tut/floats/fp_eg/jel.html
//  → https://www.boost.org/doc/libs/1_72_0/libs/math/doc/html/math_toolkit/real_concepts.html
// search for string 'unqualified'. Perhaps we should file a bugreport to CGAL.

// OK, bug reported to  CGAL: https://github.com/CGAL/cgal/issues/4527
//                     Eigen: https://gitlab.com/libeigen/eigen/issues/1823
// we can disable this later by #ifdef-ing a CGAL version.
#if CGAL_VERSION_NR < CGAL_VERSION_NUMBER(5, 1, 1)
using ::yade::math::pow;
using ::yade::math::sqrt;
#endif
}

#endif // CGAL_NUM_TRAITS_HPP