Exemple #1
0
def jn_zeros(n, k, method="diofant", dps=15):
    """
    Zeros of the spherical Bessel function of the first kind.

    This returns an array of zeros of jn up to the k-th zero.

    * method = "diofant": uses mpmath's function ``besseljzero``
    * method = "scipy": uses the
      `SciPy's sph_jn <http://docs.scipy.org/doc/scipy/reference/generated/scipy.special.jn_zeros.html>`_
      and
      `newton <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_
      to find all
      roots, which is faster than computing the zeros using a general
      numerical solver, but it requires SciPy and only works with low
      precision floating point numbers.  [The function used with
      method="diofant" is a recent addition to mpmath, before that a general
      solver was used.]

    Examples
    ========

    >>> from diofant import jn_zeros
    >>> jn_zeros(2, 4, dps=5)
    [5.7635, 9.095, 12.323, 15.515]

    See Also
    ========

    jn, yn, besselj, besselk, bessely
    """
    from math import pi

    if method == "diofant":
        prec = dps_to_prec(dps)
        return [
            Expr._from_mpmath(
                besseljzero(sympify(n + 0.5)._to_mpmath(prec), int(l)), prec)
            for l in range(1, k + 1)
        ]
    elif method == "scipy":
        from scipy.optimize import newton
        try:
            from scipy.special import spherical_jn
        except ImportError:  # pragma: no cover
            from scipy.special import sph_jn

            def spherical_jn(n, x):
                return sph_jn(n, x)[0][-1]

        def f(x):
            return spherical_jn(n, x)
    else:
        raise NotImplementedError("Unknown method.")

    def solver(f, x):
        if method == "scipy":
            root = newton(f, x)
        else:
            raise NotImplementedError("Unknown method.")
        return root

    # we need to approximate the position of the first root:
    root = n + pi
    # determine the first root exactly:
    root = solver(f, root)
    roots = [root]
    for i in range(k - 1):
        # estimate the position of the next root using the last root + pi:
        root = solver(f, root + pi)
        roots.append(root)
    return roots
Exemple #2
0
    def eval_approx(self, n):
        """Evaluate this complex root to the given precision.

        This uses secant method and root bounds are used to both
        generate an initial guess and to check that the root
        returned is valid. If ever the method converges outside the
        root bounds, the bounds will be made smaller and updated.
        """
        prec = dps_to_prec(n)
        with workprec(prec):
            g = self.poly.gen
            if not g.is_Symbol:
                d = Dummy('x')
                if self.is_imaginary:
                    d *= I
                func = lambdify(d, self.expr.subs(g, d))
            else:
                expr = self.expr
                if self.is_imaginary:
                    expr = self.expr.subs(g, I*g)
                func = lambdify(g, expr)

            interval = self._get_interval()
            while True:
                if self.is_real:
                    a = mpf(str(interval.a))
                    b = mpf(str(interval.b))
                    if a == b:
                        root = a
                        break
                    x0 = mpf(str(interval.center))
                    x1 = x0 + mpf(str(interval.dx))/4
                elif self.is_imaginary:
                    a = mpf(str(interval.ay))
                    b = mpf(str(interval.by))
                    if a == b:
                        root = mpc(mpf('0'), a)
                        break
                    x0 = mpf(str(interval.center[1]))
                    x1 = x0 + mpf(str(interval.dy))/4
                else:
                    ax = mpf(str(interval.ax))
                    bx = mpf(str(interval.bx))
                    ay = mpf(str(interval.ay))
                    by = mpf(str(interval.by))
                    if ax == bx and ay == by:
                        root = mpc(ax, ay)
                        break
                    x0 = mpc(*map(str, interval.center))
                    x1 = x0 + mpc(*map(str, (interval.dx, interval.dy)))/4
                try:
                    # without a tolerance, this will return when (to within
                    # the given precision) x_i == x_{i-1}
                    root = findroot(func, (x0, x1))
                    # If the (real or complex) root is not in the 'interval',
                    # then keep refining the interval. This happens if findroot
                    # accidentally finds a different root outside of this
                    # interval because our initial estimate 'x0' was not close
                    # enough. It is also possible that the secant method will
                    # get trapped by a max/min in the interval; the root
                    # verification by findroot will raise a ValueError in this
                    # case and the interval will then be tightened -- and
                    # eventually the root will be found.
                    #
                    # It is also possible that findroot will not have any
                    # successful iterations to process (in which case it
                    # will fail to initialize a variable that is tested
                    # after the iterations and raise an UnboundLocalError).
                    if self.is_real or self.is_imaginary:
                        if not bool(root.imag) == self.is_real and (
                                a <= root <= b):
                            if self.is_imaginary:
                                root = mpc(mpf('0'), root.real)
                            break
                    elif (ax <= root.real <= bx and ay <= root.imag <= by):
                        break
                except (UnboundLocalError, ValueError):
                    pass
                interval = interval.refine()

        # update the interval so we at least (for this precision or
        # less) don't have much work to do to recompute the root
        self._set_interval(interval)
        return (Float._new(root.real._mpf_, prec) +
            I*Float._new(root.imag._mpf_, prec))
