File: RealHPConfig.cpp

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 (131 lines) | stat: -rw-r--r-- 8,310 bytes parent folder | download | duplicates (3)
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
/*************************************************************************
*  2020 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. *
*************************************************************************/

#include <lib/high-precision/RealHPConfig.hpp>
#include <lib/high-precision/RealIO.hpp>

namespace forCtags {
struct RealHPConfig { // these are directly assigned below in pyRegister(), they are not C++ variables. So let ctags find them here, struct forCtags::RealHPConfig is not used anywhere (except for ctags).
	bool isFloat128Broken;
	bool isEnabledRealHP;
}; // for ctags
}

namespace yade {
namespace math {

	int RealHPConfig::extraStringDigits10 { 4 };
	// https://stackoverflow.com/questions/14395967/proper-initialization-of-static-constexpr-array-in-class-template
	// should not have a (duplicate) initializer in its namespace scope definition
	const constexpr size_t                                       RealHPConfig::sizeEigenCgal;
	const constexpr size_t                                       RealHPConfig::sizeMinieigen;
	const constexpr std::array<int, RealHPConfig::sizeEigenCgal> RealHPConfig::supportedByEigenCgal;
	const constexpr std::array<int, RealHPConfig::sizeMinieigen> RealHPConfig::supportedByMinieigen;

	void RealHPConfig::pyRegister()
	{
		namespace py = ::boost::python;
		py::scope cl = py::class_<RealHPConfig>(
		                       "RealHPConfig",
		                       // The docstrings can use syntax
		                       //  :param ……: ………
		                       //  :return: …….
		                       // For details see https://thomas-cokelaer.info/tutorials/sphinx/docstring_python.html
		                       // docstrings for static properties are forbidden in python. The solution is to put it into __doc__
		                       // https://stackoverflow.com/questions/25386370/docstrings-for-static-properties-in-boostpython
		                       R"""(
``RealHPConfig`` class provides information about RealHP<N> type.

:cvar extraStringDigits10: this static variable allows to control how many extra digits to use when converting to decimal strings. Assign a different value to it to affect the string conversion done in :ysrccommit:`C++ ↔ python conversions <26bffeb7ef4fd0d15e4faa025f68f97381621f04/lib/high-precision/ToFromPythonConverter.hpp#L37>` as well as in :ysrccommit:`all other conversions<26bffeb7ef4fd0d15e4faa025f68f97381621f04/lib/high-precision/RealIO.hpp#L34>`. Be careful, because values smaller than 3 can fail the round trip conversion test.

:cvar isFloat128Broken: provides runtime information if Yade was compiled with g++ version < 9.2.1 and thus ``boost::multiprecision::float128`` cannot work.

:cvar isEnabledRealHP:  provides runtime information ``RealHP<N>`` is available for N higher than 1.

:cvar workaroundSlowBoostBinFloat: ``boost::multiprecision::cpp_bin_float`` has some problem that importing it in python is very slow when these functions are exported: erf, erfc, lgamma, tgamma. In such case the python ``import yade.math`` can take more than minute. The workaround is to make them unavailable in python for higher N values. See invocation of IfConstexprForSlowFunctions in :ysrccommit:`_math.cpp<61fc7f208027344e27dc832052b3f8c911a5909e/py/high-precision/_math.cpp#L672>`. This variable contains the highest N in which these functions are available. It equals to highest N when ``boost::multiprecision::cpp_bin_float`` is not used.

)""")
		                       .add_static_property(
		                               "extraStringDigits10",
		                               py::make_getter(&RealHPConfig::extraStringDigits10, py::return_value_policy<py::return_by_value>()),
		                               py::make_setter(&RealHPConfig::extraStringDigits10, py::return_value_policy<py::return_by_value>())
		                               // python docstrings for static variables have to be written inside :cvar ………: in __doc__ of a class. See above.
		                       );
		py::def("getSupportedByEigenCgal",
		        getSupportedByEigenCgal,
		        R"""(:return: the ``tuple`` containing ``N`` from ``RealHP<N>`` precisions supported by Eigen and CGAL)""");
		py::def("getSupportedByMinieigen",
		        getSupportedByMinieigen,
		        R"""(:return: the ``tuple`` containing ``N`` from ``RealHP<N>`` precisions supported by minieigenHP)""");
		py::def("getDigits10", getDigits10, (py::arg("N")), R"""(
This is a yade.math.RealHPConfig diagnostic function.

:param N: ``int`` - the value of ``N`` in ``RealHP<N>``.
:return: the ``int`` representing ``std::numeric_limits<RealHP<N>>::digits10``)""");
		py::def("getDigits2", getDigits2, (py::arg("N")), R"""(
This is a yade.math.RealHPConfig diagnostic function.

:param N: ``int`` - the value of ``N`` in ``RealHP<N>``.
:return: the ``int`` representing ``std::numeric_limits<RealHP<N>>::digits``, which corresponds to the number of significand bits used by this type.)""");
#if (__GNUC__ < 9) // It should be checking  if (ver < 9.2.1) in fact. But cmake does the job. So here it's only to catch 'larger' mistakes.
#ifndef YADE_DISABLE_REAL_MULTI_HP
#warning "RealHP<…> won't work on this system, cmake sets YADE_DISABLE_REAL_MULTI_HP to use RealHP<1> for all precisions RealHP<N>. Also you can try -O0 flag."
// see file lib/high-precision/RealHP.hpp line: 'template <int Level> using RealHP    = Real;'
#endif
		// When using gcc older than 9.2.1 it is not possible for RealHP<N> to work. Without optimizations -O0 it can work, except for float128.
		// If YADE_DISABLE_REAL_MULTI_HP is set, then RealHP<1> is used in place of all possible precisions RealHP<N> : see file RealHP.hpp for this setting.
		// So this is for local testing only. With flag -O0 most of RealHP<…> works, except for float128 which is always segfaulting.
		py::scope().attr("isFloat128Broken") = true;
#else
		py::scope().attr("isFloat128Broken")  = false;
#endif
#ifndef YADE_DISABLE_REAL_MULTI_HP
		py::scope().attr("isEnabledRealHP") = true;
#else
		py::scope().attr("isEnabledRealHP")   = false;
#endif
		py::scope().attr("workaroundSlowBoostBinFloat") = int(workaroundSlowBoostBinFloat);
#ifdef BOOST_MP_FLOAT128_HPP
		py::scope().attr("isFloat128Present") = true;
#else
		py::scope().attr("isFloat128Present") = false;
#endif
	}

} // namespace math
} // namespace yade

