ctx_base.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. from operator import gt, lt
  2. from .libmp.backend import xrange
  3. from .functions.functions import SpecialFunctions
  4. from .functions.rszeta import RSCache
  5. from .calculus.quadrature import QuadratureMethods
  6. from .calculus.inverselaplace import LaplaceTransformInversionMethods
  7. from .calculus.calculus import CalculusMethods
  8. from .calculus.optimization import OptimizationMethods
  9. from .calculus.odes import ODEMethods
  10. from .matrices.matrices import MatrixMethods
  11. from .matrices.calculus import MatrixCalculusMethods
  12. from .matrices.linalg import LinearAlgebraMethods
  13. from .matrices.eigen import Eigen
  14. from .identification import IdentificationMethods
  15. from .visualization import VisualizationMethods
  16. from . import libmp
  17. class Context(object):
  18. pass
  19. class StandardBaseContext(Context,
  20. SpecialFunctions,
  21. RSCache,
  22. QuadratureMethods,
  23. LaplaceTransformInversionMethods,
  24. CalculusMethods,
  25. MatrixMethods,
  26. MatrixCalculusMethods,
  27. LinearAlgebraMethods,
  28. Eigen,
  29. IdentificationMethods,
  30. OptimizationMethods,
  31. ODEMethods,
  32. VisualizationMethods):
  33. NoConvergence = libmp.NoConvergence
  34. ComplexResult = libmp.ComplexResult
  35. def __init__(ctx):
  36. ctx._aliases = {}
  37. # Call those that need preinitialization (e.g. for wrappers)
  38. SpecialFunctions.__init__(ctx)
  39. RSCache.__init__(ctx)
  40. QuadratureMethods.__init__(ctx)
  41. LaplaceTransformInversionMethods.__init__(ctx)
  42. CalculusMethods.__init__(ctx)
  43. MatrixMethods.__init__(ctx)
  44. def _init_aliases(ctx):
  45. for alias, value in ctx._aliases.items():
  46. try:
  47. setattr(ctx, alias, getattr(ctx, value))
  48. except AttributeError:
  49. pass
  50. _fixed_precision = False
  51. # XXX
  52. verbose = False
  53. def warn(ctx, msg):
  54. print("Warning:", msg)
  55. def bad_domain(ctx, msg):
  56. raise ValueError(msg)
  57. def _re(ctx, x):
  58. if hasattr(x, "real"):
  59. return x.real
  60. return x
  61. def _im(ctx, x):
  62. if hasattr(x, "imag"):
  63. return x.imag
  64. return ctx.zero
  65. def _as_points(ctx, x):
  66. return x
  67. def fneg(ctx, x, **kwargs):
  68. return -ctx.convert(x)
  69. def fadd(ctx, x, y, **kwargs):
  70. return ctx.convert(x)+ctx.convert(y)
  71. def fsub(ctx, x, y, **kwargs):
  72. return ctx.convert(x)-ctx.convert(y)
  73. def fmul(ctx, x, y, **kwargs):
  74. return ctx.convert(x)*ctx.convert(y)
  75. def fdiv(ctx, x, y, **kwargs):
  76. return ctx.convert(x)/ctx.convert(y)
  77. def fsum(ctx, args, absolute=False, squared=False):
  78. if absolute:
  79. if squared:
  80. return sum((abs(x)**2 for x in args), ctx.zero)
  81. return sum((abs(x) for x in args), ctx.zero)
  82. if squared:
  83. return sum((x**2 for x in args), ctx.zero)
  84. return sum(args, ctx.zero)
  85. def fdot(ctx, xs, ys=None, conjugate=False):
  86. if ys is not None:
  87. xs = zip(xs, ys)
  88. if conjugate:
  89. cf = ctx.conj
  90. return sum((x*cf(y) for (x,y) in xs), ctx.zero)
  91. else:
  92. return sum((x*y for (x,y) in xs), ctx.zero)
  93. def fprod(ctx, args):
  94. prod = ctx.one
  95. for arg in args:
  96. prod *= arg
  97. return prod
  98. def nprint(ctx, x, n=6, **kwargs):
  99. """
  100. Equivalent to ``print(nstr(x, n))``.
  101. """
  102. print(ctx.nstr(x, n, **kwargs))
  103. def chop(ctx, x, tol=None):
  104. """
  105. Chops off small real or imaginary parts, or converts
  106. numbers close to zero to exact zeros. The input can be a
  107. single number or an iterable::
  108. >>> from mpmath import *
  109. >>> mp.dps = 15; mp.pretty = False
  110. >>> chop(5+1e-10j, tol=1e-9)
  111. mpf('5.0')
  112. >>> nprint(chop([1.0, 1e-20, 3+1e-18j, -4, 2]))
  113. [1.0, 0.0, 3.0, -4.0, 2.0]
  114. The tolerance defaults to ``100*eps``.
  115. """
  116. if tol is None:
  117. tol = 100*ctx.eps
  118. try:
  119. x = ctx.convert(x)
  120. absx = abs(x)
  121. if abs(x) < tol:
  122. return ctx.zero
  123. if ctx._is_complex_type(x):
  124. #part_tol = min(tol, absx*tol)
  125. part_tol = max(tol, absx*tol)
  126. if abs(x.imag) < part_tol:
  127. return x.real
  128. if abs(x.real) < part_tol:
  129. return ctx.mpc(0, x.imag)
  130. except TypeError:
  131. if isinstance(x, ctx.matrix):
  132. return x.apply(lambda a: ctx.chop(a, tol))
  133. if hasattr(x, "__iter__"):
  134. return [ctx.chop(a, tol) for a in x]
  135. return x
  136. def almosteq(ctx, s, t, rel_eps=None, abs_eps=None):
  137. r"""
  138. Determine whether the difference between `s` and `t` is smaller
  139. than a given epsilon, either relatively or absolutely.
  140. Both a maximum relative difference and a maximum difference
  141. ('epsilons') may be specified. The absolute difference is
  142. defined as `|s-t|` and the relative difference is defined
  143. as `|s-t|/\max(|s|, |t|)`.
  144. If only one epsilon is given, both are set to the same value.
  145. If none is given, both epsilons are set to `2^{-p+m}` where
  146. `p` is the current working precision and `m` is a small
  147. integer. The default setting typically allows :func:`~mpmath.almosteq`
  148. to be used to check for mathematical equality
  149. in the presence of small rounding errors.
  150. **Examples**
  151. >>> from mpmath import *
  152. >>> mp.dps = 15
  153. >>> almosteq(3.141592653589793, 3.141592653589790)
  154. True
  155. >>> almosteq(3.141592653589793, 3.141592653589700)
  156. False
  157. >>> almosteq(3.141592653589793, 3.141592653589700, 1e-10)
  158. True
  159. >>> almosteq(1e-20, 2e-20)
  160. True
  161. >>> almosteq(1e-20, 2e-20, rel_eps=0, abs_eps=0)
  162. False
  163. """
  164. t = ctx.convert(t)
  165. if abs_eps is None and rel_eps is None:
  166. rel_eps = abs_eps = ctx.ldexp(1, -ctx.prec+4)
  167. if abs_eps is None:
  168. abs_eps = rel_eps
  169. elif rel_eps is None:
  170. rel_eps = abs_eps
  171. diff = abs(s-t)
  172. if diff <= abs_eps:
  173. return True
  174. abss = abs(s)
  175. abst = abs(t)
  176. if abss < abst:
  177. err = diff/abst
  178. else:
  179. err = diff/abss
  180. return err <= rel_eps
  181. def arange(ctx, *args):
  182. r"""
  183. This is a generalized version of Python's :func:`~mpmath.range` function
  184. that accepts fractional endpoints and step sizes and
  185. returns a list of ``mpf`` instances. Like :func:`~mpmath.range`,
  186. :func:`~mpmath.arange` can be called with 1, 2 or 3 arguments:
  187. ``arange(b)``
  188. `[0, 1, 2, \ldots, x]`
  189. ``arange(a, b)``
  190. `[a, a+1, a+2, \ldots, x]`
  191. ``arange(a, b, h)``
  192. `[a, a+h, a+h, \ldots, x]`
  193. where `b-1 \le x < b` (in the third case, `b-h \le x < b`).
  194. Like Python's :func:`~mpmath.range`, the endpoint is not included. To
  195. produce ranges where the endpoint is included, :func:`~mpmath.linspace`
  196. is more convenient.
  197. **Examples**
  198. >>> from mpmath import *
  199. >>> mp.dps = 15; mp.pretty = False
  200. >>> arange(4)
  201. [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0')]
  202. >>> arange(1, 2, 0.25)
  203. [mpf('1.0'), mpf('1.25'), mpf('1.5'), mpf('1.75')]
  204. >>> arange(1, -1, -0.75)
  205. [mpf('1.0'), mpf('0.25'), mpf('-0.5')]
  206. """
  207. if not len(args) <= 3:
  208. raise TypeError('arange expected at most 3 arguments, got %i'
  209. % len(args))
  210. if not len(args) >= 1:
  211. raise TypeError('arange expected at least 1 argument, got %i'
  212. % len(args))
  213. # set default
  214. a = 0
  215. dt = 1
  216. # interpret arguments
  217. if len(args) == 1:
  218. b = args[0]
  219. elif len(args) >= 2:
  220. a = args[0]
  221. b = args[1]
  222. if len(args) == 3:
  223. dt = args[2]
  224. a, b, dt = ctx.mpf(a), ctx.mpf(b), ctx.mpf(dt)
  225. assert a + dt != a, 'dt is too small and would cause an infinite loop'
  226. # adapt code for sign of dt
  227. if a > b:
  228. if dt > 0:
  229. return []
  230. op = gt
  231. else:
  232. if dt < 0:
  233. return []
  234. op = lt
  235. # create list
  236. result = []
  237. i = 0
  238. t = a
  239. while 1:
  240. t = a + dt*i
  241. i += 1
  242. if op(t, b):
  243. result.append(t)
  244. else:
  245. break
  246. return result
  247. def linspace(ctx, *args, **kwargs):
  248. """
  249. ``linspace(a, b, n)`` returns a list of `n` evenly spaced
  250. samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)``
  251. is also valid.
  252. This function is often more convenient than :func:`~mpmath.arange`
  253. for partitioning an interval into subintervals, since
  254. the endpoint is included::
  255. >>> from mpmath import *
  256. >>> mp.dps = 15; mp.pretty = False
  257. >>> linspace(1, 4, 4)
  258. [mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')]
  259. You may also provide the keyword argument ``endpoint=False``::
  260. >>> linspace(1, 4, 4, endpoint=False)
  261. [mpf('1.0'), mpf('1.75'), mpf('2.5'), mpf('3.25')]
  262. """
  263. if len(args) == 3:
  264. a = ctx.mpf(args[0])
  265. b = ctx.mpf(args[1])
  266. n = int(args[2])
  267. elif len(args) == 2:
  268. assert hasattr(args[0], '_mpi_')
  269. a = args[0].a
  270. b = args[0].b
  271. n = int(args[1])
  272. else:
  273. raise TypeError('linspace expected 2 or 3 arguments, got %i' \
  274. % len(args))
  275. if n < 1:
  276. raise ValueError('n must be greater than 0')
  277. if not 'endpoint' in kwargs or kwargs['endpoint']:
  278. if n == 1:
  279. return [ctx.mpf(a)]
  280. step = (b - a) / ctx.mpf(n - 1)
  281. y = [i*step + a for i in xrange(n)]
  282. y[-1] = b
  283. else:
  284. step = (b - a) / ctx.mpf(n)
  285. y = [i*step + a for i in xrange(n)]
  286. return y
  287. def cos_sin(ctx, z, **kwargs):
  288. return ctx.cos(z, **kwargs), ctx.sin(z, **kwargs)
  289. def cospi_sinpi(ctx, z, **kwargs):
  290. return ctx.cospi(z, **kwargs), ctx.sinpi(z, **kwargs)
  291. def _default_hyper_maxprec(ctx, p):
  292. return int(1000 * p**0.25 + 4*p)
  293. _gcd = staticmethod(libmp.gcd)
  294. list_primes = staticmethod(libmp.list_primes)
  295. isprime = staticmethod(libmp.isprime)
  296. bernfrac = staticmethod(libmp.bernfrac)
  297. moebius = staticmethod(libmp.moebius)
  298. _ifac = staticmethod(libmp.ifac)
  299. _eulernum = staticmethod(libmp.eulernum)
  300. _stirling1 = staticmethod(libmp.stirling1)
  301. _stirling2 = staticmethod(libmp.stirling2)
  302. def sum_accurately(ctx, terms, check_step=1):
  303. prec = ctx.prec
  304. try:
  305. extraprec = 10
  306. while 1:
  307. ctx.prec = prec + extraprec + 5
  308. max_mag = ctx.ninf
  309. s = ctx.zero
  310. k = 0
  311. for term in terms():
  312. s += term
  313. if (not k % check_step) and term:
  314. term_mag = ctx.mag(term)
  315. max_mag = max(max_mag, term_mag)
  316. sum_mag = ctx.mag(s)
  317. if sum_mag - term_mag > ctx.prec:
  318. break
  319. k += 1
  320. cancellation = max_mag - sum_mag
  321. if cancellation != cancellation:
  322. break
  323. if cancellation < extraprec or ctx._fixed_precision:
  324. break
  325. extraprec += min(ctx.prec, cancellation)
  326. return s
  327. finally:
  328. ctx.prec = prec
  329. def mul_accurately(ctx, factors, check_step=1):
  330. prec = ctx.prec
  331. try:
  332. extraprec = 10
  333. while 1:
  334. ctx.prec = prec + extraprec + 5
  335. max_mag = ctx.ninf
  336. one = ctx.one
  337. s = one
  338. k = 0
  339. for factor in factors():
  340. s *= factor
  341. term = factor - one
  342. if (not k % check_step):
  343. term_mag = ctx.mag(term)
  344. max_mag = max(max_mag, term_mag)
  345. sum_mag = ctx.mag(s-one)
  346. #if sum_mag - term_mag > ctx.prec:
  347. # break
  348. if -term_mag > ctx.prec:
  349. break
  350. k += 1
  351. cancellation = max_mag - sum_mag
  352. if cancellation != cancellation:
  353. break
  354. if cancellation < extraprec or ctx._fixed_precision:
  355. break
  356. extraprec += min(ctx.prec, cancellation)
  357. return s
  358. finally:
  359. ctx.prec = prec
  360. def power(ctx, x, y):
  361. r"""Converts `x` and `y` to mpmath numbers and evaluates
  362. `x^y = \exp(y \log(x))`::
  363. >>> from mpmath import *
  364. >>> mp.dps = 30; mp.pretty = True
  365. >>> power(2, 0.5)
  366. 1.41421356237309504880168872421
  367. This shows the leading few digits of a large Mersenne prime
  368. (performing the exact calculation ``2**43112609-1`` and
  369. displaying the result in Python would be very slow)::
  370. >>> power(2, 43112609)-1
  371. 3.16470269330255923143453723949e+12978188
  372. """
  373. return ctx.convert(x) ** ctx.convert(y)
  374. def _zeta_int(ctx, n):
  375. return ctx.zeta(n)
  376. def maxcalls(ctx, f, N):
  377. """
  378. Return a wrapped copy of *f* that raises ``NoConvergence`` when *f*
  379. has been called more than *N* times::
  380. >>> from mpmath import *
  381. >>> mp.dps = 15
  382. >>> f = maxcalls(sin, 10)
  383. >>> print(sum(f(n) for n in range(10)))
  384. 1.95520948210738
  385. >>> f(10) # doctest: +IGNORE_EXCEPTION_DETAIL
  386. Traceback (most recent call last):
  387. ...
  388. NoConvergence: maxcalls: function evaluated 10 times
  389. """
  390. counter = [0]
  391. def f_maxcalls_wrapped(*args, **kwargs):
  392. counter[0] += 1
  393. if counter[0] > N:
  394. raise ctx.NoConvergence("maxcalls: function evaluated %i times" % N)
  395. return f(*args, **kwargs)
  396. return f_maxcalls_wrapped
  397. def memoize(ctx, f):
  398. """
  399. Return a wrapped copy of *f* that caches computed values, i.e.
  400. a memoized copy of *f*. Values are only reused if the cached precision
  401. is equal to or higher than the working precision::
  402. >>> from mpmath import *
  403. >>> mp.dps = 15; mp.pretty = True
  404. >>> f = memoize(maxcalls(sin, 1))
  405. >>> f(2)
  406. 0.909297426825682
  407. >>> f(2)
  408. 0.909297426825682
  409. >>> mp.dps = 25
  410. >>> f(2) # doctest: +IGNORE_EXCEPTION_DETAIL
  411. Traceback (most recent call last):
  412. ...
  413. NoConvergence: maxcalls: function evaluated 1 times
  414. """
  415. f_cache = {}
  416. def f_cached(*args, **kwargs):
  417. if kwargs:
  418. key = args, tuple(kwargs.items())
  419. else:
  420. key = args
  421. prec = ctx.prec
  422. if key in f_cache:
  423. cprec, cvalue = f_cache[key]
  424. if cprec >= prec:
  425. return +cvalue
  426. value = f(*args, **kwargs)
  427. f_cache[key] = (prec, value)
  428. return value
  429. f_cached.__name__ = f.__name__
  430. f_cached.__doc__ = f.__doc__
  431. return f_cached