Exemple #3
0
    def eval_approx(self, n):
        """Evaluate this complex root to the given precision.

        This uses secant method and root bounds are used to both
        generate an initial guess and to check that the root
        returned is valid. If ever the method converges outside the
        root bounds, the bounds will be made smaller and updated.
        """
        prec = dps_to_prec(n)
        with workprec(prec):
            g = self.poly.gen
            if not g.is_Symbol:
                d = Dummy('x')
                if self.is_imaginary:
                    d *= I
                func = lambdify(d, self.expr.subs(g, d))
            else:
                expr = self.expr
                if self.is_imaginary:
                    expr = self.expr.subs(g, I * g)
                func = lambdify(g, expr)

            interval = self._get_interval()
            while True:
                if self.is_real:
                    a = mpf(str(interval.a))
                    b = mpf(str(interval.b))
                    if a == b:
                        root = a
                        break
                    x0 = mpf(str(interval.center))
                    x1 = x0 + mpf(str(interval.dx)) / 4
                elif self.is_imaginary:
                    a = mpf(str(interval.ay))
                    b = mpf(str(interval.by))
                    if a == b:
                        root = mpc(mpf('0'), a)
                        break
                    x0 = mpf(str(interval.center[1]))
                    x1 = x0 + mpf(str(interval.dy)) / 4
                else:
                    ax = mpf(str(interval.ax))
                    bx = mpf(str(interval.bx))
                    ay = mpf(str(interval.ay))
                    by = mpf(str(interval.by))
                    if ax == bx and ay == by:
                        root = mpc(ax, ay)
                        break
                    x0 = mpc(*map(str, interval.center))
                    x1 = x0 + mpc(*map(str, (interval.dx, interval.dy))) / 4
                try:
                    # without a tolerance, this will return when (to within
                    # the given precision) x_i == x_{i-1}
                    root = findroot(func, (x0, x1))
                    # If the (real or complex) root is not in the 'interval',
                    # then keep refining the interval. This happens if findroot
                    # accidentally finds a different root outside of this
                    # interval because our initial estimate 'x0' was not close
                    # enough. It is also possible that the secant method will
                    # get trapped by a max/min in the interval; the root
                    # verification by findroot will raise a ValueError in this
                    # case and the interval will then be tightened -- and
                    # eventually the root will be found.
                    #
                    # It is also possible that findroot will not have any
                    # successful iterations to process (in which case it
                    # will fail to initialize a variable that is tested
                    # after the iterations and raise an UnboundLocalError).
                    if self.is_real or self.is_imaginary:
                        if not bool(root.imag) == self.is_real and (a <= root
                                                                    <= b):
                            if self.is_imaginary:
                                root = mpc(mpf('0'), root.real)
                            break
                    elif (ax <= root.real <= bx and ay <= root.imag <= by):
                        break
                except (UnboundLocalError, ValueError):
                    pass
                interval = interval.refine()

        # update the interval so we at least (for this precision or
        # less) don't have much work to do to recompute the root
        self._set_interval(interval)
        return (Float._new(root.real._mpf_, prec) +
                I * Float._new(root.imag._mpf_, prec))
