decoint 0.1.0__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.
decoint/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ from .interval import Interval
2
+ from .decorated_interval import DecoratedInterval
3
+ from .decorations import Decoration
4
+
5
+ # 2. Fully Tracked Core Arithmetic (Using your decorated module)
6
+ from .decorated_arithmetic import add, sub, mul, div, reciprocal, fma
7
+
8
+ # 3. Tracked Elementary Functions
9
+ from .decorated_functions import (
10
+ exp, log, sqrt, pow_int, sign,
11
+ sin, cos, tan, asin, acos, atan,
12
+ sinh, cosh, tanh, asinh, acosh, atanh,
13
+ abs, atan2, interval_min, interval_max, nth_root, sqr, pow_interval, exp2, exp10, log2, log10, interval_trunc, interval_ceil, interval_floor
14
+ )
15
+
16
+ __all__ = [
17
+ 'Interval', 'DecoratedInterval', 'Decoration',
18
+ 'add', 'sub', 'mul', 'div', 'reciprocal',
19
+ 'exp', 'log', 'sqrt', 'pow_int', 'sign',
20
+ 'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
21
+ 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh',
22
+ 'abs', 'atan2', 'interval_min', 'interval_max', 'nth_root', 'sqr', 'fma', 'pow_interval', 'exp2', 'exp10', 'log2', 'log10', 'interval_trunc', 'interval_ceil', 'interval_floor'
23
+ ]
decoint/arithmetic.py ADDED
@@ -0,0 +1,181 @@
1
+ from .interval import Interval
2
+ from .rounding import add_down, add_up, sub_down, sub_up, div_down, div_up, mul_down, mul_up, fma_up, fma_down
3
+ from gmpy2 import mpfr, is_infinite, is_signed, sign, context, get_context
4
+
5
+ ctx = get_context()
6
+ ctx.precision = 53
7
+ ctx.emin = -1073
8
+ ctx.emax = 1024
9
+
10
+ def add(x, y) -> Interval:
11
+ x = Interval._coerce(x)
12
+ y = Interval._coerce(y)
13
+ if x.is_empty or y.is_empty:
14
+ return Interval.empty()
15
+ lo = add_down(x.lo, y.lo)
16
+ hi = add_up(x.hi, y.hi)
17
+ return Interval(lo, hi)
18
+
19
+ def sub(x, y) -> Interval:
20
+ x = Interval._coerce(x)
21
+ y = Interval._coerce(y)
22
+ if x.is_empty or y.is_empty:
23
+ return Interval.empty()
24
+ lo = sub_down(x.lo, y.hi)
25
+ hi = sub_up(x.hi, y.lo)
26
+ return Interval(lo, hi)
27
+
28
+ def mul(x, y) -> Interval:
29
+ x = Interval._coerce(x)
30
+ y = Interval._coerce(y)
31
+ if x.is_empty or y.is_empty:
32
+ return Interval.empty()
33
+
34
+ def safe_mul_down(a, b):
35
+ if (a == 0 and is_infinite(b)) or (b == 0 and is_infinite(a)):
36
+ is_neg = is_signed(a) * is_signed(b)
37
+ return mpfr('0') if is_neg else mpfr('-0.0')
38
+ return mul_down(a, b)
39
+
40
+ def safe_mul_up(a, b):
41
+ if (a == 0 and is_infinite(b)) or (b == 0 and is_infinite(a)):
42
+ is_neg = is_signed(a) * is_signed(b)
43
+ return mpfr('0') if is_neg else mpfr('-0.0')
44
+ return mul_up(a, b)
45
+
46
+ p1 = safe_mul_down(x.lo, y.lo)
47
+ p2 = safe_mul_down(x.lo, y.hi)
48
+ p3 = safe_mul_down(x.hi, y.lo)
49
+ p4 = safe_mul_down(x.hi, y.hi)
50
+
51
+ lo = min(p1, p2, p3, p4)
52
+
53
+ q1 = safe_mul_up(x.lo, y.lo)
54
+ q2 = safe_mul_up(x.lo, y.hi)
55
+ q3 = safe_mul_up(x.hi, y.lo)
56
+ q4 = safe_mul_up(x.hi, y.hi)
57
+
58
+ hi = max(q1, q2, q3, q4)
59
+
60
+ return Interval(lo, hi)
61
+
62
+ def reciprocal(x) -> Interval:
63
+ x = Interval._coerce(x)
64
+ if x.is_empty:
65
+ return Interval.empty()
66
+ if x.contains_zero:
67
+ if x.lo == 0 and x.hi == 0:
68
+ return Interval.empty()
69
+ if x.lo == 0:
70
+ return Interval(div_down(mpfr(1), x.hi), mpfr('inf'))
71
+ if x.hi == 0:
72
+ return Interval(mpfr('-inf'), div_up(mpfr(1), x.lo))
73
+ return Interval.entire()
74
+
75
+ return Interval(div_down(mpfr(1), x.hi), div_up(mpfr(1), x.lo))
76
+
77
+ def div(x, y) -> Interval:
78
+ x = Interval._coerce(x)
79
+ y = Interval._coerce(y)
80
+ if x.is_empty or y.is_empty:
81
+ return Interval.empty()
82
+
83
+ if x.is_entire and not y.contains(0):
84
+ return Interval.entire()
85
+
86
+ if y.contains(0):
87
+ if y.hi == 0 and y.lo == 0:
88
+ return Interval.empty()
89
+ if x.hi == 0 and x.lo == 0:
90
+ return Interval(mpfr('0'), mpfr('0'))
91
+ if x.lo >= 0:
92
+ if y.lo == 0:
93
+ return Interval(div_down(x.lo, y.hi), mpfr('inf'))
94
+ elif y.hi == 0:
95
+ return Interval(mpfr('-inf'), div_up(x.lo, y.lo))
96
+ else:
97
+ return Interval.entire()
98
+ elif x.hi <= 0:
99
+ if y.lo == 0:
100
+ return Interval(mpfr('-inf'), div_up(x.hi, y.hi))
101
+ elif y.hi == 0:
102
+ return Interval(div_down(x.hi, y.lo), mpfr('inf'))
103
+ else:
104
+ return Interval.entire()
105
+ else:
106
+ return Interval.entire()
107
+
108
+ # --- CLEAN DIRECT REPLACEMENT FOR mul(x, reciprocal(y)) ---
109
+ if y.lo > 0: # Denominator is strictly positive
110
+ # Lower bound logic
111
+ if x.lo == mpfr('-inf'):
112
+ lo = mpfr('-inf')
113
+ else:
114
+ lo = div_down(x.lo, y.hi if x.lo >= 0 else y.lo)
115
+
116
+ # Upper bound logic
117
+ if x.hi == mpfr('inf'):
118
+ hi = mpfr('inf')
119
+ else:
120
+ hi = div_up(x.hi, y.lo if x.hi >= 0 else y.hi)
121
+
122
+ else: # Denominator is strictly negative (y.hi < 0)
123
+ # Lower bound logic
124
+ if x.hi == mpfr('inf'):
125
+ lo = mpfr('-inf')
126
+ else:
127
+ lo = div_down(x.hi, y.hi if x.hi >= 0 else y.lo)
128
+
129
+ # Upper bound logic
130
+ if x.lo == mpfr('-inf'):
131
+ hi = mpfr('inf')
132
+ else:
133
+ hi = div_up(x.lo, y.lo if x.lo >= 0 else y.hi)
134
+
135
+ return Interval(lo, hi)
136
+
137
+ def evaluate_fma_corner(x, y, z, round_up=False):
138
+ # 1. Handle 0 * inf indeterminate form -> results in 0, so 0 + z = z
139
+ if (x == 0 and is_infinite(y)) or (is_infinite(x) and y == 0):
140
+ return z
141
+
142
+ # 2. Handle true inf - inf indeterminate form
143
+ if is_infinite(x) or is_infinite(y):
144
+ if is_infinite(z):
145
+ prod_neg = (x < 0) != (y < 0)
146
+ z_neg = z < 0
147
+ # If the infinite product's sign opposes z's infinite sign, it's inf - inf
148
+ if prod_neg != z_neg:
149
+ return None # Signal containment explosion
150
+
151
+ # Safe to compute standard hardware rounded FMA
152
+ return fma_up(x, y, z) if round_up else fma_down(x, y, z)
153
+
154
+
155
+ def fma(x, y, z):
156
+ x = Interval._coerce(x)
157
+ y = Interval._coerce(y)
158
+ z = Interval._coerce(z)
159
+
160
+ if x.is_empty or y.is_empty or z.is_empty:
161
+ return Interval.empty()
162
+
163
+ # Evaluate lower bound corner combinations
164
+ v_down = []
165
+ for cx in (x.lo, x.hi):
166
+ for cy in (y.lo, y.hi):
167
+ val = evaluate_fma_corner(cx, cy, z.lo, round_up=False)
168
+ if val is None:
169
+ return Interval.entire()
170
+ v_down.append(val)
171
+
172
+ # Evaluate upper bound corner combinations
173
+ v_up = []
174
+ for cx in (x.lo, x.hi):
175
+ for cy in (y.lo, y.hi):
176
+ val = evaluate_fma_corner(cx, cy, z.hi, round_up=True)
177
+ if val is None:
178
+ return Interval.entire()
179
+ v_up.append(val)
180
+
181
+ return Interval(min(v_down), max(v_up))
decoint/constants.py ADDED
@@ -0,0 +1,6 @@
1
+ from gmpy2 import const_pi, context, get_context
2
+
3
+ with context(get_context(), precision = 128):
4
+ PI = const_pi()
5
+ HALF_PI = PI / 2
6
+ TWO_PI = PI * 2
@@ -0,0 +1,98 @@
1
+ from .interval import Interval
2
+ from .arithmetic import add as bare_add, sub as bare_sub, mul as bare_mul, div as bare_div, reciprocal as bare_reciprocal, fma as bare_fma
3
+ from .decorations import Decoration, combine
4
+ from .decorated_interval import DecoratedInterval
5
+
6
+ def _finalize_decoration(base_dec: Decoration, interval: Interval) -> Decoration:
7
+ if base_dec == Decoration.COM and not interval.is_bounded:
8
+ return Decoration.DAC
9
+ return base_dec
10
+
11
+ def add(x, y):
12
+ x = DecoratedInterval._coerce(x)
13
+ y = DecoratedInterval._coerce(y)
14
+
15
+ if x.is_nai or y.is_nai:
16
+ return DecoratedInterval.new_nai()
17
+
18
+ interval = bare_add(x.interval, y.interval)
19
+ dec = combine(x.decoration, y.decoration)
20
+ dec = _finalize_decoration(dec, interval)
21
+
22
+ return DecoratedInterval(interval, dec)
23
+
24
+ def sub(x, y):
25
+ x = DecoratedInterval._coerce(x)
26
+ y = DecoratedInterval._coerce(y)
27
+
28
+ if x.is_nai or y.is_nai:
29
+ return DecoratedInterval.new_nai()
30
+
31
+ interval = bare_sub(x.interval, y.interval)
32
+ dec = combine(x.decoration, y.decoration)
33
+ dec = _finalize_decoration(dec, interval)
34
+
35
+ return DecoratedInterval(interval, dec)
36
+
37
+ def mul(x, y):
38
+ x = DecoratedInterval._coerce(x)
39
+ y = DecoratedInterval._coerce(y)
40
+
41
+ if x.is_nai or y.is_nai:
42
+ return DecoratedInterval.new_nai()
43
+
44
+ interval = bare_mul(x.interval, y.interval)
45
+ dec = combine(x.decoration, y.decoration)
46
+ dec = _finalize_decoration(dec, interval)
47
+
48
+ return DecoratedInterval(interval, dec)
49
+
50
+ def reciprocal(x):
51
+ x = DecoratedInterval._coerce(x)
52
+ if x.is_nai:
53
+ return DecoratedInterval.new_nai()
54
+ if x.interval.contains(0):
55
+ if x.interval.lo < 0 and x.interval.hi > 0:
56
+ return DecoratedInterval(Interval.entire(), Decoration.TRV)
57
+ interval = bare_reciprocal(x.interval)
58
+ return DecoratedInterval(interval, Decoration.TRV)
59
+ interval = bare_reciprocal(x.interval)
60
+ dec = x.decoration
61
+ dec = _finalize_decoration(dec, interval)
62
+ return DecoratedInterval(interval, dec)
63
+
64
+ def div(x, y):
65
+ x = DecoratedInterval._coerce(x)
66
+ y = DecoratedInterval._coerce(y)
67
+
68
+ if x.is_nai or y.is_nai:
69
+ return DecoratedInterval.new_nai()
70
+
71
+ if x.is_empty or y.is_empty:
72
+ return DecoratedInterval.empty()
73
+
74
+ if y.interval.contains(0):
75
+ interval = bare_div(x.interval, y.interval)
76
+ dec = combine(x.decoration, y.decoration, Decoration.TRV)
77
+ return DecoratedInterval(interval, Decoration.TRV)
78
+
79
+ interval = bare_div(x.interval, y.interval)
80
+ dec = combine(x.decoration, y.decoration)
81
+ dec = _finalize_decoration(dec, interval)
82
+
83
+ return DecoratedInterval(interval, dec)
84
+
85
+ def fma(x, y, z):
86
+ x = DecoratedInterval._coerce(x)
87
+ y = DecoratedInterval._coerce(y)
88
+ z = DecoratedInterval._coerce(z)
89
+
90
+ if x.is_nai or y.is_nai or z.is_nai:
91
+ return DecoratedInterval.new_nai()
92
+
93
+ interval = bare_fma(x.interval, y.interval, z.interval)
94
+ dec = combine(x.decoration, y.decoration, z.decoration)
95
+
96
+ dec = _finalize_decoration(dec, interval)
97
+
98
+ return DecoratedInterval(interval, dec)