/* A bit more about extraStringDigits10:

The extraStringDigits10 is to make sure that there are no conversion errors in the last bit.
here is a quick python example which shows the 'cutting' of last digits.

# This one demonstrates that `double` used by python has just 53 bits of precision:

for a in range(128): print(str(a).rjust(3,' ')+": "+str(1+1./pow(2.,a)))

# This one shows the 'true' values:

import mpmath; mpmath.mp.dps=200;
for a in range(128): print(str(a).rjust(3,' ')+": "+str(mpmath.mpf(1)+mpmath.mpf(1)/pow(mpmath.mpf(2),mpmath.mpf(a))))

# This one shows the actual 'Real' precision used in yade. To achieve this the mth.roundTrip(…) is called to force the numbers
# to pass through C++, instead of letting mpmath to calculate this, so for example we can see that float128 has 113 bits.
# Also this test was used to verify the value for extraStringDigits10 as well as the formula given
# in IEEE Std 754-2019 Standard for Floating-Point Arithmetic: Pmin (bf) = 1 + ceiling( p × log10(2)), where p is the number of significant bits in bf

from yade import math as mth
for a in range(128): print(str(a).rjust(3,' ')+": "+str(mth.roundTrip(mth.roundTrip(1)+mth.roundTrip(1)/mth.pow(mth.roundTrip(2),a))))

# But also we can now check the precision directly by calling

yade.math.RealHPConfig.getDigits2(N) # for any N of RealHP<N>

# FIXED: Hmm maybe turn this into an external parameter? Configurable from python? And write in help "use 1 to 'just' extract results and avoid fake sense of more precision,
# use 4 or more to have numbers which will convert exactly in both directions mpmath ↔ string ↔ C++.".
# For now it is in yade.math.RealHPConfig.extraStringDigits10
*/