Exemple #4
0
    def evalf(self,
              n=15,
              subs=None,
              maxn=100,
              chop=False,
              strict=False,
              quad=None,
              verbose=False):
        """
        Evaluate the given formula to an accuracy of n digits.
        Optional keyword arguments:

            subs=<dict>
                Substitute numerical values for symbols, e.g.
                subs={x:3, y:1+pi}. The substitutions must be given as a
                dictionary.

            maxn=<integer>
                Allow a maximum temporary working precision of maxn digits
                (default=100)

            chop=<bool>
                Replace tiny real or imaginary parts in subresults
                by exact zeros (default=False)

            strict=<bool>
                Raise PrecisionExhausted if any subresult fails to evaluate
                to full accuracy, given the available maxprec
                (default=False)

            quad=<str>
                Choose algorithm for numerical quadrature. By default,
                tanh-sinh quadrature is used. For oscillatory
                integrals on an infinite interval, try quad='osc'.

            verbose=<bool>
                Print debug information (default=False)

        """
        from sympy import Float, Number
        n = n if n is not None else 15

        if subs and is_sequence(subs):
            raise TypeError('subs must be given as a dictionary')

        # for sake of sage that doesn't like evalf(1)
        if n == 1 and isinstance(self, Number):
            from sympy.core.expr import _mag
            rv = self.evalf(2, subs, maxn, chop, strict, quad, verbose)
            m = _mag(rv)
            rv = rv.round(1 - m)
            return rv

        if not evalf_table:
            _create_evalf_table()
        prec = dps_to_prec(n)
        options = {
            'maxprec': max(prec, int(maxn * LG10)),
            'chop': chop,
            'strict': strict,
            'verbose': verbose
        }
        if subs is not None:
            options['subs'] = subs
        if quad is not None:
            options['quad'] = quad
        try:
            result = evalf(self, prec + 4, options)
        except NotImplementedError:
            # Fall back to the ordinary evalf
            v = self._eval_evalf(prec)
            if v is None:
                return self
            try:
                # If the result is numerical, normalize it
                result = evalf(v, prec, options)
            except NotImplementedError:
                # Probably contains symbols or unknown functions
                return v
        re, im, re_acc, im_acc = result
        if re:
            p = max(min(prec, re_acc), 1)
            re = Float._new(re, p)
        else:
            re = S.Zero
        if im:
            p = max(min(prec, im_acc), 1)
            im = Float._new(im, p)
            return re + im * S.ImaginaryUnit
        else:
            return re
Exemple #5
0
def jn_zeros(n, k, method="sympy", dps=15):
    """
    Zeros of the spherical Bessel function of the first kind.

    This returns an array of zeros of jn up to the k-th zero.

    * method = "sympy": uses :func:`mpmath.besseljzero`
    * method = "scipy": uses the
      `SciPy's sph_jn <http://docs.scipy.org/doc/scipy/reference/generated/scipy.special.jn_zeros.html>`_
      and
      `newton <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_
      to find all
      roots, which is faster than computing the zeros using a general
      numerical solver, but it requires SciPy and only works with low
      precision floating point numbers.  [The function used with
      method="sympy" is a recent addition to mpmath, before that a general
      solver was used.]

    Examples
    ========

    >>> from sympy import jn_zeros
    >>> jn_zeros(2, 4, dps=5)
    [5.7635, 9.095, 12.323, 15.515]

    See Also
    ========

    jn, yn, besselj, besselk, bessely
    """
    from math import pi

    if method == "sympy":
        from mpmath import besseljzero
        from mpmath.libmp.libmpf import dps_to_prec
        from sympy import Expr

        prec = dps_to_prec(dps)
        return [Expr._from_mpmath(besseljzero(S(n + 0.5)._to_mpmath(prec), int(l)), prec) for l in range(1, k + 1)]
    elif method == "scipy":
        from scipy.special import sph_jn
        from scipy.optimize import newton

        f = lambda x: sph_jn(n, x)[0][-1]
    else:
        raise NotImplementedError("Unknown method.")

    def solver(f, x):
        if method == "scipy":
            root = newton(f, x)
        else:
            raise NotImplementedError("Unknown method.")
        return root

    # we need to approximate the position of the first root:
    root = n + pi
    # determine the first root exactly:
    root = solver(f, root)
    roots = [root]
    for i in range(k - 1):
        # estimate the position of the next root using the last root + pi:
        root = solver(f, root + pi)
        roots.append(root)
    return roots
