codac 2.0.0
Loading...
Searching...
No Matches
codac2_mod.h
Go to the documentation of this file.
1
9
10#pragma once
11
12#include "codac2_Interval.h"
13#include "codac2_AnalyticType.h"
15#include "codac2_arith_sub.h"
16
17namespace codac2
18{
19 static inline double canonical_mod(double x, double p)
20 {
21 double r = std::fmod(x, p);
22 if(r < 0.0) r += p;
23 return r;
24 }
25
26 struct ModOp
27 {
28 template<typename X1,typename P>
29 static std::string str(const X1& x1, const P& p)
30 {
31 return "mod(" + x1->str() + "," + p->str() + ")";
32 }
33
34 template<typename X1,typename P>
35 static std::pair<Index,Index> output_shape([[maybe_unused]] const X1& s1, [[maybe_unused]] const P& p)
36 {
37 return {1,1};
38 }
39
40 static Interval fwd(const Interval& x1, const Interval& p);
41 static ScalarType fwd_natural(const ScalarType& x1, const ScalarType& p);
42 static ScalarType fwd_centered(const ScalarType& x1, const ScalarType& p);
43 static void bwd(const Interval& y, Interval& x1, Interval& p);
44
45 static void fwd_bwd(Interval& y, Interval& x1, double p);
46 static void fwd_bwd(Interval& y, Interval& x1, Interval& p);
47 };
48
49 // Analytic operator
50 // The following function can be used to build analytic expressions.
51
52 inline ScalarExpr
53 mod(const ScalarExpr& x1, const ScalarExpr& p)
54 {
55 return { std::make_shared<AnalyticOperationExpr<ModOp,ScalarType,ScalarType,ScalarType>>(x1,p) };
56 }
57
58 // Inline functions
59
60 inline Interval ModOp::fwd(const Interval& x1, const Interval& p)
61 {
62 if(x1.is_empty() || p.is_empty())
63 return Interval::empty();
64
65 Interval pp = p & Interval(next_float(0),oo);
66 if(pp.is_empty())
67 return Interval::empty();
68
69 // Exact case: singleton / singleton
70 if(x1.is_degenerated() && pp.is_degenerated())
71 return { canonical_mod(x1.lb(), pp.lb()) };
72
73 // Exact case: for every admissible p, x1 is already in the canonical band [0,p)
74 if(x1.lb() >= 0.0 && x1.ub() < pp.lb())
75 return x1;
76
77 // Exact case: scalar period and constant quotient over the whole x1
78 if(pp.lb() == pp.ub())
79 {
80 const double q = pp.lb();
81 const double k1 = std::floor(x1.lb() / q);
82 const double k2 = std::floor(x1.ub() / q);
83
84 if(k1 == k2)
85 {
86 // y = x1 - k*q
87 return x1 - (Interval(k1) * q);
88 }
89 }
90
91 // Safe but intentionally simple enclosure
92 double yub = pp.ub();
93 if(x1.lb() >= 0.0)
94 yub = std::min(yub, x1.ub());
95
96 return { 0.0, yub };
97 }
98
99 inline ScalarType ModOp::fwd_natural(const ScalarType& x1, const ScalarType& p)
100 {
101 return {
102 fwd(x1.a, p.a),
103 x1.def_domain && p.def_domain
104 };
105 }
106
107 inline ScalarType ModOp::fwd_centered(const ScalarType& x1, const ScalarType& p)
108 {
109 if(centered_form_not_available_for_args(x1,p))
110 return fwd_natural(x1,p);
111
112 return {
113 fwd(x1.m,p.m),
114 fwd(x1.a,p.a),
115 IntervalMatrix(0,0), // not supported yet for auto diff
116 x1.def_domain && p.def_domain
117 };
118 }
119
120 inline void ModOp::bwd(const Interval& y, Interval& x1, Interval& p)
121 {
122 auto set_empty = [&]()
123 {
124 x1.set_empty();
125 p.set_empty();
126 };
127
128 if(y.is_empty() || x1.is_empty() || p.is_empty())
129 {
130 set_empty();
131 return;
132 }
133
134 // Modulo is defined only for p > 0
135 p &= Interval(next_float(0),oo);
136 if(p.is_empty())
137 {
138 set_empty();
139 return;
140 }
141
142 // Since y is not contracted here (const), we only keep the part
143 // potentially compatible with a canonical remainder: 0 <= y <= p.ub()
144 Interval y0 = y & Interval(0.0, p.ub());
145 if(y0.is_empty())
146 {
147 set_empty();
148 return;
149 }
150
151 // Candidate integer quotients:
152 // x1 = y + k p <=> k = (x1 - y)/p
153 Interval r = (x1-y0)/p;
154
155 const double kmin_d = std::ceil(r.lb());
156 const double kmax_d = std::floor(r.ub());
157
158 if(!(kmin_d <= kmax_d))
159 {
160 set_empty();
161 return;
162 }
163
164 Interval K(kmin_d, kmax_d);
165
166 // Coarse hull contraction
167 x1 &= y0+K*p;
168 if(x1.is_empty())
169 {
170 set_empty();
171 return;
172 }
173
174 // If 0 is not in K, we can also contract p approximately
175 if(!K.contains(0.))
176 {
177 p &= (x1-y0)/K;
178 p &= Interval(next_float(0),oo);
179
180 if(p.is_empty())
181 {
182 set_empty();
183 return;
184 }
185
186 y0 &= Interval(0.0, p.ub());
187 if(y0.is_empty())
188 {
189 set_empty();
190 return;
191 }
192
193 x1 &= y0+K*p;
194 if(x1.is_empty())
195 {
196 set_empty();
197 return;
198 }
199 }
200 }
201
202 inline void ModOp::fwd_bwd(Interval& y, Interval& x1, double p)
203 {
204 Interval ip(p);
205 ModOp::fwd_bwd(y,x1,ip);
206 }
207
208 inline void ModOp::fwd_bwd(Interval& y, Interval& x1, Interval& p)
209 {
210 y &= ModOp::fwd(x1,p);
211 ModOp::bwd(y,x1,p);
212 }
213}
Interval class, for representing closed and connected subsets of .
Definition codac2_Interval.h:49
static Interval empty()
Provides an empty interval.
Definition codac2_Interval_impl.h:568
void set_empty()
Marks the interval matrix as empty.
Definition codac2_Matrix_addons_IntervalMatrixBase.h:141
Definition codac2_OctaSym.h:21
double next_float(double x)
Returns the next representable double-precision floating-point value after x.
Definition codac2_Interval_impl.h:619
Eigen::Matrix< Interval,-1,-1 > IntervalMatrix
Alias for a dynamic-size matrix of intervals.
Definition codac2_IntervalMatrix.h:25