Skip to content

Commit 9cb1e81

Browse files
Merge pull request #251 from Tom-Hubrecht/precision-manager
Add PrecisionManager to flint context
2 parents debe91d + d5f1a18 commit 9cb1e81

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

doc/source/general.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,28 @@ The special method ``ctx.cleanup()`` frees up internal caches
5555
used by MPFR, FLINT and Arb. The user does normally not have to
5656
worry about this.
5757

58+
The context object ``flint.ctx`` can be controlled locally to increase the
59+
working precision using python context managers::
60+
61+
>>> arb(2).sqrt()
62+
[1.41421356237309 +/- 5.15e-15]
63+
>>> with ctx.extraprec(15):
64+
... arb(2).sqrt()
65+
...
66+
[1.414213562373095049 +/- 2.10e-19]
67+
68+
In the same manner, it is possible to exactly set the working precision,
69+
or to update it in terms of digits::
70+
71+
>>> with ctx.extradps(15):
72+
... arb(2).sqrt()
73+
...
74+
[1.41421356237309504880168872421 +/- 6.27e-31]
75+
>>> with ctx.workprec(15):
76+
... arb(2).sqrt()
77+
...
78+
[1.414 +/- 2.46e-4]
79+
5880
Types and methods
5981
-----------------
6082

src/flint/flint_base/flint_context.pyx

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ from flint.flintlib.types.flint cimport (
66
)
77
from flint.utils.conversion cimport prec_to_dps, dps_to_prec
88

9+
from functools import wraps
10+
911
cdef class FlintContext:
1012
def __init__(self):
1113
self.default()
@@ -57,6 +59,88 @@ cdef class FlintContext:
5759
assert num >= 1 and num <= 64
5860
flint_set_num_threads(num)
5961

62+
def extraprec(self, n):
63+
"""
64+
Adds n bits of precision to the current flint context.
65+
66+
>>> from flint import arb, ctx
67+
>>> with ctx.extraprec(5): x = arb(2).sqrt().str()
68+
>>> x
69+
'[1.414213562373095 +/- 5.53e-17]'
70+
71+
This function also works as a wrapper:
72+
73+
>>> from flint import arb, ctx
74+
>>> @ctx.extraprec(10)
75+
... def f(x):
76+
... return x.sqrt().str()
77+
>>> f(arb(2))
78+
'[1.41421356237309505 +/- 1.46e-18]'
79+
"""
80+
return self.workprec(n + self.prec)
81+
82+
def extradps(self, n):
83+
"""
84+
Adds n digits of precision to the current flint context.
85+
86+
>>> from flint import arb, ctx
87+
>>> with ctx.extradps(5): x = arb(2).sqrt().str()
88+
>>> x
89+
'[1.4142135623730950488 +/- 2.76e-21]'
90+
91+
This function also works as a wrapper:
92+
93+
>>> from flint import arb, ctx
94+
>>> @ctx.extradps(10)
95+
... def f(x):
96+
... return x.sqrt().str()
97+
>>> f(arb(2))
98+
'[1.414213562373095048801689 +/- 3.13e-25]'
99+
"""
100+
return self.workdps(n + self.dps)
101+
102+
def workprec(self, n):
103+
"""
104+
Sets the working precision for the current flint context,
105+
using a python context manager.
106+
107+
>>> from flint import arb, ctx
108+
>>> with ctx.workprec(5): x = arb(2).sqrt().str()
109+
>>> x
110+
'[1e+0 +/- 0.438]'
111+
112+
This function also works as a wrapper:
113+
114+
>>> from flint import arb, ctx
115+
>>> @ctx.workprec(24)
116+
... def f(x):
117+
... return x.sqrt().str()
118+
>>> f(arb(2))
119+
'[1.41421 +/- 3.66e-6]'
120+
"""
121+
return PrecisionManager(self, eprec=n)
122+
123+
def workdps(self, n):
124+
"""
125+
Sets the working precision in digits for the current
126+
flint context, using a python context manager.
127+
128+
>>> from flint import arb, ctx
129+
>>> with ctx.workdps(5): x = arb(2).sqrt().str()
130+
>>> x
131+
'[1.4142 +/- 1.51e-5]'
132+
133+
This function also works as a wrapper:
134+
135+
>>> from flint import arb, ctx
136+
>>> @ctx.workdps(10)
137+
... def f(x):
138+
... return x.sqrt().str()
139+
>>> f(arb(2))
140+
'[1.414213562 +/- 3.85e-10]'
141+
"""
142+
return PrecisionManager(self, edps=n)
143+
60144
def __repr__(self):
61145
return "pretty = %-8s # pretty-print repr() output\n" \
62146
"unicode = %-8s # use unicode characters in output\n" \
@@ -69,4 +153,51 @@ cdef class FlintContext:
69153
def cleanup(self):
70154
flint_cleanup()
71155

156+
157+
cdef class PrecisionManager:
158+
cdef FlintContext ctx
159+
cdef int eprec
160+
cdef int edps
161+
cdef int _oldprec
162+
163+
def __init__(self, ctx, eprec=-1, edps=-1):
164+
if eprec != -1 and edps != -1:
165+
raise ValueError("two different precisions requested")
166+
167+
self.ctx = ctx
168+
169+
self.eprec = eprec
170+
self.edps = edps
171+
172+
def __call__(self, func):
173+
@wraps(func)
174+
def wrapped(*args, **kwargs):
175+
_oldprec = self.ctx.prec
176+
177+
try:
178+
if self.eprec != -1:
179+
self.ctx.prec = self.eprec
180+
181+
if self.edps != -1:
182+
self.ctx.dps = self.edps
183+
184+
return func(*args, **kwargs)
185+
finally:
186+
self.ctx.prec = _oldprec
187+
188+
return wrapped
189+
190+
def __enter__(self):
191+
self._oldprec = self.ctx.prec
192+
193+
if self.eprec != -1:
194+
self.ctx.prec = self.eprec
195+
196+
if self.edps != -1:
197+
self.ctx.dps = self.edps
198+
199+
def __exit__(self, type, value, traceback):
200+
self.ctx.prec = self._oldprec
201+
202+
72203
cdef FlintContext thectx = FlintContext()

0 commit comments

Comments
 (0)