Exemple #6
0
    def evalf(self,
              dps=15,
              subs=None,
              maxn=110,
              chop=False,
              strict=True,
              quad=None):
        """
        Evaluate the given formula to an accuracy of dps decimal digits.
        Optional keyword arguments:

            subs=<dict>
                Substitute numerical values for symbols, e.g.
                subs={x:3, y:1+pi}. The substitutions must be given as a
                dictionary.

            maxn=<integer>
                Allow a maximum temporary working precision of maxn digits
                (default=110)

            chop=<bool>
                Replace tiny real or imaginary parts in subresults
                by exact zeros (default=False)

            strict=<bool>
                Raise PrecisionExhausted if any subresult fails to evaluate
                to full accuracy, given the available maxprec
                (default=True)

            quad=<str>
                Choose algorithm for numerical quadrature. By default,
                tanh-sinh quadrature is used. For oscillatory
                integrals on an infinite interval, try quad='osc'.

        """
        from .numbers import Float, I, Integer

        if subs and is_sequence(subs):
            raise TypeError('subs must be given as a dictionary')

        if not evalf_table:
            _create_evalf_table()
        prec = dps_to_prec(dps)
        options = {
            'maxprec': max(prec, int(maxn * LG10)),
            'chop': chop,
            'strict': strict
        }
        if subs is not None:
            options['subs'] = subs
        if quad is not None:
            options['quad'] = quad
        try:
            result = evalf(self, prec + 4, options)
        except PrecisionExhausted:
            if self.is_Float and self._prec >= prec:
                return Float._new(self._mpf_, prec)
            else:
                raise
        except NotImplementedError:
            # Fall back to the ordinary evalf
            v = self._eval_evalf(prec)  # pylint: disable=assignment-from-none
            if v is None:
                return self
            else:
                # Normalize result
                return v.subs(
                    {_: _.evalf(dps, strict=strict)
                     for _ in v.atoms(Float)})
        re, im, re_acc, im_acc = result
        if re:
            p = max(min(prec, re_acc), 1)
            re = Float._new(re, p)
        else:
            re = Integer(0)
        if im:
            p = max(min(prec, im_acc), 1)
            im = Float._new(im, p)
            return re + im * I
        else:
            return re
Exemple #7
0
    def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False):
        """
        Evaluate the given formula to an accuracy of n digits.
        Optional keyword arguments:

            subs=<dict>
                Substitute numerical values for symbols, e.g.
                subs={x:3, y:1+pi}. The substitutions must be given as a
                dictionary.

            maxn=<integer>
                Allow a maximum temporary working precision of maxn digits
                (default=100)

            chop=<bool>
                Replace tiny real or imaginary parts in subresults
                by exact zeros (default=False)

            strict=<bool>
                Raise PrecisionExhausted if any subresult fails to evaluate
                to full accuracy, given the available maxprec
                (default=False)

            quad=<str>
                Choose algorithm for numerical quadrature. By default,
                tanh-sinh quadrature is used. For oscillatory
                integrals on an infinite interval, try quad='osc'.

            verbose=<bool>
                Print debug information (default=False)

        """
        from sympy import Float, Number
        n = n if n is not None else 15

        if subs and is_sequence(subs):
            raise TypeError('subs must be given as a dictionary')

        # for sake of sage that doesn't like evalf(1)
        if n == 1 and isinstance(self, Number):
            from sympy.core.expr import _mag
            rv = self.evalf(2, subs, maxn, chop, strict, quad, verbose)
            m = _mag(rv)
            rv = rv.round(1 - m)
            return rv

        if not evalf_table:
            _create_evalf_table()
        prec = dps_to_prec(n)
        options = {'maxprec': max(prec, int(maxn*LG10)), 'chop': chop,
               'strict': strict, 'verbose': verbose}
        if subs is not None:
            options['subs'] = subs
        if quad is not None:
            options['quad'] = quad
        try:
            result = evalf(self, prec + 4, options)
        except NotImplementedError:
            # Fall back to the ordinary evalf
            v = self._eval_evalf(prec)
            if v is None:
                return self
            try:
                # If the result is numerical, normalize it
                result = evalf(v, prec, options)
            except NotImplementedError:
                # Probably contains symbols or unknown functions
                return v
        re, im, re_acc, im_acc = result
        if re:
            p = max(min(prec, re_acc), 1)
            re = Float._new(re, p)
        else:
            re = S.Zero
        if im:
            p = max(min(prec, im_acc), 1)
            im = Float._new(im, p)
            return re + im*S.ImaginaryUnit
        else:
            return re
