aGrUM
0.20.3
a C++ library for (probabilistic) graphical models
rational_tpl.h
Go to the documentation of this file.
1
/**
2
*
3
* Copyright (c) 2005-2021 by Pierre-Henri WUILLEMIN(@LIP6) & Christophe GONZALES(@AMU)
4
* info_at_agrum_dot_org
5
*
6
* This library is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this library. If not, see <http://www.gnu.org/licenses/>.
18
*
19
*/
20
21
22
/**
23
* @file
24
* @brief Class template used to approximate decimal numbers by rationals.
25
* @author Matthieu HOURBRACQ and Pierre-Henri WUILLEMIN(@LIP6)
26
*/
27
28
// To help IDE Parsers
29
#
include
<
agrum
/
agrum
.
h
>
30
#
include
<
agrum
/
tools
/
core
/
math
/
rational
.
h
>
31
32
namespace
gum
{
33
34
template
<
typename
GUM_SCALAR >
35
void
Rational<
GUM_SCALAR
>::
farey
(
int64_t
&
numerator
,
36
int64_t
&
denominator
,
37
const
GUM_SCALAR
&
number
,
38
const
int64_t
&
den_max
,
39
const
GUM_SCALAR
&
zero
) {
40
bool
isNegative
= (
number
< 0) ?
true
:
false
;
41
GUM_SCALAR
pnumber
= (
isNegative
) ? -
number
:
number
;
42
43
if
(
std
::
abs
(
pnumber
-
GUM_SCALAR
(1.)) <
zero
) {
44
numerator
= (
isNegative
) ? -1 : 1;
45
denominator
= 1;
46
return
;
47
}
else
if
(
pnumber
<
zero
) {
48
numerator
= 0;
49
denominator
= 1;
50
return
;
51
}
52
53
int64_t
a
(0),
b
(1),
c
(1),
d
(1);
54
double
mediant
(0.0F);
55
56
while
(
b
<=
den_max
&&
d
<=
den_max
) {
57
mediant
= (
GUM_SCALAR
)(
a
+
c
) / (
GUM_SCALAR
)(
b
+
d
);
58
59
if
(
std
::
fabs
(
pnumber
-
mediant
) <
zero
) {
60
if
(
b
+
d
<=
den_max
) {
61
numerator
= (
isNegative
) ? -(
a
+
c
) : (
a
+
c
);
62
denominator
=
b
+
d
;
63
return
;
64
}
else
if
(
d
>
b
) {
65
numerator
= (
isNegative
) ? -
c
:
c
;
66
denominator
=
d
;
67
return
;
68
}
else
{
69
numerator
= (
isNegative
) ? -
a
:
a
;
70
denominator
=
b
;
71
return
;
72
}
73
}
else
if
(
pnumber
>
mediant
) {
74
a
=
a
+
c
;
75
b
=
b
+
d
;
76
}
else
{
77
c
=
a
+
c
;
78
d
=
b
+
d
;
79
}
80
}
81
82
if
(
b
>
den_max
) {
83
numerator
= (
isNegative
) ? -
c
:
c
;
84
denominator
=
d
;
85
return
;
86
}
else
{
87
numerator
= (
isNegative
) ? -
a
:
a
;
88
denominator
=
b
;
89
return
;
90
}
91
}
/// end of farey func
92
93
template
<
typename
GUM_SCALAR
>
94
void
Rational
<
GUM_SCALAR
>::
continuedFracFirst
(
int64_t
&
numerator
,
95
int64_t
&
denominator
,
96
const
GUM_SCALAR
&
number
,
97
const
double
&
zero
) {
98
const
GUM_SCALAR
pnumber
= (
number
> 0) ?
number
: -
number
;
99
100
/// reciprocal over iterations
101
GUM_SCALAR
rnumber
=
pnumber
;
102
103
/// convergents
104
std
::
vector
<
uint64_t
>
p
({0, 1});
105
std
::
vector
<
uint64_t
>
q
({1, 0});
106
107
/// quotients
108
std
::
vector
<
uint64_t
>
a
;
109
110
uint64_t
p_tmp
,
q_tmp
;
111
112
uint64_t
n
;
113
double
delta
,
delta_tmp
;
114
115
/// we find all convergents until we found a best one
116
/// since we look for a delta < zero, we can start looking for
117
/// semi-convergents
118
/// when we found a convergent with delta < zero, and look for the
119
/// semi-convergents before
120
while
(
true
) {
121
a
.
push_back
(
std
::
lrint
(
std
::
floor
(
rnumber
)));
122
p
.
push_back
(
a
.
back
() *
p
.
back
() +
p
[
p
.
size
() - 2]);
123
q
.
push_back
(
a
.
back
() *
q
.
back
() +
q
[
q
.
size
() - 2]);
124
125
delta
=
std
::
fabs
(
pnumber
- (
GUM_SCALAR
)
p
.
back
() /
q
.
back
());
126
127
if
(
delta
<
zero
) {
128
numerator
= (
int64_t
)
p
.
back
();
129
if
(
number
< 0)
numerator
= -
numerator
;
130
denominator
=
q
.
back
();
131
break
;
132
}
133
134
if
(
std
::
abs
(
rnumber
-
a
.
back
()) < 1e-6)
break
;
135
136
rnumber
=
GUM_SCALAR
(1.) / (
rnumber
-
a
.
back
());
137
}
/// end of while
138
139
if
(
a
.
size
() < 2)
return
;
140
141
/// we can start looking at the semi-convergents made of the last two
142
/// convergents
143
/// before the one within precision zero of number found previously
144
Idx
i
=
Idx
(
p
.
size
() - 2);
145
/// the last convergent has already been computed previously : end of for is
146
/// p.size() - 2
147
/// for ( ; i < p.size() - 1; ++i ) {
148
// Test n = a[i-1]/2 ( when a[i-1] is even )
149
n
=
a
[
i
- 1] / 2;
150
p_tmp
=
n
*
p
[
i
] +
p
[
i
- 1];
151
q_tmp
=
n
*
q
[
i
] +
q
[
i
- 1];
152
153
delta
=
std
::
fabs
(
pnumber
- ((
double
)
p
[
i
]) /
q
[
i
]);
154
delta_tmp
=
std
::
fabs
(
pnumber
- ((
double
)
p_tmp
) /
q_tmp
);
155
156
if
(
delta
<
zero
) {
157
numerator
= (
int64_t
)
p
[
i
];
158
if
(
number
< 0)
numerator
= -
numerator
;
159
denominator
=
q
[
i
];
160
return
;
161
}
162
163
if
(
delta_tmp
<
zero
) {
164
numerator
= (
int64_t
)
p_tmp
;
165
if
(
number
< 0)
numerator
= -
numerator
;
166
denominator
=
q_tmp
;
167
return
;
168
}
169
170
// next semi-convergents until next convergent from smaller denominator to
171
// bigger
172
// denominator
173
for
(
n
= (
a
[
i
- 1] + 2) / 2;
n
<
a
[
i
- 1]; ++
n
) {
174
p_tmp
=
n
*
p
[
i
] +
p
[
i
- 1];
175
q_tmp
=
n
*
q
[
i
] +
q
[
i
- 1];
176
177
delta_tmp
=
std
::
fabs
(
pnumber
- ((
double
)
p_tmp
) /
q_tmp
);
178
179
if
(
delta_tmp
<
zero
) {
180
numerator
= (
int64_t
)
p_tmp
;
181
if
(
number
< 0)
numerator
= -
numerator
;
182
denominator
=
q_tmp
;
183
return
;
184
}
185
}
/// end of for
186
187
///} // end of for
188
}
189
190
template
<
typename
GUM_SCALAR
>
191
void
Rational
<
GUM_SCALAR
>::
continuedFracBest
(
int64_t
&
numerator
,
192
int64_t
&
denominator
,
193
const
GUM_SCALAR
&
number
,
194
const
int64_t
&
den_max
) {
195
const
GUM_SCALAR
pnumber
= (
number
> 0) ?
number
: -
number
;
196
197
const
uint64_t
denMax
= (
uint64_t
)
den_max
;
/// signed and unsigned comparison resolution ...
198
199
/// reciprocal over iterations
200
GUM_SCALAR
rnumber
=
pnumber
;
201
202
/// convergents
203
std
::
vector
<
uint64_t
>
p
({0, 1});
204
std
::
vector
<
uint64_t
>
q
({1, 0});
205
206
/// quotients
207
std
::
vector
<
uint64_t
>
a
;
208
209
uint64_t
p_tmp
,
q_tmp
;
210
211
uint64_t
n
;
212
double
delta
,
delta_tmp
;
213
214
/// we find all convergents until we met den_max
215
while
(
true
) {
216
a
.
push_back
(
std
::
lrint
(
std
::
floor
(
rnumber
)));
217
218
p_tmp
=
a
.
back
() *
p
.
back
() +
p
[
p
.
size
() - 2];
219
q_tmp
=
a
.
back
() *
q
.
back
() +
q
[
q
.
size
() - 2];
220
221
if
(
q_tmp
>
denMax
||
p_tmp
>
denMax
)
break
;
222
223
p
.
push_back
(
p_tmp
);
224
q
.
push_back
(
q_tmp
);
225
226
if
(
std
::
fabs
(
rnumber
-
a
.
back
()) < 1e-6)
break
;
227
228
rnumber
=
GUM_SCALAR
(1.) / (
rnumber
-
a
.
back
());
229
}
/// end of while
230
231
if
(
a
.
size
() < 2 ||
q
.
back
() ==
denMax
||
p
.
back
() ==
denMax
) {
232
numerator
=
p
.
back
();
233
if
(
number
< 0)
numerator
= -
numerator
;
234
denominator
=
q
.
back
();
235
return
;
236
}
237
238
/// we can start looking at the semi-convergents made of the last two
239
/// convergents
240
/// before the one within precision zero of number found previously
241
Idx
i
=
Idx
(
p
.
size
() - 1);
242
243
/// the last convergent has already been computed previously : end of for is
244
/// p.size() - 2
245
/// for ( ; i < p.size() - 1; ++i ) {
246
for
(
n
=
a
[
i
- 1] - 1;
n
>= (
a
[
i
- 1] + 2) / 2; --
n
) {
247
p_tmp
=
n
*
p
[
i
] +
p
[
i
- 1];
248
q_tmp
=
n
*
q
[
i
] +
q
[
i
- 1];
249
250
if
(
q_tmp
>
denMax
||
p_tmp
>
denMax
)
continue
;
251
252
numerator
= (
int64_t
)
p_tmp
;
253
if
(
number
< 0)
numerator
= -
numerator
;
254
denominator
=
q_tmp
;
255
return
;
256
}
// end of for
257
258
// Test n = a[i-1]/2
259
n
=
a
[
i
- 1] / 2;
260
p_tmp
=
n
*
p
[
i
] +
p
[
i
- 1];
261
q_tmp
=
n
*
q
[
i
] +
q
[
i
- 1];
262
263
delta_tmp
=
std
::
fabs
(
pnumber
- ((
double
)
p_tmp
) /
q_tmp
);
264
delta
=
std
::
fabs
(
pnumber
- ((
double
)
p
[
i
]) /
q
[
i
]);
265
266
if
(
delta_tmp
<
delta
&&
q_tmp
<=
denMax
&&
p_tmp
<=
denMax
) {
267
numerator
= (
int64_t
)
p_tmp
;
268
if
(
number
< 0)
numerator
= -
numerator
;
269
denominator
=
q_tmp
;
270
}
else
{
271
numerator
= (
int64_t
)
p
[
i
];
272
if
(
number
< 0)
numerator
= -
numerator
;
273
274
denominator
=
q
[
i
];
275
}
276
277
///}
278
}
279
280
}
// namespace gum
gum::Set::emplace
INLINE void emplace(Args &&... args)
Definition:
set_tpl.h:643