def _update_approx(self, center, rad, prec, derivatives): ini, path = self._path_to(center, prec) eps = RBF.one() >> prec # keep="all" won't do anything until _path_to returns better paths ctx = ancont.Context(self.dop, path, eps, keep="all") pairs = ancont.analytic_continuation(ctx, ini=ini) for (vert, val) in pairs: known = self._inivecs.get(vert) if known is None or known[0].accuracy() < val[0][0].accuracy(): self._inivecs[vert] = [c[0] for c in val] logger.info( "computing new polynomial approximations: " "ini=%s, path=%s, rad=%s, eps=%s, ord=%s", ini, path, rad, eps, derivatives) polys = polapprox.doit(self.dop, ini=ini, path=path, rad=rad, eps=eps, derivatives=derivatives, x_is_real=True, economization=polapprox.chebyshev_economization) logger.info("...done") approx = self._polys.get(center, []) new_approx = [] for ord, pol in enumerate(polys): if ord >= len(approx) or approx[ord].prec < prec: new_approx.append(RealPolApprox(pol, prec)) else: new_approx.append(approx[ord]) self._update_approx_hook(center, rad, polys) self._polys[center] = new_approx return polys
def _disk(self, pt): assert pt.is_real() # Since approximation disks satisfy 2·rad ≤ dist(center, sing), any # approximation disk containing pt must have rad ≤ dist(pt, sing) max_rad = pt.dist_to_sing().min(self.max_rad) # What we want is the largest such disk containing pt expo = ZZ(max_rad.log(2).upper().ceil()) - 1 # rad = 2^expo logger.log(logging.DEBUG - 2, "max_rad = %s, expo = %s", max_rad, expo) while True: approx_pt = pt.approx_abs_real(-expo) mantissa = (approx_pt.squash() >> expo).floor() if ZZ(mantissa) % 2 == 0: mantissa += 1 center = mantissa << expo dist = Point(center, pt.dop).dist_to_sing() rad = RBF.one() << expo logger.log( logging.DEBUG - 2, "candidate disk: approx_pt = %s, mantissa = %s, " "center = %s, dist = %s, rad = %s", approx_pt, mantissa, center, dist, rad) if safe_ge(dist >> 1, rad): break expo -= 1 logger.debug("disk for %s: center=%s, rad=%s", pt, center, rad) # pt may be a ball with nonzero radius: check that it is contained in # our candidate disk log = RBF.zero() if 0 in approx_pt else approx_pt.abs().log(2) F = RealBallField(ZZ((expo - log).max(0).upper().ceil()) + 10) dist_to_center = (F(approx_pt) - F(center)).abs() if not safe_le(dist_to_center, rad): assert not safe_gt((approx_pt.squash() - center).squash(), rad) logger.info("check that |%s - %s| < %s failed", approx_pt, center, rad) return None, None # exactify center so that subsequent computations are not limited by the # precision of its parent center = QQ(center) return center, rad
def approx(self, pt, prec=None, post_transform=None): r""" TESTS:: sage: from ore_algebra import * sage: from ore_algebra.analytic.function import DFiniteFunction sage: DiffOps, x, Dx = DifferentialOperators() sage: h = DFiniteFunction(Dx^3-1, [0, 0, 1]) sage: h.approx(0, post_transform=Dx^2) [2.0000000000000...] sage: f = DFiniteFunction((x^2 + 1)*Dx^2 + 2*x*Dx, [0, 1], max_prec=20) sage: f.approx(1/3, prec=10) [0.32...] sage: f.approx(1/3, prec=40) [0.321750554396...] sage: f.approx(1/3, prec=10, post_transform=Dx) [0.9...] sage: f.approx(1/3, prec=40, post_transform=Dx) [0.900000000000...] sage: f.approx(1/3, prec=10, post_transform=Dx^2) [-0.54...] sage: f.approx(1/3, prec=40, post_transform=Dx^2) [-0.540000000000...] """ pt = Point(pt, self.dop) if prec is None: prec = _guess_prec(pt) if post_transform is None: post_transform = self.dop.parent().one() derivatives = min(post_transform.order() + 1, self._max_derivatives) post_transform = normalize_post_transform(self.dop, post_transform) if prec >= self.max_prec or not pt.is_real(): logger.info( "performing high-prec evaluation " "(pt=%s, prec=%s, post_transform=%s)", pt, prec, post_transform) ini, path = self._path_to(pt) eps = RBF.one() >> prec return self.dop.numerical_solution(ini, path, eps, post_transform=post_transform) center, rad = self._disk(pt) if center is None: # raise NotImplementedError logger.info("falling back on generic evaluator") ini, path = self._path_to(pt) eps = RBF.one() >> prec return self.dop.numerical_solution(ini, path, eps, post_transform=post_transform) approx = self._polys.get(center, []) Balls = RealBallField(prec) # due to the way the polynomials are recomputed, the precisions attached # to the successive derivatives are nonincreasing if (len(approx) < derivatives or approx[derivatives - 1].prec < prec): polys = self._update_approx(center, rad, prec, derivatives) else: polys = [a.pol for a in approx] bpt = Balls(pt.value) reduced_pt = bpt - Balls(center) val = sum( ZZ(j).factorial() * coeff(bpt) * polys[j](reduced_pt) for j, coeff in enumerate(post_transform)) return val
def _rad(self, center): return RBF.one() << QQ(center).valuation(2)