Exemple #8
0
    def evalf(self, dps=15, subs=None, maxn=110, chop=False, strict=True, quad=None):
        """
        Evaluate the given formula to an accuracy of dps decimal digits.
        Optional keyword arguments:

            subs=<dict>
                Substitute numerical values for symbols, e.g.
                subs={x:3, y:1+pi}. The substitutions must be given as a
                dictionary.

            maxn=<integer>
                Allow a maximum temporary working precision of maxn digits
                (default=110)

            chop=<bool>
                Replace tiny real or imaginary parts in subresults
                by exact zeros (default=False)

            strict=<bool>
                Raise PrecisionExhausted if any subresult fails to evaluate
                to full accuracy, given the available maxprec
                (default=True)

            quad=<str>
                Choose algorithm for numerical quadrature. By default,
                tanh-sinh quadrature is used. For oscillatory
                integrals on an infinite interval, try quad='osc'.

        """
        from .numbers import Float, I

        if subs and is_sequence(subs):
            raise TypeError('subs must be given as a dictionary')

        if not evalf_table:
            _create_evalf_table()
        prec = dps_to_prec(dps)
        options = {'maxprec': max(prec, int(maxn*LG10)), 'chop': chop,
                   'strict': strict}
        if subs is not None:
            options['subs'] = subs
        if quad is not None:
            options['quad'] = quad
        try:
            result = evalf(self, prec + 4, options)
        except PrecisionExhausted:
            if self.is_Float and self._prec >= prec:
                return Float._new(self._mpf_, prec)
            else:
                raise
        except NotImplementedError:
            # Fall back to the ordinary evalf
            v = self._eval_evalf(prec)
            if v is None:
                return self
            else:
                # Normalize result
                return v.subs({_: _.evalf(dps, strict=strict)
                               for _ in v.atoms(Float)})
        re, im, re_acc, im_acc = result
        if re:
            p = max(min(prec, re_acc), 1)
            re = Float._new(re, p)
        else:
            re = S.Zero
        if im:
            p = max(min(prec, im_acc), 1)
            im = Float._new(im, p)
            return re + im*I
        else:
            return re
Exemple #9
0
def jn_zeros(n, k, method="diofant", dps=15):
    """
    Zeros of the spherical Bessel function of the first kind.

    This returns an array of zeros of jn up to the k-th zero.

    * method = "diofant": uses mpmath's function ``besseljzero``
    * method = "scipy": uses :func:`scipy.special.jn_zeros`.
      and :func:`scipy.optimize.newton` to find all
      roots, which is faster than computing the zeros using a general
      numerical solver, but it requires SciPy and only works with low
      precision floating point numbers.  [The function used with
      method="diofant" is a recent addition to mpmath, before that a general
      solver was used.]

    Examples
    ========

    >>> jn_zeros(2, 4, dps=5)
    [5.7635, 9.095, 12.323, 15.515]

    See Also
    ========

    jn, yn, besselj, besselk, bessely

    """
    from math import pi

    if method == "diofant":
        prec = dps_to_prec(dps)
        return [Expr._from_mpmath(besseljzero(sympify(n + 0.5)._to_mpmath(prec),
                                              int(l)), prec)
                for l in range(1, k + 1)]
    elif method == "scipy":
        from scipy.optimize import newton
        try:
            from scipy.special import spherical_jn
        except ImportError:  # pragma: no cover
            from scipy.special import sph_jn

            def spherical_jn(n, x):
                return sph_jn(n, x)[0][-1]

        def f(x):
            return spherical_jn(n, x)
    else:
        raise NotImplementedError("Unknown method.")

    def solver(f, x):
        if method == "scipy":
            root = newton(f, x)
        else:
            raise NotImplementedError("Unknown method.")
        return root

    # we need to approximate the position of the first root:
    root = n + pi
    # determine the first root exactly:
    root = solver(f, root)
    roots = [root]
    for i in range(k - 1):
        # estimate the position of the next root using the last root + pi:
        root = solver(f, root + pi)
        roots.append(root)
    return roots
