vusvna 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vusvna/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """
2
+ vusvna, Vulkan Self-Validated Numerical Analysis: Slang module for rigorous affine arithmetic in Vulkan
3
+ shaders. The Python package only ships the Slang sources, add get_include() to the include paths of the
4
+ Slang compiler (slangpy: SlangCompilerOptions({"include_paths": [vusvna.get_include()]})) and write
5
+ `import vusvna;`.
6
+ """
7
+
8
+ import os
9
+
10
+ __version__ = "1.0.1"
11
+
12
+ _package_dir = os.path.dirname(os.path.abspath(__file__))
13
+
14
+
15
+ def get_include():
16
+ """Directory containing vusvna.slang, to add to the include paths of the Slang compiler."""
17
+ slang_dir = os.path.join(_package_dir, "slang")
18
+ if os.path.isfile(os.path.join(slang_dir, "vusvna.slang")):
19
+ return slang_dir
20
+ # source checkout or editable install: the sources are in the vusvna/ directory of the repository
21
+ return os.path.join(os.path.dirname(os.path.dirname(_package_dir)), "vusvna")
vusvna/__main__.py ADDED
@@ -0,0 +1,12 @@
1
+ import argparse
2
+
3
+ import vusvna
4
+
5
+ parser = argparse.ArgumentParser(prog="python -m vusvna", description=vusvna.__doc__.strip().split("\n")[0])
6
+ parser.add_argument("--include", action="store_true", help="print the include directory of the Slang module")
7
+ parser.add_argument("--version", action="version", version=vusvna.__version__)
8
+ args = parser.parse_args()
9
+ if args.include:
10
+ print(vusvna.get_include())
11
+ else:
12
+ parser.print_help()
vusvna/slang/aa.slang ADDED
@@ -0,0 +1,304 @@
1
+ /* Affine arithmetic with N noise symbols.
2
+ *
3
+ * An affine form x = b + sum_i x_i e_i + e, where the e_i are shared noise symbols in [-1, 1] and
4
+ * |e| <= err, represents all the values taken by an expression when its inputs vary in their intervals.
5
+ * Each input variable gets its own symbol (variable), the symbols track the linear correlations between
6
+ * intermediate results. Rounding errors and the errors of nonlinear approximations are accumulated in
7
+ * the independent term err (reduced affine arithmetic, AF1): the number of symbols stays N, at the cost
8
+ * of losing the correlations of these errors.
9
+ *
10
+ * When VUSVNA_IGNORE_ROUNDING is defined, the rounding errors are not accumulated in err (see rounding.slang).
11
+ *
12
+ * Forms whose magnitude |b| + sum |x_i| + err exceeds LIMIT = 2^60 are entire: they represent any real
13
+ * number, and every operation on an entire form returns an entire form.
14
+ */
15
+ implementing vusvna;
16
+
17
+ /* coefficients below the smallest normal number are set to zero, the driver may flush them anyway */
18
+ float flush_denormal(float v) { return (!IGNORE_ROUNDING && f_abs(v) < MIN_NORMAL ? 0.0 : v); }
19
+
20
+ public struct AA<let N : int> : IReal {
21
+ /* central value */
22
+ public float b;
23
+ /* coefficients of the noise symbols */
24
+ public float x[N];
25
+ /* bound of the independent error term */
26
+ public float err;
27
+
28
+ public __init(float c) {
29
+ b = 0.0;
30
+ [ForceUnroll] for (int i = 0; i < N; ++i) { x[i] = 0.0; }
31
+ err = 0.0;
32
+ if (!is_bounded(c)) {
33
+ err = HUGE;
34
+ }
35
+ else if (f_abs(c) < MIN_NORMAL) {
36
+ err = (c != 0.0 ? MIN_NORMAL : 0.0);
37
+ }
38
+ else {
39
+ b = c;
40
+ }
41
+ }
42
+
43
+ public static AA<N> entire() { return AA<N>(HUGE); }
44
+
45
+ public static AA<N> from_float(float c) { return AA<N>(c); }
46
+
47
+ /* the input variable of symbol k (0 <= k < N) that takes all the values of I */
48
+ public static AA<N> variable(int k, Interval I) {
49
+ if (!I.is_bounded() || I.is_empty()) { return entire(); }
50
+ AA<N> r = AA<N>(0.0);
51
+ r.b = flush_denormal(I.mid());
52
+ const float rad = (I.a == I.b ? 0.0 : f_max(sub_up(r.b, I.a), sub_up(I.b, r.b)));
53
+ [ForceUnroll] for (int i = 0; i < N; ++i) { r.x[i] = (i == k ? rad : 0.0); }
54
+ return r;
55
+ }
56
+
57
+ /* a quantity that takes any value in I, uncorrelated with the other quantities */
58
+ public static AA<N> from_interval(Interval I) {
59
+ if (!I.is_bounded() || I.is_empty()) { return entire(); }
60
+ AA<N> r = AA<N>(0.0);
61
+ r.b = flush_denormal(I.mid());
62
+ r.err = (I.a == I.b ? 0.0 : f_max(sub_up(r.b, I.a), sub_up(I.b, r.b)));
63
+ return r;
64
+ }
65
+
66
+ public bool is_entire() { return !(err <= LIMIT); }
67
+
68
+ /* sum of the magnitudes of the coefficients */
69
+ public float radius() {
70
+ float r = 0.0;
71
+ [ForceUnroll] for (int i = 0; i < N; ++i) { r += f_abs(x[i]); }
72
+ return r;
73
+ }
74
+
75
+ /* range of the affine part b + sum_i x_i e_i */
76
+ public Interval affine_range() {
77
+ if (is_entire()) { return Interval::entire(); }
78
+ const float r = inflate(radius(), N);
79
+ return Interval(sub_down(b, r), add_up(b, r));
80
+ }
81
+
82
+ /* enclosure of all the values of the form */
83
+ public Interval interval() {
84
+ if (is_entire()) { return Interval::entire(); }
85
+ const float r = inflate(radius() + err, N);
86
+ return Interval(sub_down(b, r), add_up(b, r));
87
+ }
88
+
89
+ public Sign sign() { return interval().sign(); }
90
+
91
+ /* magnitude of the dependency on symbol k */
92
+ public float magnitude(int k) { return f_abs(x[k]); }
93
+
94
+ /* Last step of every operation: raw bounds the independent error term of the exact operation and
95
+ the rounding errors made by the computation of r, up to the relative and absolute errors covered
96
+ by inflate. Denormal coefficients are flushed, which is also covered by inflate. */
97
+ static AA<N> finalize(AA<N> r, float raw) {
98
+ r.b = flush_denormal(r.b);
99
+ float s = f_abs(r.b);
100
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
101
+ r.x[i] = flush_denormal(r.x[i]);
102
+ s += f_abs(r.x[i]);
103
+ }
104
+ r.err = inflate(raw, N);
105
+ if (!(s + r.err <= LIMIT)) { return entire(); }
106
+ return r;
107
+ }
108
+
109
+ /* image of the form by an affine approximation of a function */
110
+ public AA<N> apply(Approx A) {
111
+ if (A.entire || is_entire()) { return entire(); }
112
+ AA<N> r;
113
+ const float ab = A.alpha * b;
114
+ r.b = ab + A.zeta;
115
+ float s = f_abs(ab) + f_abs(r.b);
116
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
117
+ r.x[i] = A.alpha * x[i];
118
+ s += f_abs(r.x[i]);
119
+ }
120
+ return finalize(r, A.delta + A.err + ROUNDING_U * s);
121
+ }
122
+
123
+ /*** IReal ***/
124
+
125
+ public AA<N> op_neg() {
126
+ AA<N> r = this;
127
+ r.b = -b;
128
+ [ForceUnroll] for (int i = 0; i < N; ++i) { r.x[i] = -x[i]; }
129
+ return r;
130
+ }
131
+
132
+ public AA<N> op_add(AA<N> o) {
133
+ if (is_entire() || o.is_entire()) { return entire(); }
134
+ AA<N> r;
135
+ r.b = b + o.b;
136
+ float s = f_abs(r.b);
137
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
138
+ r.x[i] = x[i] + o.x[i];
139
+ s += f_abs(r.x[i]);
140
+ }
141
+ return finalize(r, err + o.err + ROUNDING_U * s);
142
+ }
143
+
144
+ public AA<N> op_sub(AA<N> o) { return op_add(o.op_neg()); }
145
+
146
+ public AA<N> op_addf(float c) {
147
+ if (is_entire() || !is_bounded(c)) { return entire(); }
148
+ AA<N> r = this;
149
+ r.b = b + c;
150
+ return finalize(r, err + ROUNDING_U * f_abs(r.b));
151
+ }
152
+
153
+ /* c - this */
154
+ public AA<N> op_rsubf(float c) { return op_neg().op_addf(c); }
155
+
156
+ public AA<N> op_mulf(float c) {
157
+ if (is_entire() || !is_bounded(c)) { return entire(); }
158
+ if (f_abs(c) < MIN_NORMAL) {
159
+ /* |c x| <= 2^-126 2^60 */
160
+ AA<N> r = AA<N>(0.0);
161
+ r.err = 1.3552527156068805e-20;
162
+ return r;
163
+ }
164
+ AA<N> r;
165
+ r.b = c * b;
166
+ float s = f_abs(r.b);
167
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
168
+ r.x[i] = c * x[i];
169
+ s += f_abs(r.x[i]);
170
+ }
171
+ return finalize(r, f_abs(c) * err + ROUNDING_U * s);
172
+ }
173
+
174
+ public AA<N> op_divf(float c) {
175
+ if (is_entire() || !(f_abs(c) >= 8.673617379884035e-19 && f_abs(c) <= LIMIT)) { return entire(); }
176
+ AA<N> r;
177
+ r.b = b / c;
178
+ float s = f_abs(r.b);
179
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
180
+ r.x[i] = x[i] / c;
181
+ s += f_abs(r.x[i]);
182
+ }
183
+ /* divisions are within 2.5 ulps, 5 u relative */
184
+ return finalize(r, err / f_abs(c) + 8.0 * ROUNDING_U * s);
185
+ }
186
+
187
+ public AA<N> op_mul(AA<N> o) {
188
+ if (is_entire() || o.is_entire()) { return entire(); }
189
+ const float rx = radius();
190
+ const float ry = o.radius();
191
+ /* upper bounds of the magnitudes of the nonconstant parts */
192
+ const float Rx = inflate(rx + err, N);
193
+ const float Ry = inflate(ry + o.err, N);
194
+ AA<N> r;
195
+ r.b = b * o.b;
196
+ float s = f_abs(r.b) + f_abs(b) * ry + f_abs(o.b) * rx;
197
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
198
+ r.x[i] = b * o.x[i] + o.b * x[i];
199
+ s += f_abs(r.x[i]);
200
+ }
201
+ return finalize(r, f_abs(b) * o.err + f_abs(o.b) * err + Rx * Ry + ROUNDING_U * s);
202
+ }
203
+
204
+ public AA<N> fn_sqr() {
205
+ if (is_entire()) { return entire(); }
206
+ /* (b + l)^2 = b^2 + 2 b l + l^2 where 0 <= l^2 <= R^2 */
207
+ const float R = inflate(radius() + err, N);
208
+ const float h = 0.5 * mul_up(R, R);
209
+ const float bb = b * b;
210
+ AA<N> r;
211
+ r.b = bb + h;
212
+ float s = bb + f_abs(r.b);
213
+ [ForceUnroll] for (int i = 0; i < N; ++i) {
214
+ r.x[i] = (2.0 * b) * x[i];
215
+ s += f_abs(r.x[i]);
216
+ }
217
+ return finalize(r, 2.0 * f_abs(b) * err + h + ROUNDING_U * s);
218
+ }
219
+
220
+ public AA<N> fn_reciprocal() { return apply(approx_reciprocal(affine_range(), err)); }
221
+ public AA<N> op_div(AA<N> o) { return op_mul(o.fn_reciprocal()); }
222
+ /* c / this */
223
+ public AA<N> op_rdivf(float c) { return fn_reciprocal().op_mulf(c); }
224
+
225
+ public AA<N> fn_abs() { return apply(approx_abs(affine_range(), err)); }
226
+ public AA<N> fn_positive_part() { return apply(approx_positive_part(affine_range(), err)); }
227
+ public AA<N> fn_sqrt() { return apply(approx_sqrt(affine_range(), err)); }
228
+ public AA<N> fn_rsqrt() { return apply(approx_rsqrt(affine_range(), err)); }
229
+ public AA<N> fn_cbrt() { return apply(approx_cbrt(affine_range(), err)); }
230
+ public AA<N> fn_exp() { return apply(approx_exp(affine_range(), err)); }
231
+ public AA<N> fn_exp2() { return apply(approx_exp2(affine_range(), err)); }
232
+ public AA<N> fn_log() { return apply(approx_log(affine_range(), err)); }
233
+ public AA<N> fn_log2() { return apply(approx_log2(affine_range(), err)); }
234
+ public AA<N> fn_sin() { return apply(approx_sin(affine_range(), err)); }
235
+ public AA<N> fn_cos() { return apply(approx_cos(affine_range(), err)); }
236
+ public AA<N> fn_tan() { return apply(approx_tan(affine_range(), err)); }
237
+ public AA<N> fn_atan() { return apply(approx_atan(affine_range(), err)); }
238
+
239
+ public AA<N> fn_pown(int n) {
240
+ if (n == 2) { return fn_sqr(); }
241
+ if (n == -1) { return fn_reciprocal(); }
242
+ return apply(approx_pown(affine_range(), err, n));
243
+ }
244
+
245
+ public AA<N> fn_pow(float y) {
246
+ if (y == 0.5) { return fn_sqrt(); }
247
+ return apply(approx_pow(affine_range(), err, y));
248
+ }
249
+
250
+ public AA<N> fn_cot() { return apply(approx_cot(affine_range(), err)); }
251
+ public AA<N> fn_sinpi() { return apply(approx_sinpi(affine_range(), err)); }
252
+ public AA<N> fn_cospi() { return apply(approx_cospi(affine_range(), err)); }
253
+ public AA<N> fn_exp10() { return apply(approx_exp10(affine_range(), err)); }
254
+ public AA<N> fn_log10() { return apply(approx_log10(affine_range(), err)); }
255
+ public AA<N> fn_rcbrt() { return apply(approx_rcbrt(affine_range(), err)); }
256
+
257
+ /* atan2(this, x): atan(y/x) for x > 0, +-pi/2 - atan(x/y) for y > 0 or y < 0, [-pi, pi] otherwise */
258
+ public AA<N> fn_atan2(AA<N> x) {
259
+ if (is_entire() || x.is_entire()) { return entire(); }
260
+ if (x.interval().a > 0.0) { return op_div(x).fn_atan(); }
261
+ const Interval Y = interval();
262
+ const AA<N> half_pi = from_interval(Interval(0.5 * PI_LO, 0.5 * PI_HI));
263
+ if (Y.a > 0.0) { return half_pi.op_sub(x.op_div(this).fn_atan()); }
264
+ if (Y.b < 0.0) { return half_pi.op_neg().op_sub(x.op_div(this).fn_atan()); }
265
+ return from_interval(Interval(-PI_HI, PI_HI));
266
+ }
267
+
268
+ /* Discontinuous functions are evaluated on the whole range of their input. They are exact (and
269
+ correlated with their input) when the range does not cross a discontinuity, else the result is the
270
+ range of the function, uncorrelated with the input. */
271
+ public AA<N> fn_floor() { return apply(approx_step(interval(), TO_FLOOR)); }
272
+ public AA<N> fn_ceil() { return apply(approx_step(interval(), TO_CEIL)); }
273
+ public AA<N> fn_trunc() { return apply(approx_step(interval(), TO_TRUNC)); }
274
+ public AA<N> fn_round() { return apply(approx_step(interval(), TO_ROUND)); }
275
+ public AA<N> fn_rint() { return apply(approx_step(interval(), TO_RINT)); }
276
+ public AA<N> fn_fract() { return apply(approx_fract(interval(), err)); }
277
+ public AA<N> fn_modf() { return apply(approx_modf(interval(), err)); }
278
+
279
+ /* x - n*c with n = trunc(x/c) (fmod) or n = rint(x/c) (remainder), the range of n is returned */
280
+ AA<N> reduce(float c, int mode, out int quo_lo, out int quo_hi) {
281
+ const Interval X = interval();
282
+ const Interval Q = quotient_range(mode, X, point(c));
283
+ quo_lo = integer_lo(Q.a);
284
+ quo_hi = integer_hi(Q.b);
285
+ return apply(approx_reduction(X, err, c, mode, Q));
286
+ }
287
+
288
+ /* the same with an affine divisor, entire if y may be zero */
289
+ AA<N> reduce_by(AA<N> y, int mode, out int quo_lo, out int quo_hi) {
290
+ const Interval X = interval();
291
+ const Interval Y = y.interval();
292
+ const Interval Q = quotient_range(mode, X, Y);
293
+ quo_lo = integer_lo(Q.a);
294
+ quo_hi = integer_hi(Q.b);
295
+ if (!Q.is_bounded()) { return entire(); }
296
+ if (Q.a == Q.b) { return op_sub(y.op_mulf(Q.a)); }
297
+ return from_interval(remainder_range(mode, X, Y.mag()));
298
+ }
299
+
300
+ public AA<N> fn_fmod(float c) { int lo, hi; return reduce(c, TO_TRUNC, lo, hi); }
301
+ public AA<N> fn_remquo(float c, out int quo_lo, out int quo_hi) { return reduce(c, TO_RINT, quo_lo, quo_hi); }
302
+ public AA<N> fn_fmod_by(AA<N> y) { int lo, hi; return reduce_by(y, TO_TRUNC, lo, hi); }
303
+ public AA<N> fn_remquo_by(AA<N> y, out int quo_lo, out int quo_hi) { return reduce_by(y, TO_RINT, quo_lo, quo_hi); }
304
+ };