ctx_iv.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. import operator
  2. from . import libmp
  3. from .libmp.backend import basestring
  4. from .libmp import (
  5. int_types, MPZ_ONE,
  6. prec_to_dps, dps_to_prec, repr_dps,
  7. round_floor, round_ceiling,
  8. fzero, finf, fninf, fnan,
  9. mpf_le, mpf_neg,
  10. from_int, from_float, from_str, from_rational,
  11. mpi_mid, mpi_delta, mpi_str,
  12. mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub,
  13. mpi_mul, mpi_div, mpi_pow_int, mpi_pow,
  14. mpi_from_str,
  15. mpci_pos, mpci_neg, mpci_add, mpci_sub, mpci_mul, mpci_div, mpci_pow,
  16. mpci_abs, mpci_pow, mpci_exp, mpci_log,
  17. ComplexResult,
  18. mpf_hash, mpc_hash)
  19. from .matrices.matrices import _matrix
  20. mpi_zero = (fzero, fzero)
  21. from .ctx_base import StandardBaseContext
  22. new = object.__new__
  23. def convert_mpf_(x, prec, rounding):
  24. if hasattr(x, "_mpf_"): return x._mpf_
  25. if isinstance(x, int_types): return from_int(x, prec, rounding)
  26. if isinstance(x, float): return from_float(x, prec, rounding)
  27. if isinstance(x, basestring): return from_str(x, prec, rounding)
  28. raise NotImplementedError
  29. class ivmpf(object):
  30. """
  31. Interval arithmetic class. Precision is controlled by iv.prec.
  32. """
  33. def __new__(cls, x=0):
  34. return cls.ctx.convert(x)
  35. def cast(self, cls, f_convert):
  36. a, b = self._mpi_
  37. if a == b:
  38. return cls(f_convert(a))
  39. raise ValueError
  40. def __int__(self):
  41. return self.cast(int, libmp.to_int)
  42. def __float__(self):
  43. return self.cast(float, libmp.to_float)
  44. def __complex__(self):
  45. return self.cast(complex, libmp.to_float)
  46. def __hash__(self):
  47. a, b = self._mpi_
  48. if a == b:
  49. return mpf_hash(a)
  50. else:
  51. return hash(self._mpi_)
  52. @property
  53. def real(self): return self
  54. @property
  55. def imag(self): return self.ctx.zero
  56. def conjugate(self): return self
  57. @property
  58. def a(self):
  59. a, b = self._mpi_
  60. return self.ctx.make_mpf((a, a))
  61. @property
  62. def b(self):
  63. a, b = self._mpi_
  64. return self.ctx.make_mpf((b, b))
  65. @property
  66. def mid(self):
  67. ctx = self.ctx
  68. v = mpi_mid(self._mpi_, ctx.prec)
  69. return ctx.make_mpf((v, v))
  70. @property
  71. def delta(self):
  72. ctx = self.ctx
  73. v = mpi_delta(self._mpi_, ctx.prec)
  74. return ctx.make_mpf((v,v))
  75. @property
  76. def _mpci_(self):
  77. return self._mpi_, mpi_zero
  78. def _compare(*args):
  79. raise TypeError("no ordering relation is defined for intervals")
  80. __gt__ = _compare
  81. __le__ = _compare
  82. __gt__ = _compare
  83. __ge__ = _compare
  84. def __contains__(self, t):
  85. t = self.ctx.mpf(t)
  86. return (self.a <= t.a) and (t.b <= self.b)
  87. def __str__(self):
  88. return mpi_str(self._mpi_, self.ctx.prec)
  89. def __repr__(self):
  90. if self.ctx.pretty:
  91. return str(self)
  92. a, b = self._mpi_
  93. n = repr_dps(self.ctx.prec)
  94. a = libmp.to_str(a, n)
  95. b = libmp.to_str(b, n)
  96. return "mpi(%r, %r)" % (a, b)
  97. def _compare(s, t, cmpfun):
  98. if not hasattr(t, "_mpi_"):
  99. try:
  100. t = s.ctx.convert(t)
  101. except:
  102. return NotImplemented
  103. return cmpfun(s._mpi_, t._mpi_)
  104. def __eq__(s, t): return s._compare(t, libmp.mpi_eq)
  105. def __ne__(s, t): return s._compare(t, libmp.mpi_ne)
  106. def __lt__(s, t): return s._compare(t, libmp.mpi_lt)
  107. def __le__(s, t): return s._compare(t, libmp.mpi_le)
  108. def __gt__(s, t): return s._compare(t, libmp.mpi_gt)
  109. def __ge__(s, t): return s._compare(t, libmp.mpi_ge)
  110. def __abs__(self):
  111. return self.ctx.make_mpf(mpi_abs(self._mpi_, self.ctx.prec))
  112. def __pos__(self):
  113. return self.ctx.make_mpf(mpi_pos(self._mpi_, self.ctx.prec))
  114. def __neg__(self):
  115. return self.ctx.make_mpf(mpi_neg(self._mpi_, self.ctx.prec))
  116. def ae(s, t, rel_eps=None, abs_eps=None):
  117. return s.ctx.almosteq(s, t, rel_eps, abs_eps)
  118. class ivmpc(object):
  119. def __new__(cls, re=0, im=0):
  120. re = cls.ctx.convert(re)
  121. im = cls.ctx.convert(im)
  122. y = new(cls)
  123. y._mpci_ = re._mpi_, im._mpi_
  124. return y
  125. def __hash__(self):
  126. (a, b), (c,d) = self._mpci_
  127. if a == b and c == d:
  128. return mpc_hash((a, c))
  129. else:
  130. return hash(self._mpci_)
  131. def __repr__(s):
  132. if s.ctx.pretty:
  133. return str(s)
  134. return "iv.mpc(%s, %s)" % (repr(s.real), repr(s.imag))
  135. def __str__(s):
  136. return "(%s + %s*j)" % (str(s.real), str(s.imag))
  137. @property
  138. def a(self):
  139. (a, b), (c,d) = self._mpci_
  140. return self.ctx.make_mpf((a, a))
  141. @property
  142. def b(self):
  143. (a, b), (c,d) = self._mpci_
  144. return self.ctx.make_mpf((b, b))
  145. @property
  146. def c(self):
  147. (a, b), (c,d) = self._mpci_
  148. return self.ctx.make_mpf((c, c))
  149. @property
  150. def d(self):
  151. (a, b), (c,d) = self._mpci_
  152. return self.ctx.make_mpf((d, d))
  153. @property
  154. def real(s):
  155. return s.ctx.make_mpf(s._mpci_[0])
  156. @property
  157. def imag(s):
  158. return s.ctx.make_mpf(s._mpci_[1])
  159. def conjugate(s):
  160. a, b = s._mpci_
  161. return s.ctx.make_mpc((a, mpf_neg(b)))
  162. def overlap(s, t):
  163. t = s.ctx.convert(t)
  164. real_overlap = (s.a <= t.a <= s.b) or (s.a <= t.b <= s.b) or (t.a <= s.a <= t.b) or (t.a <= s.b <= t.b)
  165. imag_overlap = (s.c <= t.c <= s.d) or (s.c <= t.d <= s.d) or (t.c <= s.c <= t.d) or (t.c <= s.d <= t.d)
  166. return real_overlap and imag_overlap
  167. def __contains__(s, t):
  168. t = s.ctx.convert(t)
  169. return t.real in s.real and t.imag in s.imag
  170. def _compare(s, t, ne=False):
  171. if not isinstance(t, s.ctx._types):
  172. try:
  173. t = s.ctx.convert(t)
  174. except:
  175. return NotImplemented
  176. if hasattr(t, '_mpi_'):
  177. tval = t._mpi_, mpi_zero
  178. elif hasattr(t, '_mpci_'):
  179. tval = t._mpci_
  180. if ne:
  181. return s._mpci_ != tval
  182. return s._mpci_ == tval
  183. def __eq__(s, t): return s._compare(t)
  184. def __ne__(s, t): return s._compare(t, True)
  185. def __lt__(s, t): raise TypeError("complex intervals cannot be ordered")
  186. __le__ = __gt__ = __ge__ = __lt__
  187. def __neg__(s): return s.ctx.make_mpc(mpci_neg(s._mpci_, s.ctx.prec))
  188. def __pos__(s): return s.ctx.make_mpc(mpci_pos(s._mpci_, s.ctx.prec))
  189. def __abs__(s): return s.ctx.make_mpf(mpci_abs(s._mpci_, s.ctx.prec))
  190. def ae(s, t, rel_eps=None, abs_eps=None):
  191. return s.ctx.almosteq(s, t, rel_eps, abs_eps)
  192. def _binary_op(f_real, f_complex):
  193. def g_complex(ctx, sval, tval):
  194. return ctx.make_mpc(f_complex(sval, tval, ctx.prec))
  195. def g_real(ctx, sval, tval):
  196. try:
  197. return ctx.make_mpf(f_real(sval, tval, ctx.prec))
  198. except ComplexResult:
  199. sval = (sval, mpi_zero)
  200. tval = (tval, mpi_zero)
  201. return g_complex(ctx, sval, tval)
  202. def lop_real(s, t):
  203. if isinstance(t, _matrix): return NotImplemented
  204. ctx = s.ctx
  205. if not isinstance(t, ctx._types): t = ctx.convert(t)
  206. if hasattr(t, "_mpi_"): return g_real(ctx, s._mpi_, t._mpi_)
  207. if hasattr(t, "_mpci_"): return g_complex(ctx, (s._mpi_, mpi_zero), t._mpci_)
  208. return NotImplemented
  209. def rop_real(s, t):
  210. ctx = s.ctx
  211. if not isinstance(t, ctx._types): t = ctx.convert(t)
  212. if hasattr(t, "_mpi_"): return g_real(ctx, t._mpi_, s._mpi_)
  213. if hasattr(t, "_mpci_"): return g_complex(ctx, t._mpci_, (s._mpi_, mpi_zero))
  214. return NotImplemented
  215. def lop_complex(s, t):
  216. if isinstance(t, _matrix): return NotImplemented
  217. ctx = s.ctx
  218. if not isinstance(t, s.ctx._types):
  219. try:
  220. t = s.ctx.convert(t)
  221. except (ValueError, TypeError):
  222. return NotImplemented
  223. return g_complex(ctx, s._mpci_, t._mpci_)
  224. def rop_complex(s, t):
  225. ctx = s.ctx
  226. if not isinstance(t, s.ctx._types):
  227. t = s.ctx.convert(t)
  228. return g_complex(ctx, t._mpci_, s._mpci_)
  229. return lop_real, rop_real, lop_complex, rop_complex
  230. ivmpf.__add__, ivmpf.__radd__, ivmpc.__add__, ivmpc.__radd__ = _binary_op(mpi_add, mpci_add)
  231. ivmpf.__sub__, ivmpf.__rsub__, ivmpc.__sub__, ivmpc.__rsub__ = _binary_op(mpi_sub, mpci_sub)
  232. ivmpf.__mul__, ivmpf.__rmul__, ivmpc.__mul__, ivmpc.__rmul__ = _binary_op(mpi_mul, mpci_mul)
  233. ivmpf.__div__, ivmpf.__rdiv__, ivmpc.__div__, ivmpc.__rdiv__ = _binary_op(mpi_div, mpci_div)
  234. ivmpf.__pow__, ivmpf.__rpow__, ivmpc.__pow__, ivmpc.__rpow__ = _binary_op(mpi_pow, mpci_pow)
  235. ivmpf.__truediv__ = ivmpf.__div__; ivmpf.__rtruediv__ = ivmpf.__rdiv__
  236. ivmpc.__truediv__ = ivmpc.__div__; ivmpc.__rtruediv__ = ivmpc.__rdiv__
  237. class ivmpf_constant(ivmpf):
  238. def __new__(cls, f):
  239. self = new(cls)
  240. self._f = f
  241. return self
  242. def _get_mpi_(self):
  243. prec = self.ctx._prec[0]
  244. a = self._f(prec, round_floor)
  245. b = self._f(prec, round_ceiling)
  246. return a, b
  247. _mpi_ = property(_get_mpi_)
  248. class MPIntervalContext(StandardBaseContext):
  249. def __init__(ctx):
  250. ctx.mpf = type('ivmpf', (ivmpf,), {})
  251. ctx.mpc = type('ivmpc', (ivmpc,), {})
  252. ctx._types = (ctx.mpf, ctx.mpc)
  253. ctx._constant = type('ivmpf_constant', (ivmpf_constant,), {})
  254. ctx._prec = [53]
  255. ctx._set_prec(53)
  256. ctx._constant._ctxdata = ctx.mpf._ctxdata = ctx.mpc._ctxdata = [ctx.mpf, new, ctx._prec]
  257. ctx._constant.ctx = ctx.mpf.ctx = ctx.mpc.ctx = ctx
  258. ctx.pretty = False
  259. StandardBaseContext.__init__(ctx)
  260. ctx._init_builtins()
  261. def _mpi(ctx, a, b=None):
  262. if b is None:
  263. return ctx.mpf(a)
  264. return ctx.mpf((a,b))
  265. def _init_builtins(ctx):
  266. ctx.one = ctx.mpf(1)
  267. ctx.zero = ctx.mpf(0)
  268. ctx.inf = ctx.mpf('inf')
  269. ctx.ninf = -ctx.inf
  270. ctx.nan = ctx.mpf('nan')
  271. ctx.j = ctx.mpc(0,1)
  272. ctx.exp = ctx._wrap_mpi_function(libmp.mpi_exp, libmp.mpci_exp)
  273. ctx.sqrt = ctx._wrap_mpi_function(libmp.mpi_sqrt)
  274. ctx.ln = ctx._wrap_mpi_function(libmp.mpi_log, libmp.mpci_log)
  275. ctx.cos = ctx._wrap_mpi_function(libmp.mpi_cos, libmp.mpci_cos)
  276. ctx.sin = ctx._wrap_mpi_function(libmp.mpi_sin, libmp.mpci_sin)
  277. ctx.tan = ctx._wrap_mpi_function(libmp.mpi_tan)
  278. ctx.gamma = ctx._wrap_mpi_function(libmp.mpi_gamma, libmp.mpci_gamma)
  279. ctx.loggamma = ctx._wrap_mpi_function(libmp.mpi_loggamma, libmp.mpci_loggamma)
  280. ctx.rgamma = ctx._wrap_mpi_function(libmp.mpi_rgamma, libmp.mpci_rgamma)
  281. ctx.factorial = ctx._wrap_mpi_function(libmp.mpi_factorial, libmp.mpci_factorial)
  282. ctx.fac = ctx.factorial
  283. ctx.eps = ctx._constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1))
  284. ctx.pi = ctx._constant(libmp.mpf_pi)
  285. ctx.e = ctx._constant(libmp.mpf_e)
  286. ctx.ln2 = ctx._constant(libmp.mpf_ln2)
  287. ctx.ln10 = ctx._constant(libmp.mpf_ln10)
  288. ctx.phi = ctx._constant(libmp.mpf_phi)
  289. ctx.euler = ctx._constant(libmp.mpf_euler)
  290. ctx.catalan = ctx._constant(libmp.mpf_catalan)
  291. ctx.glaisher = ctx._constant(libmp.mpf_glaisher)
  292. ctx.khinchin = ctx._constant(libmp.mpf_khinchin)
  293. ctx.twinprime = ctx._constant(libmp.mpf_twinprime)
  294. def _wrap_mpi_function(ctx, f_real, f_complex=None):
  295. def g(x, **kwargs):
  296. if kwargs:
  297. prec = kwargs.get('prec', ctx._prec[0])
  298. else:
  299. prec = ctx._prec[0]
  300. x = ctx.convert(x)
  301. if hasattr(x, "_mpi_"):
  302. return ctx.make_mpf(f_real(x._mpi_, prec))
  303. if hasattr(x, "_mpci_"):
  304. return ctx.make_mpc(f_complex(x._mpci_, prec))
  305. raise ValueError
  306. return g
  307. @classmethod
  308. def _wrap_specfun(cls, name, f, wrap):
  309. if wrap:
  310. def f_wrapped(ctx, *args, **kwargs):
  311. convert = ctx.convert
  312. args = [convert(a) for a in args]
  313. prec = ctx.prec
  314. try:
  315. ctx.prec += 10
  316. retval = f(ctx, *args, **kwargs)
  317. finally:
  318. ctx.prec = prec
  319. return +retval
  320. else:
  321. f_wrapped = f
  322. setattr(cls, name, f_wrapped)
  323. def _set_prec(ctx, n):
  324. ctx._prec[0] = max(1, int(n))
  325. ctx._dps = prec_to_dps(n)
  326. def _set_dps(ctx, n):
  327. ctx._prec[0] = dps_to_prec(n)
  328. ctx._dps = max(1, int(n))
  329. prec = property(lambda ctx: ctx._prec[0], _set_prec)
  330. dps = property(lambda ctx: ctx._dps, _set_dps)
  331. def make_mpf(ctx, v):
  332. a = new(ctx.mpf)
  333. a._mpi_ = v
  334. return a
  335. def make_mpc(ctx, v):
  336. a = new(ctx.mpc)
  337. a._mpci_ = v
  338. return a
  339. def _mpq(ctx, pq):
  340. p, q = pq
  341. a = libmp.from_rational(p, q, ctx.prec, round_floor)
  342. b = libmp.from_rational(p, q, ctx.prec, round_ceiling)
  343. return ctx.make_mpf((a, b))
  344. def convert(ctx, x):
  345. if isinstance(x, (ctx.mpf, ctx.mpc)):
  346. return x
  347. if isinstance(x, ctx._constant):
  348. return +x
  349. if isinstance(x, complex) or hasattr(x, "_mpc_"):
  350. re = ctx.convert(x.real)
  351. im = ctx.convert(x.imag)
  352. return ctx.mpc(re,im)
  353. if isinstance(x, basestring):
  354. v = mpi_from_str(x, ctx.prec)
  355. return ctx.make_mpf(v)
  356. if hasattr(x, "_mpi_"):
  357. a, b = x._mpi_
  358. else:
  359. try:
  360. a, b = x
  361. except (TypeError, ValueError):
  362. a = b = x
  363. if hasattr(a, "_mpi_"):
  364. a = a._mpi_[0]
  365. else:
  366. a = convert_mpf_(a, ctx.prec, round_floor)
  367. if hasattr(b, "_mpi_"):
  368. b = b._mpi_[1]
  369. else:
  370. b = convert_mpf_(b, ctx.prec, round_ceiling)
  371. if a == fnan or b == fnan:
  372. a = fninf
  373. b = finf
  374. assert mpf_le(a, b), "endpoints must be properly ordered"
  375. return ctx.make_mpf((a, b))
  376. def nstr(ctx, x, n=5, **kwargs):
  377. x = ctx.convert(x)
  378. if hasattr(x, "_mpi_"):
  379. return libmp.mpi_to_str(x._mpi_, n, **kwargs)
  380. if hasattr(x, "_mpci_"):
  381. re = libmp.mpi_to_str(x._mpci_[0], n, **kwargs)
  382. im = libmp.mpi_to_str(x._mpci_[1], n, **kwargs)
  383. return "(%s + %s*j)" % (re, im)
  384. def mag(ctx, x):
  385. x = ctx.convert(x)
  386. if isinstance(x, ctx.mpc):
  387. return max(ctx.mag(x.real), ctx.mag(x.imag)) + 1
  388. a, b = libmp.mpi_abs(x._mpi_)
  389. sign, man, exp, bc = b
  390. if man:
  391. return exp+bc
  392. if b == fzero:
  393. return ctx.ninf
  394. if b == fnan:
  395. return ctx.nan
  396. return ctx.inf
  397. def isnan(ctx, x):
  398. return False
  399. def isinf(ctx, x):
  400. return x == ctx.inf
  401. def isint(ctx, x):
  402. x = ctx.convert(x)
  403. a, b = x._mpi_
  404. if a == b:
  405. sign, man, exp, bc = a
  406. if man:
  407. return exp >= 0
  408. return a == fzero
  409. return None
  410. def ldexp(ctx, x, n):
  411. a, b = ctx.convert(x)._mpi_
  412. a = libmp.mpf_shift(a, n)
  413. b = libmp.mpf_shift(b, n)
  414. return ctx.make_mpf((a,b))
  415. def absmin(ctx, x):
  416. return abs(ctx.convert(x)).a
  417. def absmax(ctx, x):
  418. return abs(ctx.convert(x)).b
  419. def atan2(ctx, y, x):
  420. y = ctx.convert(y)._mpi_
  421. x = ctx.convert(x)._mpi_
  422. return ctx.make_mpf(libmp.mpi_atan2(y,x,ctx.prec))
  423. def _convert_param(ctx, x):
  424. if isinstance(x, libmp.int_types):
  425. return x, 'Z'
  426. if isinstance(x, tuple):
  427. p, q = x
  428. return (ctx.mpf(p) / ctx.mpf(q), 'R')
  429. x = ctx.convert(x)
  430. if isinstance(x, ctx.mpf):
  431. return x, 'R'
  432. if isinstance(x, ctx.mpc):
  433. return x, 'C'
  434. raise ValueError
  435. def _is_real_type(ctx, z):
  436. return isinstance(z, ctx.mpf) or isinstance(z, int_types)
  437. def _is_complex_type(ctx, z):
  438. return isinstance(z, ctx.mpc)
  439. def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs):
  440. coeffs = list(coeffs)
  441. num = range(p)
  442. den = range(p,p+q)
  443. #tol = ctx.eps
  444. s = t = ctx.one
  445. k = 0
  446. while 1:
  447. for i in num: t *= (coeffs[i]+k)
  448. for i in den: t /= (coeffs[i]+k)
  449. k += 1; t /= k; t *= z; s += t
  450. if t == 0:
  451. return s
  452. #if abs(t) < tol:
  453. # return s
  454. if k > maxterms:
  455. raise ctx.NoConvergence
  456. # Register with "numbers" ABC
  457. # We do not subclass, hence we do not use the @abstractmethod checks. While
  458. # this is less invasive it may turn out that we do not actually support
  459. # parts of the expected interfaces. See
  460. # http://docs.python.org/2/library/numbers.html for list of abstract
  461. # methods.
  462. try:
  463. import numbers
  464. numbers.Complex.register(ivmpc)
  465. numbers.Real.register(ivmpf)
  466. except ImportError:
  467. pass