Exemple #10
0
class Printer9000(PrettyPrinter):
    _float_cutoff = dps_to_prec(refs.ExtraPrecision) - 4

    def _print_Float(self, e):
        # TODO: fix float printing in List
        e = s.Float(e, precision=max(e._prec - self._float_cutoff, 1))
        full_prec = self._settings["full_prec"]
        if full_prec == "auto":
            full_prec = self._print_level == 1
        return prettyForm(sstr(e, full_prec=full_prec))

    def _print_Range(self, e):
        if isinstance(e, s.Range):
            return super()._print_Range(e)
        return super()._print_Function(e)

    def _print_Dot(self, e):
        if isinstance(e, Dot):
            return self._print_seq(
                e.args,
                None,
                None,
                ".",
                parenthesize=lambda x: precedence_traditional(x) <= PRECEDENCE["Mul"],
            )
        return super()._print_Dot(e)

    def _print_Cross(self, e):
        if isinstance(e, Cross):
            return self._print_seq(
                e.args,
                None,
                None,
                "×",
                parenthesize=lambda x: precedence_traditional(x) <= PRECEDENCE["Mul"],
            )
        return super()._print_Cross(e)

    # def _print_Rule(self, e):
    #     TODO: Proper rule printing
    #     return self._print_Implies(List(
    #         self._print_seq(e.lhs),
    #         self._print_seq(e.rhs)
    #     ), altchar='->')

    def _print_Limit(self, lim):
        if isinstance(lim, Limit):
            return super()._print_Function(lim)
        return super()._print_Limit(lim)

    @staticmethod
    def pretty_int(x):
        x = str(x)
        i = len(x) % 3

        for digit in x:
            if i > 0:
                yield digit
                i -= 1
            else:
                i = 2
                yield " "
                yield digit

    # def _print_Integer(self, x):
    #     pretty_int = self.pretty_int(x)
    #     final_str = ''.join(*pretty_int).strip()
    #     prettyForm(final_str)

    def _print_List(self, e):
        # for better performance
        if len(e) > 10:
            avg_len = (
                sum([len(pretty_print(e[x])) for x in range(0, len(e), len(e) // 10)])
                / 10
            )
            avg_total_len = (avg_len + 2) * len(e) + 2
            if avg_total_len > 1000:
                m = int(75 / avg_len)
                return self._print_seq(
                    e.value[:m] + [ListSkip(len(e.value) - 2 * m)] + e.value[-m:],
                    "{",
                    "}",
                )
        return self._print_seq(e.value, "{", "}")

    def _print_Mod(self, expr):
        if len(expr.args) > 2:
            return self._print_Function(expr)
        return super()._print_Mod(expr)

    def _print_Plus(self, expr):
        return super()._print_Add(s.Add(*expr.args, evaluate=False))

    def _print_Power(self, expr):
        return super()._print_Pow(s.Pow(*expr.args, evaluate=False))

    def _print_Times(self, expr):
        return super()._print_Mul(s.Mul(*expr.args, evaluate=False))

    def _print_Max(self, expr):
        return self._print_Function(expr)

    def _print_Min(self, expr):
        return self._print_Function(expr)

    @staticmethod
    def _print_ComplexInfinity(*args):
        return prettyForm("ComplexInfinity")

    def _helper_print_function(
        self,
        func,
        args,
        sort=False,
        func_name=None,
        delimiter=", ",
        elementwise=False,
    ):
        if sort:
            args = sorted(args, key=s.utilities.default_sort_key)

        if not func_name and hasattr(func, "__name__"):
            func_name = func.__name__

        if func_name:
            if func_name in FunctionWrappersReverse and not issubclass(
                func, DefinedFunction
            ):
                func_name = FunctionWrappersReverse[func_name]
            prettyFunc = self._print(s.Symbol(func_name))
        else:
            prettyFunc = prettyForm(*self._print(func).parens(left="[", right="]"))

        if elementwise:
            if self._use_unicode:
                circ = pretty_atom("Modifier Letter Low Ring")
            else:
                circ = "."
            circ = self._print(circ)
            prettyFunc = prettyForm(
                binding=prettyForm.LINE, *stringPict.next(prettyFunc, circ)
            )

        prettyArgs = prettyForm(
            *self._print_seq(args, delimiter=delimiter).parens(left="[", right="]")
        )

        pform = prettyForm(
            binding=prettyForm.FUNC, *stringPict.next(prettyFunc, prettyArgs)
        )

        # store pform parts so it can be reassembled e.g. when powered
        pform.prettyFunc = prettyFunc
        pform.prettyArgs = prettyArgs

        return pform