Exemplo n.º 1
0
    def _evalf_(self, x, parent=None, algorithm=None):
        """
        EXAMPLES::

            sage: arcsec(2).n(100)
            1.0471975511965977461542144611
            sage: arcsec(1/2).n(100)
            NaN

        TESTS:

        Test complex input::

            sage: arcsec(complex(1,1))  # rel tol 1e-15
            (1.118517879643706+0.5306375309525178j)

        """
        if parent is float:
            return math.acos(1 / x)

        try:
            return (1 / x).arccos()
        except AttributeError:
            # Usually this means that x is of type 'complex'
            from sage.rings.complex_double import CDF
            return complex(CDF(1 / x).arccos())
Exemplo n.º 2
0
    def _evalf_(self, x, parent=None, algorithm=None):
        """
        EXAMPLES::

            sage: arccsc(2).n(100)
            0.52359877559829887307710723055
            sage: float(arccsc(2))
            0.52359877559829...

        TESTS:

        Test complex input::

            sage: arccsc(complex(1,1))  # rel tol 1e-15
            (0.45227844715119064-0.5306375309525178j)

        """
        if parent is float:
            return math.asin(1 / x)

        try:
            return (1 / x).arcsin()
        except AttributeError:
            # Usually this means that x is of type 'complex'
            from sage.rings.complex_double import CDF
            return complex(CDF(1 / x).arcsin())
Exemplo n.º 3
0
def real(x):
    """
    Return the real part of x.
    
    EXAMPLES::
    
        sage: z = 1+2*I
        sage: real(z)
        1
        sage: real(5/3)
        5/3
        sage: a = 2.5
        sage: real(a)
        2.50000000000000
        sage: type(real(a))
        <type 'sage.rings.real_mpfr.RealLiteral'>
    """

    #Try to all the .real() method
    try:
        return x.real()
    except AttributeError:
        pass

    #Try to coerce x into RDF.  If that
    #succeeds, then we can just return x
    try:
        rdf_x = RDF(x)
        return x
    except TypeError:
        pass

    #Finall try to coerce x into CDF and call
    #the .real() method.
    return CDF(x).real()
Exemplo n.º 4
0
    def _evalf_(self, x, parent=None, algorithm=None):
        """
        EXAMPLES::

            sage: arccot(1/2).n(100)
            1.1071487177940905030170654602
            sage: float(arccot(1/2))
            1.1071487177940904

        TESTS:

        Test complex input::

            sage: arccot(complex(1,1))  # rel tol 1e-15
            (0.5535743588970452-0.4023594781085251j)

        """
        if parent is float:
            return math.pi / 2 - math.atan(x)

        from sage.symbolic.constants import pi
        try:
            return parent(pi / 2 - x.arctan())
        except AttributeError:
            # Usually this means that x is of type 'complex'
            from sage.rings.complex_double import CDF
            return complex(pi / 2 - CDF(x).arctan())
Exemplo n.º 5
0
def sqrt(x):
    """
    Returns a square root of x.

    This function (``numerical_sqrt``) is deprecated.  Use ``sqrt(x,
    prec=n)`` instead.

    EXAMPLES::

        sage: numerical_sqrt(10.1)
        doctest:1: DeprecationWarning: numerical_sqrt is deprecated, use sqrt(x, prec=n) instead
        See http://trac.sagemath.org/5404 for details.
        3.17804971641414
        sage: numerical_sqrt(9)
        3
    """
    from sage.misc.superseded import deprecation
    deprecation(5404,
                "numerical_sqrt is deprecated, use sqrt(x, prec=n) instead")
    try:
        return x.sqrt()
    except (AttributeError, ValueError):
        try:
            return RDF(x).sqrt()
        except TypeError:
            return CDF(x).sqrt()
Exemplo n.º 6
0
def imag(x):
    """
    Return the imaginary part of x.
    """
    try:
        return x.imag()
    except AttributeError:
        return CDF(x).imag()
Exemplo n.º 7
0
def arg(x):
    """
    Return the argument of a complex number `x`.
    
    EXAMPLES::
    
        sage: z = CC(1,2)
        sage: theta = arg(z)
        sage: cos(theta)*abs(z)   
        1.00000000000000
        sage: sin(theta)*abs(z)
        2.00000000000000
    """
    try:
        return x.arg()
    except AttributeError:
        return CDF(x).arg()
Exemplo n.º 8
0
def sqrt(x):
    """
    Return a square root of x.
    
    EXAMPLES::
    
        sage: sqrt(10.1)
        3.17804971641414
        sage: sqrt(9)
        3
    """
    try:
        return x.sqrt()
    except (AttributeError, ValueError):
        try:
            return RDF(x).sqrt()
        except TypeError:
            return CDF(x).sqrt()
Exemplo n.º 9
0
def eta(x):
    r"""
    Returns the value of the eta function at `x`, which must be
    in the upper half plane.

    The `\eta` function is

    .. MATH::

                    \eta(z) = e^{\pi i z / 12} \prod_{n=1}^{\infty}(1-e^{2\pi inz})

    EXAMPLES::

        sage: eta(1+I)
        0.7420487758365647 + 0.1988313702299107*I
    """
    try: return x.eta()
    except AttributeError: return CDF(x).eta()
Exemplo n.º 10
0
Arquivo: log.py Projeto: yarv/sage
    def _evalf_(self, n, z, parent=None, algorithm=None):
        """
        EXAMPLES::

            sage: N(lambert_w(1))
            0.567143290409784
            sage: lambert_w(RealField(100)(1))
            0.56714329040978387299996866221

        SciPy is used to evaluate for float, RDF, and CDF inputs::

            sage: lambert_w(RDF(1))
            0.5671432904097838
            sage: lambert_w(float(1))
            0.5671432904097838
            sage: lambert_w(CDF(1))
            0.5671432904097838
            sage: lambert_w(complex(1))
            (0.5671432904097838+0j)
            sage: lambert_w(RDF(-1))  # abs tol 2e-16
            -0.31813150520476413 + 1.3372357014306895*I
            sage: lambert_w(float(-1))  # abs tol 2e-16
            (-0.31813150520476413+1.3372357014306895j)
        """
        R = parent or s_parent(z)
        if R is float or R is RDF:
            from scipy.special import lambertw
            res = lambertw(z, n)
            # SciPy always returns a complex value, make it real if possible
            if not res.imag:
                return R(res.real)
            elif R is float:
                return complex(res)
            else:
                return CDF(res)
        elif R is complex or R is CDF:
            from scipy.special import lambertw
            return R(lambertw(z, n))
        else:
            import mpmath
            return mpmath_utils.call(mpmath.lambertw, z, n, parent=R)
Exemplo n.º 11
0
def sqrt(x):
    """
    Returns a square root of x.

    This function (``numerical_sqrt``) is deprecated.  Use ``sqrt(x,
    prec=n)`` instead.
    
    EXAMPLES::
    
        sage: numerical_sqrt(10.1)
        doctest:1: DeprecationWarning: numerical_sqrt is deprecated, use sqrt(x, prec=n) instead
        3.17804971641414
        sage: numerical_sqrt(9)
        3
    """
    from sage.misc.misc import deprecation
    deprecation("numerical_sqrt is deprecated, use sqrt(x, prec=n) instead")
    try:
        return x.sqrt()
    except (AttributeError, ValueError):
        try:
            return RDF(x).sqrt()
        except TypeError:
            return CDF(x).sqrt()
Exemplo n.º 12
0
    def __init__(self,
                 x,
                 universe=None,
                 check=True,
                 immutable=False,
                 cr=False,
                 cr_str=None,
                 use_sage_types=False):
        """
        Create a sequence.

        EXAMPLES::

            sage: Sequence([1..5])
            [1, 2, 3, 4, 5]
            sage: a = Sequence([1..3], universe=QQ, check=False, immutable=True, cr=True, cr_str=False, use_sage_types=True)
            sage: a
            [
            1,
            2,
            3
            ]
            sage: a = Sequence([1..5], universe=QQ, check=False, immutable=True, cr_str=True, use_sage_types=True)
            sage: a
            [1, 2, 3, 4, 5]
            sage: a._Sequence__cr_str
            True
            sage: a.__str__()
            '[\n1,\n2,\n3,\n4,\n5\n]'
        """
        if not isinstance(x, (list, tuple)):
            x = list(x)
            #raise TypeError, "x must be a list or tuple"
        self.__hash = None

        self.__cr = cr
        if cr_str is None:
            self.__cr_str = cr
        else:
            self.__cr_str = cr_str

        if isinstance(x, Sequence):
            if universe is None or universe == x.__universe:
                list.__init__(self, x)
                self.__universe = x.__universe
                self._is_immutable = immutable
                return
        if universe is None:
            if len(x) == 0:
                import sage.categories.all
                universe = sage.categories.all.Objects()
            else:
                import sage.structure.element as coerce
                y = x
                x = list(
                    x
                )  # make a copy, or we'd change the type of the elements of x, which would be bad.
                if use_sage_types:
                    # convert any Python builtin numerical types to Sage objects
                    from sage.rings.integer_ring import ZZ
                    from sage.rings.real_double import RDF
                    from sage.rings.complex_double import CDF
                    for i in range(len(x)):
                        if isinstance(x[i], int) or isinstance(x[i], long):
                            x[i] = ZZ(x[i])
                        elif isinstance(x[i], float):
                            x[i] = RDF(x[i])
                        elif isinstance(x[i], complex):
                            x[i] = CDF(x[i])
                # start the pairwise coercion
                for i in range(len(x) - 1):
                    try:
                        x[i], x[i + 1] = coerce.canonical_coercion(
                            x[i], x[i + 1])
                    except TypeError:
                        import sage.categories.all
                        universe = sage.categories.all.Objects()
                        x = list(y)
                        check = False  # no point
                        break
                if universe is None:  # no type errors raised.
                    universe = coerce.parent(x[len(x) - 1])
                #universe = sage.structure.coerce.parent(x[0])
        self.__universe = universe
        if check:
            x = [universe(t) for t in x]
        list.__init__(self, x)
        self._is_immutable = immutable
Exemplo n.º 13
0
def numerical_approx(x, prec=None, digits=None, algorithm=None):
    r"""
    Returns a numerical approximation of an object ``x`` with at
    least ``prec`` bits (or decimal ``digits``) of precision.

    .. note::

       Both upper case ``N`` and lower case ``n`` are aliases for
       :func:`numerical_approx`, and all three may be used as
       methods.

    INPUT:

    -  ``x`` - an object that has a numerical_approx
       method, or can be coerced into a real or complex field
    -  ``prec`` (optional) - an integer (bits of
       precision)
    -  ``digits`` (optional) - an integer (digits of
       precision)
    -  ``algorithm`` (optional) - a string specifying
       the algorithm to use for functions that implement
       more than one

    If neither the ``prec`` or ``digits`` are specified,
    the default is 53 bits of precision.  If both are
    specified, then ``prec`` is used.

    EXAMPLES::

        sage: numerical_approx(pi, 10)
        3.1
        sage: numerical_approx(pi, digits=10)
        3.141592654
        sage: numerical_approx(pi^2 + e, digits=20)
        12.587886229548403854
        sage: n(pi^2 + e)
        12.5878862295484
        sage: N(pi^2 + e)
        12.5878862295484
        sage: n(pi^2 + e, digits=50)
        12.587886229548403854194778471228813633070946500941
        sage: a = CC(-5).n(prec=40)
        sage: b = ComplexField(40)(-5)
        sage: a == b
        True
        sage: parent(a) is parent(b)
        True
        sage: numerical_approx(9)
        9.00000000000000

    You can also usually use method notation.  ::

        sage: (pi^2 + e).n()
        12.5878862295484
        sage: (pi^2 + e).N()
        12.5878862295484
        sage: (pi^2 + e).numerical_approx()
        12.5878862295484

    Vectors and matrices may also have their entries approximated.  ::

        sage: v = vector(RDF, [1,2,3])
        sage: v.n()
        (1.00000000000000, 2.00000000000000, 3.00000000000000)

        sage: v = vector(CDF, [1,2,3])
        sage: v.n()
        (1.00000000000000, 2.00000000000000, 3.00000000000000)
        sage: _.parent()
        Vector space of dimension 3 over Complex Field with 53 bits of precision
        sage: v.n(prec=20)
        (1.0000, 2.0000, 3.0000)

        sage: u = vector(QQ, [1/2, 1/3, 1/4])
        sage: n(u, prec=15)
        (0.5000, 0.3333, 0.2500)
        sage: n(u, digits=5)
        (0.50000, 0.33333, 0.25000)

        sage: v = vector(QQ, [1/2, 0, 0, 1/3, 0, 0, 0, 1/4], sparse=True)
        sage: u = v.numerical_approx(digits=4)
        sage: u.is_sparse()
        True
        sage: u
        (0.5000, 0.0000, 0.0000, 0.3333, 0.0000, 0.0000, 0.0000, 0.2500)

        sage: A = matrix(QQ, 2, 3, range(6))
        sage: A.n()
        [0.000000000000000  1.00000000000000  2.00000000000000]
        [ 3.00000000000000  4.00000000000000  5.00000000000000]

        sage: B = matrix(Integers(12), 3, 8, srange(24))
        sage: N(B, digits=2)
        [0.00  1.0  2.0  3.0  4.0  5.0  6.0  7.0]
        [ 8.0  9.0  10.  11. 0.00  1.0  2.0  3.0]
        [ 4.0  5.0  6.0  7.0  8.0  9.0  10.  11.]

    Internally, numerical approximations of real numbers are stored in base-2.
    Therefore, numbers which look the same in their decimal expansion might be
    different::

        sage: x=N(pi, digits=3); x
        3.14
        sage: y=N(3.14, digits=3); y
        3.14
        sage: x==y
        False
        sage: x.str(base=2)
        '11.001001000100'
        sage: y.str(base=2)
        '11.001000111101'

    Increasing the precision of a floating point number is not allowed::

        sage: CC(-5).n(prec=100)
        Traceback (most recent call last):
        ...
        TypeError: cannot approximate to a precision of 100 bits, use at most 53 bits
        sage: n(1.3r, digits=20)
        Traceback (most recent call last):
        ...
        TypeError: cannot approximate to a precision of 70 bits, use at most 53 bits
        sage: RealField(24).pi().n()
        Traceback (most recent call last):
        ...
        TypeError: cannot approximate to a precision of 53 bits, use at most 24 bits

    As an exceptional case, ``digits=1`` usually leads to 2 digits (one
    significant) in the decimal output (see :trac:`11647`)::

        sage: N(pi, digits=1)
        3.2
        sage: N(pi, digits=2)
        3.1
        sage: N(100*pi, digits=1)
        320.
        sage: N(100*pi, digits=2)
        310.

    In the following example, ``pi`` and ``3`` are both approximated to two
    bits of precision and then subtracted, which kills two bits of precision::

        sage: N(pi, prec=2)
        3.0
        sage: N(3, prec=2)
        3.0
        sage: N(pi - 3, prec=2)
        0.00

    TESTS::

        sage: numerical_approx(I)
        1.00000000000000*I
        sage: x = QQ['x'].gen()
        sage: F.<k> = NumberField(x^2+2, embedding=sqrt(CC(2))*CC.0)
        sage: numerical_approx(k)
        1.41421356237309*I

        sage: type(numerical_approx(CC(1/2)))
        <type 'sage.rings.complex_number.ComplexNumber'>

    The following tests :trac:`10761`, in which ``n()`` would break when
    called on complex-valued algebraic numbers.  ::

        sage: E = matrix(3, [3,1,6,5,2,9,7,3,13]).eigenvalues(); E
        [18.16815365088822?, -0.08407682544410650? - 0.2190261484802906?*I, -0.08407682544410650? + 0.2190261484802906?*I]
        sage: E[1].parent()
        Algebraic Field
        sage: [a.n() for a in E]
        [18.1681536508882, -0.0840768254441065 - 0.219026148480291*I, -0.0840768254441065 + 0.219026148480291*I]

    Make sure we've rounded up log(10,2) enough to guarantee
    sufficient precision (:trac:`10164`)::

        sage: ks = 4*10**5, 10**6
        sage: check_str_length = lambda k: len(str(numerical_approx(1+10**-k,digits=k+1)))-1 >= k+1
        sage: check_precision = lambda k: numerical_approx(1+10**-k,digits=k+1)-1 > 0
        sage: all(check_str_length(k) and check_precision(k) for k in ks)
        True

    Testing we have sufficient precision for the golden ratio (:trac:`12163`), note
    that the decimal point adds 1 to the string length::

        sage: len(str(n(golden_ratio, digits=5000)))
        5001
        sage: len(str(n(golden_ratio, digits=5000000)))  # long time (4s on sage.math, 2012)
        5000001

    Check that :trac:`14778` is fixed::

        sage: n(0, algorithm='foo')
        0.000000000000000
    """
    if prec is None:
        if digits is None:
            prec = 53
        else:
            prec = int((digits+1) * LOG_TEN_TWO_PLUS_EPSILON) + 1
    try:
        return x._numerical_approx(prec, algorithm=algorithm)
    except AttributeError:
        pass

    from sage.structure.element import parent
    B = parent(x)

    RR = sage.rings.real_mpfr.RealField(prec)
    map = RR.coerce_map_from(B)
    if map is not None:
        return map(x)
    CC = sage.rings.complex_field.ComplexField(prec)
    map = CC.coerce_map_from(B)
    if map is not None:
        return map(x)

    # Coercion didn't work: there are 3 possibilities:
    # (1) There is a coercion possible to a lower precision
    # (2) There is a conversion but no coercion
    # (3) The type doesn't convert at all

    # Figure out input precision to check for case (1)
    try:
        inprec = x.prec()
    except AttributeError:
        if prec > 53 and CDF.has_coerce_map_from(B):
            # If we can coerce to CDF, assume input precision was 53 bits
            inprec = 53
        else:
            # Otherwise, assume precision wasn't the issue
            inprec = prec

    if prec > inprec:
        raise TypeError("cannot approximate to a precision of %s bits, use at most %s bits" % (prec, inprec))

    # The issue is not precision, try conversion instead
    try:
        return RR(x)
    except (TypeError, ValueError):
        pass
    return CC(x)
Exemplo n.º 14
0
def Zinf(a, l, m, r, algorithm='spline'):
    r"""
    Amplitude factor of the mode `(\ell,m)`.

    The factor `Z^\infty_{\ell m}(r)` is obtained by spline interpolation
    of tabulated numerical solutions of the radial component of the
    Teukolsky equation.

    INPUT:

    - ``a`` -- BH angular momentum parameter (in units of `M`, the BH mass)
    - ``l`` -- integer >= 2; the harmonic degree `\ell`
    - ``m`` -- integer within the range ``[-l, l]``; the azimuthal number `m`
    - ``r`` -- areal radius of the orbit (in units of `M`)
    - ``algorithm`` -- (default: ``'spline'``) string describing the
      computational method; allowed values are

      - ``'spline'``: spline interpolation of tabulated data
      - ``'1.5PN'`` (only for ``a=0``): 1.5-post-Newtonian expansion following
        E. Poisson, Phys. Rev. D **47**, 1497 (1993)
        [:doi:`10.1103/PhysRevD.47.1497`], with a minus one factor
        accounting for a different convention for the metric signature.

    OUTPUT:

    - coefficient `Z^\infty_{\ell m}(r)` (in units of `M^{-2}`)

    EXAMPLES::

        sage: from kerrgeodesic_gw import Zinf
        sage: Zinf(0.98, 2, 2, 1.7)  # tol 1.0e-13
        -0.04302234478778856 + 0.28535368610053824*I
        sage: Zinf(0., 2, 2, 10.)  # tol 1.0e-13
        0.0011206407919254163 - 0.0003057608384581628*I
        sage: Zinf(0., 2, 2, 10., algorithm='1.5PN')  # tol 1.0e-13
        0.0011971529546749354 - 0.0003551610880408921*I

    """
    if m < 0:
        return (-1)**l * Zinf(a, l, -m, r, algorithm=algorithm).conjugate()
    if algorithm == '1.5PN':
        if a == 0:
            # the factor (-1) below accounts for a difference of signature with
            # Poisson (1993):
            return -Zinf_Schwarzchild_PN(l, m, r)
        raise ValueError("a must be zero for algorithm='1.5PN'")
    a = RDF(a)
    param = (a, l, m)
    if param in _cached_splines:
        splines = _cached_splines[param]
    else:
        file_name = "data/Zinf_a{:.1f}.dat".format(float(a)) if a <= 0.9 \
                    else "data/Zinf_a{:.2f}.dat".format(float(a))
        file_name = os.path.join(os.path.dirname(__file__), file_name)
        r_high_l = 20. if a <= 0.9 else 10.
        with open(file_name, "r") as data_file:
            lm_values = []  # l values up to 10 (for r <= r_high_l)
            lm_values_low = []  # l values up to 5 only (for r > r_high_l)
            for ld in range(2, 6):
                for md in range(1, ld + 1):
                    lm_values_low.append((ld, md))
            for ld in range(2, 11):
                for md in range(1, ld + 1):
                    lm_values.append((ld, md))
            Zreal = {}
            Zimag = {}
            for (ld, md) in lm_values:
                Zreal[(ld, md)] = []
                Zimag[(ld, md)] = []
            for line in data_file:
                items = line.split('\t')
                rd = RDF(items.pop(0))
                if rd <= r_high_l:
                    for (ld, md) in lm_values:
                        Zreal[(ld, md)].append((rd, RDF(items.pop(0))))
                        Zimag[(ld, md)].append((rd, RDF(items.pop(0))))
                else:
                    for (ld, md) in lm_values_low:
                        Zreal[(ld, md)].append((rd, RDF(items.pop(0))))
                        Zimag[(ld, md)].append((rd, RDF(items.pop(0))))
        for (ld, md) in lm_values:
            _cached_splines[(a, ) + (ld, md)] = (spline(Zreal[(ld, md)]),
                                                 spline(Zimag[(ld, md)]))
        if param not in _cached_splines:
            raise ValueError(
                "Zinf: case (a, l, m) = {} not implemented".format(param))
        splines = _cached_splines[param]
    # The factor (-1)**(l+m) below accounts for a difference of convention
    # in the C++ code used to produce the data files
    return (-1)**(l + m) * CDF(splines[0](r), splines[1](r))
Exemplo n.º 15
0
def Zinf_Schwarzchild_PN(l, m, r):
    r"""
    Amplitude factor of the mode `(\ell,m)` for a Schwarzschild BH at the 1.5PN
    level.

    The 1.5PN formulas are taken from E. Poisson, Phys. Rev. D **47**, 1497
    (1993), :doi:`10.1103/PhysRevD.47.1497`.

    INPUT:

    - ``l`` -- integer >= 2; the harmonic degree `\ell`
    - ``m`` -- integer within the range ``[-l, l]``; the azimuthal number `m`
    - ``r`` -- areal radius of the orbit (in units of `M`, the BH mass)

    OUTPUT:

    - coefficient `Z^\infty_{\ell m}(r)` (in units of `M^{-2}`)

    EXAMPLES::

        sage: from kerrgeodesic_gw import Zinf_Schwarzchild_PN
        sage: Zinf_Schwarzchild_PN(2, 2, 6.)  # tol 1.0e-13
        -0.00981450418730346 + 0.003855681972781947*I
        sage: Zinf_Schwarzchild_PN(5, 3, 6.)  # tol 1.0e-13
        -6.958527913913504e-05*I

    """
    if m < 0:
        return (-1)**l * Zinf_Schwarzchild_PN(l, -m, r).conjugate()
    v = 1. / sqrt(r)
    if l == 2:
        b = RDF(sqrt(pi / 5.) / r**4)
        if m == 1:
            return CDF(4. / 3. * I * b * v * (1 - 17. / 28. * v**2))
        if m == 2:
            return CDF(-16 * b *
                       (1 - 107. / 42. * v**2 +
                        (2 * pi + 4 * I *
                         (3 * ln(2 * v) - 0.839451001765134)) * v**3))
    if l == 3:
        b = RDF(sqrt(pi / 7.) / r**(4.5))
        if m == 1:
            return CDF(I / 3. * b / sqrt(10.) * (1 - 8. / 3. * v**2))
        if m == 2:
            return CDF(-16. / 3. * b * v)
        if m == 3:
            return CDF(-81 * I * b / sqrt(8.) * (1 - 4 * v**2))
    if l == 4:
        b = RDF(sqrt(pi) / r**5)
        if m == 1:
            return CDF(I / 105. * b / sqrt(2) * v)
        if m == 2:
            return CDF(-16. / 63. * b)
        if m == 3:
            return CDF(-81. / 5. * I * b / sqrt(14.) * v)
        if m == 4:
            return CDF(512. / 9. * b / sqrt(7.))
    if l == 5:
        b = RDF(sqrt(pi) / r**(5.5))
        if m == 1:
            return CDF(I / 360. * b / sqrt(77.))
        if m == 2:
            return CDF(0)
        if m == 3:
            return CDF(-81. / 40. * I * b * sqrt(3. / 22.))
        if m == 4:
            return CDF(0)
        if m == 5:
            return CDF(3125. / 24. * I * b * sqrt(5. / 66.))
    raise NotImplementedError("{} not implemented".format((l, m)))
Exemplo n.º 16
0
def numerical_approx(x, prec=None, digits=None, algorithm=None):
    r"""
    Returns a numerical approximation of an object ``x`` with at
    least ``prec`` bits (or decimal ``digits``) of precision.

    .. note::

       Both upper case ``N`` and lower case ``n`` are aliases for
       :func:`numerical_approx`, and all three may be used as
       methods.

    INPUT:

    -  ``x`` - an object that has a numerical_approx
       method, or can be coerced into a real or complex field
    -  ``prec`` (optional) - an integer (bits of
       precision)
    -  ``digits`` (optional) - an integer (digits of
       precision)
    -  ``algorithm`` (optional) - a string specifying
       the algorithm to use for functions that implement
       more than one

    If neither the ``prec`` or ``digits`` are specified,
    the default is 53 bits of precision.  If both are
    specified, then ``prec`` is used.

    EXAMPLES::

        sage: numerical_approx(pi, 10)
        3.1
        sage: numerical_approx(pi, digits=10)
        3.141592654
        sage: numerical_approx(pi^2 + e, digits=20)
        12.587886229548403854
        sage: n(pi^2 + e)
        12.5878862295484
        sage: N(pi^2 + e)
        12.5878862295484
        sage: n(pi^2 + e, digits=50)
        12.587886229548403854194778471228813633070946500941
        sage: a = CC(-5).n(prec=40)
        sage: b = ComplexField(40)(-5)
        sage: a == b
        True
        sage: parent(a) is parent(b)
        True
        sage: numerical_approx(9)
        9.00000000000000

    You can also usually use method notation.  ::

        sage: (pi^2 + e).n()
        12.5878862295484
        sage: (pi^2 + e).N()
        12.5878862295484
        sage: (pi^2 + e).numerical_approx()
        12.5878862295484

    Vectors and matrices may also have their entries approximated.  ::

        sage: v = vector(RDF, [1,2,3])
        sage: v.n()
        (1.00000000000000, 2.00000000000000, 3.00000000000000)

        sage: v = vector(CDF, [1,2,3])
        sage: v.n()
        (1.00000000000000, 2.00000000000000, 3.00000000000000)
        sage: _.parent()
        Vector space of dimension 3 over Complex Field with 53 bits of precision
        sage: v.n(prec=20)
        (1.0000, 2.0000, 3.0000)

        sage: u = vector(QQ, [1/2, 1/3, 1/4])
        sage: n(u, prec=15)
        (0.5000, 0.3333, 0.2500)
        sage: n(u, digits=5)
        (0.50000, 0.33333, 0.25000)

        sage: v = vector(QQ, [1/2, 0, 0, 1/3, 0, 0, 0, 1/4], sparse=True)
        sage: u = v.numerical_approx(digits=4)
        sage: u.is_sparse()
        True
        sage: u
        (0.5000, 0.0000, 0.0000, 0.3333, 0.0000, 0.0000, 0.0000, 0.2500)

        sage: A = matrix(QQ, 2, 3, range(6))
        sage: A.n()
        [0.000000000000000  1.00000000000000  2.00000000000000]
        [ 3.00000000000000  4.00000000000000  5.00000000000000]

        sage: B = matrix(Integers(12), 3, 8, srange(24))
        sage: N(B, digits=2)
        [0.00  1.0  2.0  3.0  4.0  5.0  6.0  7.0]
        [ 8.0  9.0  10.  11. 0.00  1.0  2.0  3.0]
        [ 4.0  5.0  6.0  7.0  8.0  9.0  10.  11.]

    Internally, numerical approximations of real numbers are stored in base-2.
    Therefore, numbers which look the same in their decimal expansion might be
    different::

        sage: x=N(pi, digits=3); x
        3.14
        sage: y=N(3.14, digits=3); y
        3.14
        sage: x==y
        False
        sage: x.str(base=2)
        '11.001001000100'
        sage: y.str(base=2)
        '11.001000111101'

    Increasing the precision of a floating point number is not allowed::

        sage: CC(-5).n(prec=100)
        Traceback (most recent call last):
        ...
        TypeError: cannot approximate to a precision of 100 bits, use at most 53 bits
        sage: n(1.3r, digits=20)
        Traceback (most recent call last):
        ...
        TypeError: cannot approximate to a precision of 70 bits, use at most 53 bits
        sage: RealField(24).pi().n()
        Traceback (most recent call last):
        ...
        TypeError: cannot approximate to a precision of 53 bits, use at most 24 bits

    As an exceptional case, ``digits=1`` usually leads to 2 digits (one
    significant) in the decimal output (see :trac:`11647`)::

        sage: N(pi, digits=1)
        3.2
        sage: N(pi, digits=2)
        3.1
        sage: N(100*pi, digits=1)
        320.
        sage: N(100*pi, digits=2)
        310.

    In the following example, ``pi`` and ``3`` are both approximated to two
    bits of precision and then subtracted, which kills two bits of precision::

        sage: N(pi, prec=2)
        3.0
        sage: N(3, prec=2)
        3.0
        sage: N(pi - 3, prec=2)
        0.00

    TESTS::

        sage: numerical_approx(I)
        1.00000000000000*I
        sage: x = QQ['x'].gen()
        sage: F.<k> = NumberField(x^2+2, embedding=sqrt(CC(2))*CC.0)
        sage: numerical_approx(k)
        1.41421356237309*I

        sage: type(numerical_approx(CC(1/2)))
        <type 'sage.rings.complex_number.ComplexNumber'>

    The following tests :trac:`10761`, in which ``n()`` would break when
    called on complex-valued algebraic numbers.  ::

        sage: E = matrix(3, [3,1,6,5,2,9,7,3,13]).eigenvalues(); E
        [18.16815365088822?, -0.08407682544410650? - 0.2190261484802906?*I, -0.08407682544410650? + 0.2190261484802906?*I]
        sage: E[1].parent()
        Algebraic Field
        sage: [a.n() for a in E]
        [18.1681536508882, -0.0840768254441065 - 0.219026148480291*I, -0.0840768254441065 + 0.219026148480291*I]

    Make sure we've rounded up log(10,2) enough to guarantee
    sufficient precision (:trac:`10164`)::

        sage: ks = 4*10**5, 10**6
        sage: check_str_length = lambda k: len(str(numerical_approx(1+10**-k,digits=k+1)))-1 >= k+1
        sage: check_precision = lambda k: numerical_approx(1+10**-k,digits=k+1)-1 > 0
        sage: all(check_str_length(k) and check_precision(k) for k in ks)
        True

    Testing we have sufficient precision for the golden ratio (:trac:`12163`), note
    that the decimal point adds 1 to the string length::

        sage: len(str(n(golden_ratio, digits=5000)))
        5001
        sage: len(str(n(golden_ratio, digits=5000000)))  # long time (4s on sage.math, 2012)
        5000001

    Check that :trac:`14778` is fixed::

        sage: n(0, algorithm='foo')
        0.000000000000000
    """
    if prec is None:
        if digits is None:
            prec = 53
        else:
            prec = int((digits+1) * LOG_TEN_TWO_PLUS_EPSILON) + 1
    try:
        return x._numerical_approx(prec, algorithm=algorithm)
    except AttributeError:
        pass

    from sage.structure.element import parent
    B = parent(x)

    RR = sage.rings.real_mpfr.RealField(prec)
    map = RR.coerce_map_from(B)
    if map is not None:
        return map(x)
    CC = sage.rings.complex_field.ComplexField(prec)
    map = CC.coerce_map_from(B)
    if map is not None:
        return map(x)

    # Coercion didn't work: there are 3 possibilities:
    # (1) There is a coercion possible to a lower precision
    # (2) There is a conversion but no coercion
    # (3) The type doesn't convert at all

    # Figure out input precision to check for case (1)
    try:
        inprec = x.prec()
    except AttributeError:
        if prec > 53 and CDF.has_coerce_map_from(B):
            # If we can coerce to CDF, assume input precision was 53 bits
            inprec = 53
        else:
            # Otherwise, assume precision wasn't the issue
            inprec = prec

    if prec > inprec:
        raise TypeError("cannot approximate to a precision of %s bits, use at most %s bits" % (prec, inprec))

    # The issue is not precision, try conversion instead
    try:
        return RR(x)
    except (TypeError, ValueError):
        pass
    return CC(x)
Exemplo n.º 17
0
def Sequence(x, universe=None, check=True, immutable=False, cr=False, cr_str=None, use_sage_types=False):
    """
    A mutable list of elements with a common guaranteed universe,
    which can be set immutable.

    A universe is either an object that supports coercion (e.g., a
    parent), or a category.

    INPUT:

    - ``x`` - a list or tuple instance

    - ``universe`` - (default: None) the universe of elements; if None
      determined using canonical coercions and the entire list of
      elements.  If list is empty, is category Objects() of all
      objects.

    - ``check`` -- (default: True) whether to coerce the elements of x
      into the universe

    - ``immutable`` - (default: True) whether or not this sequence is
      immutable

    - ``cr`` - (default: False) if True, then print a carriage return
      after each comma when printing this sequence.

    - ``cr_str`` - (default: False) if True, then print a carriage return
      after each comma when calling ``str()`` on this sequence.

    - ``use_sage_types`` -- (default: False) if True, coerce the
       built-in Python numerical types int, long, float, complex to the
       corresponding Sage types (this makes functions like vector()
       more flexible)
    
    OUTPUT:

    - a sequence

    EXAMPLES::
    
        sage: v = Sequence(range(10))
        sage: v.universe()
        <type 'int'>
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    We can request that the built-in Python numerical types be coerced
    to Sage objects::
    
        sage: v = Sequence(range(10), use_sage_types=True)
        sage: v.universe()
        Integer Ring
        sage: v
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    You can also use seq for "Sequence", which is identical to using
    Sequence::
    
        sage: v = seq([1,2,1/1]); v
        [1, 2, 1]
        sage: v.universe()
        Rational Field
        sage: v.parent()
        Category of sequences in Rational Field
        sage: v.parent()([3,4/3])
        [3, 4/3]
        

    Note that assignment coerces if possible,::
    
        sage: v = Sequence(range(10), ZZ)
        sage: a = QQ(5)
        sage: v[3] = a
        sage: parent(v[3])
        Integer Ring
        sage: parent(a)
        Rational Field
        sage: v[3] = 2/3
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    Sequences can be used absolutely anywhere lists or tuples can be used::
    
        sage: isinstance(v, list)
        True

    Sequence can be immutable, so entries can't be changed::
    
        sage: v = Sequence([1,2,3], immutable=True)
        sage: v.is_immutable()
        True
        sage: v[0] = 5
        Traceback (most recent call last):
        ...
        ValueError: object is immutable; please change a copy instead.

    Only immutable sequences are hashable (unlike Python lists),
    though the hashing is potentially slow, since it first involves
    conversion of the sequence to a tuple, and returning the hash of
    that.::

        sage: v = Sequence(range(10), ZZ, immutable=True)
        sage: hash(v)
        1591723448             # 32-bit
        -4181190870548101704   # 64-bit


    If you really know what you are doing, you can circumvent the type
    checking (for an efficiency gain)::
    
        sage: list.__setitem__(v, int(1), 2/3)        # bad circumvention
        sage: v
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: list.__setitem__(v, int(1), int(2))     # not so bad circumvention

    You can make a sequence with a new universe from an old sequence.::
    
        sage: w = Sequence(v, QQ)
        sage: w
        [0, 2, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: w.universe()
        Rational Field
        sage: w[1] = 2/3
        sage: w
        [0, 2/3, 2, 3, 4, 5, 6, 7, 8, 9]

    Sequences themselves live in a category, the category of all sequences
    in the given universe.::
    
        sage: w.category()
        Category of sequences in Rational Field
        
    This is also the parent of any sequence::
    
        sage: w.parent()
        Category of sequences in Rational Field

    The default universe for any sequence, if no compatible parent structure
    can be found, is the universe of all Sage objects.

    This example illustrates how every element of a list is taken into account
    when constructing a sequence.::
    
        sage: v = Sequence([1,7,6,GF(5)(3)]); v
        [1, 2, 1, 3]
        sage: v.universe()
        Finite Field of size 5
        sage: v.parent()
        Category of sequences in Finite Field of size 5
        sage: v.parent()([7,8,9])
        [2, 3, 4]
    """
    from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal


    if isinstance(x, Sequence_generic) and universe is None:
        universe = x.universe()
        x = list(x)

    if isinstance(x, MPolynomialIdeal) and universe is None:
        universe = x.ring()
        x = x.gens()

    if universe is None:
        if not isinstance(x, (list, tuple)):
            x = list(x)
            #raise TypeError("x must be a list or tuple")

        if len(x) == 0:
            import sage.categories.all
            universe = sage.categories.all.Objects()
        else:
            import sage.structure.element as coerce
            y = x
            x = list(x)   # make a copy, or we'd change the type of the elements of x, which would be bad.
            if use_sage_types:
                # convert any Python built-in numerical types to Sage objects
                from sage.rings.integer_ring import ZZ
                from sage.rings.real_double import RDF
                from sage.rings.complex_double import CDF
                for i in range(len(x)):
                    if isinstance(x[i], int) or isinstance(x[i], long):
                        x[i] = ZZ(x[i])
                    elif isinstance(x[i], float):
                        x[i] = RDF(x[i])
                    elif isinstance(x[i], complex):
                        x[i] = CDF(x[i])
            # start the pairwise coercion
            for i in range(len(x)-1):
                try:
                    x[i], x[i+1] = coerce.canonical_coercion(x[i],x[i+1])
                except TypeError:
                    import sage.categories.all
                    universe = sage.categories.all.Objects()
                    x = list(y)
                    check = False  # no point
                    break
            if universe is None:   # no type errors raised.
                universe = coerce.parent(x[len(x)-1])

    from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing
    from sage.rings.quotient_ring import is_QuotientRing
    from sage.rings.polynomial.pbori import BooleanMonomialMonoid

    if is_MPolynomialRing(universe) or \
            (is_QuotientRing(universe) and is_MPolynomialRing(universe.cover_ring())) or \
            isinstance(universe, BooleanMonomialMonoid):
        from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
        try:
            return PolynomialSequence(x, universe, immutable=immutable, cr=cr, cr_str=cr_str)
        except (TypeError,AttributeError):
            return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types)
    else:
        return Sequence_generic(x, universe, check, immutable, cr, cr_str, use_sage_types)
Exemplo n.º 18
0
def julia_plot(f=None, **kwds):
    r"""
    Plots the Julia set of a given polynomial ``f``. Users can specify whether
    they would like to display the Mandelbrot side by side with the Julia set
    with the ``mandelbrot`` argument. If ``f`` is not specified, this method
    defaults to `f(z) = z^2-1`.

    The Julia set of a polynomial ``f`` is the set of complex numbers `z` for
    which the function `f(z)` is bounded under iteration. The Julia set can
    be visualized by plotting each point in the set in the complex plane.
    Julia sets are examples of fractals when plotted in the complex plane.

    ALGORITHM:

    Let `R_c = \bigl(1 + \sqrt{1 + 4|c|}\bigr)/2` if the polynomial is of the
    form `f(z) = z^2 + c`; otherwise, let `R_c = 2`.
    For every `p \in \mathbb{C}`, if `|f^{k}(p)| > R_c` for some `k \geq 0`,
    then `f^{n}(p) \to \infty`.  Let `N` be the maximum number of iterations.
    Compute the first `N` points on the orbit of `p` under `f`. If for
    any `k < N`, `|f^{k}(p)| > R_c`, we stop the iteration and assign a color
    to the point `p` based on how quickly `p` escaped to infinity under
    iteration of `f`. If `|f^{i}(p)| \leq R_c` for all `i \leq N`, we assume
    `p` is in the Julia set and assign the point `p` the color black.

    INPUT:

    - ``f`` -- input polynomial (optional - default: ``z^2 - 1``).

    - ``period`` -- list (optional - default: ``None``), returns the Julia set
      for a random `c` value with the given (formal) cycle structure.

    - ``mandelbrot`` -- boolean (optional - default: ``True``), when set to
      ``True``, an image of the Mandelbrot set is appended to the right of the
      Julia set.

    - ``point_color`` -- RGB color (optional - default: ``'tomato'``),
      color of the point `c` in the Mandelbrot set (any valid input for Color).

    - ``x_center`` -- double (optional - default: ``-1.0``), Real part
      of center point.

    - ``y_center`` -- double (optional - default: ``0.0``), Imaginary part
      of center point.

    - ``image_width`` -- double (optional - default: ``4.0``), width of image
      in the complex plane.

    - ``max_iteration`` -- long (optional - default: ``500``), maximum number
      of iterations the map `f(z)`.

    - ``pixel_count`` -- long (optional - default: ``500``), side length of
      image in number of pixels.

    - ``base_color`` -- hex color (optional - default: ``'steelblue'``), color
      used to determine the coloring of set (any valid input for Color).

    - ``level_sep`` -- long (optional - default: 1), number of iterations
      between each color level.

    - ``number_of_colors`` -- long (optional - default: 30), number of colors
      used to plot image.

    - ``interact`` -- boolean (optional - default: ``False``), controls whether
      plot will have interactive functionality.

    OUTPUT:

    24-bit RGB image of the Julia set in the complex plane.

    .. TODO::

        Implement the side-by-side Mandelbrot-Julia plots for general one-parameter families
        of polynomials.

    EXAMPLES:

    The default ``f`` is `z^2 - 1`::

        sage: julia_plot()
        1001x500px 24-bit RGB image

    To display only the Julia set, set ``mandelbrot`` to ``False``::

        sage: julia_plot(mandelbrot=False)
        500x500px 24-bit RGB image

    ::

        sage: R.<z> = CC[]
        sage: f = z^3 - z + 1
        sage: julia_plot(f)
        500x500px 24-bit RGB image

    To display an interactive plot of the Julia set in the Notebook,
    set ``interact`` to ``True``. (This is only implemented for polynomials of
    the form ``f = z^2 + c``)::

        sage: julia_plot(interact=True)
        interactive(children=(FloatSlider(value=-1.0, description='Real c'...

        ::

        sage: R.<z> = CC[]
        sage: f = z^2 + 1/2
        sage: julia_plot(f,interact=True)
        interactive(children=(FloatSlider(value=0.5, description='Real c'...

    To return the Julia set of a random `c` value with (formal) cycle structure
    `(2,3)`, set ``period = [2,3]``::

        sage: julia_plot(period=[2,3])
        1001x500px 24-bit RGB image

    To return all of the Julia sets of `c` values with (formal) cycle structure
    `(2,3)`::

        sage: period = [2,3] # not tested
        ....: R.<c> = QQ[]
        ....: P.<x,y> = ProjectiveSpace(R,1)
        ....: f = DynamicalSystem([x^2+c*y^2, y^2])
        ....: L = f.dynatomic_polynomial(period).subs({x:0,y:1}).roots(ring=CC)
        ....: c_values = [k[0] for k in L]
        ....: for c in c_values:
        ....:     julia_plot(c)

    Polynomial maps can be defined over a polynomial ring or a fraction field,
    so long as ``f`` is polynomial::

        sage: R.<z> = CC[]
        sage: f = z^2 - 1
        sage: julia_plot(f)
        1001x500px 24-bit RGB image

    ::

        sage: R.<z> = CC[]
        sage: K = R.fraction_field(); z = K.gen()
        sage: f = z^2-1
        sage: julia_plot(f)
        1001x500px 24-bit RGB image

    Interact functionality is not implemented if the polynomial is not of the
    form `f = z^2 + c`::

        sage: R.<z> = CC[]
        sage: f = z^3 + 1
        sage: julia_plot(f, interact=True)
        Traceback (most recent call last):
        ...
        NotImplementedError: The interactive plot is only implemented for ...
    """

    # extract keyword arguments
    period = kwds.pop("period", None)
    mandelbrot = kwds.pop("mandelbrot", True)
    point_color = kwds.pop("point_color", 'tomato')
    x_center = kwds.pop("x_center", 0.0)
    y_center = kwds.pop("y_center", 0.0)
    image_width = kwds.pop("image_width", 4.0)
    max_iteration = kwds.pop("max_iteration", 500)
    pixel_count = kwds.pop("pixel_count", 500)
    base_color = kwds.pop("base_color", 'steelblue')
    level_sep = kwds.pop("level_sep", 1)
    number_of_colors = kwds.pop("number_of_colors", 30)
    interacts = kwds.pop("interact", False)

    f_is_default_after_all = None

    if period: # pick a random c with the specified period
        R = PolynomialRing(CC, 'c')
        c = R.gen()
        x, y = ProjectiveSpace(R, 1, 'x,y').gens()
        F = DynamicalSystem([x**2 + c * y**2, y**2])
        L = F.dynatomic_polynomial(period).subs({x: 0, y: 1}).roots(ring=CC)
        c = L[randint(0, len(L) - 1)][0]

    base_color = Color(base_color)
    point_color = Color(point_color)

    EPS = 0.00001

    if f is not None and period is None: # f user-specified and no period given

        # try to coerce f to live in a polynomial ring
        S = PolynomialRing(CC, names='z')
        z = S.gen()
        try:
            f_poly = S(f)
        except TypeError:
            R = f.parent()
            if not (R.is_integral_domain() and
                   (CC.is_subring(R) or CDF.is_subring(R))):
                raise ValueError('Given `f` must be a complex polynomial.')
            else:
                raise NotImplementedError(
                        'Julia sets not implemented for rational functions.'
                        )

        if (f_poly - z*z) in CC: # f is specified and of the form z^2 + c.
            f_is_default_after_all = True
            c = f_poly - z*z
        else: # f is specified and not of the form z^2 + c
            if interacts:
                raise NotImplementedError(
                        "The interactive plot is only implemented for "
                        "polynomials of the form f = z^2 + c."
                            )
            else:
                return general_julia(f_poly, x_center, y_center,
                                    image_width, max_iteration,
                                    pixel_count, level_sep,
                                    number_of_colors, base_color)

    # otherwise we can use fast_julia_plot for z^2 + c
    if f_is_default_after_all or f is None or period is not None:

        # specify default c = -1 value if f and period were not specified
        if not f_is_default_after_all and period is None:
            c = -1

        c = CC(c)
        c_real = c.real()
        c_imag = c.imag()

        if interacts: # set widgets
            from ipywidgets.widgets import FloatSlider, IntSlider, \
                                           ColorPicker, interact
            widgets = dict(
                c_real = FloatSlider(min=-2.0, max=2.0, step=EPS,
                                  value=c_real, description="Real c"),
                c_imag = FloatSlider(min=-2.0, max=2.0, step=EPS,
                                  value=c_imag, description="Imag c"),
                x_center = FloatSlider(min=-1.0, max=1.0, step=EPS,
                                  value=x_center, description="Real center"),
                y_center = FloatSlider(min=-1.0, max=1.0, step=EPS,
                                  value=y_center, description="Imag center"),
                image_width = FloatSlider(min=EPS, max=4.0, step=EPS,
                                  value=image_width, description="Width"),
                max_iteration = IntSlider(min=0, max=1000,
                                  value=max_iteration, description="Iterations"),
                pixel_count = IntSlider(min=10, max=1000,
                                  value=pixel_count, description="Pixels"),
                level_sep = IntSlider(min=1, max=20,
                                  value=level_sep, description="Color sep"),
                color_num = IntSlider(min=1, max=100,
                                  value=number_of_colors, description="# Colors"),
                base_color = ColorPicker(value=base_color.html_color(),
                                         description="Base color"),
            )
            if mandelbrot:
                widgets["point_color"] = ColorPicker(value=point_color.html_color(),
                                                     description="Point color")
                return interact(**widgets).widget(julia_helper)
            else:
                return interact(**widgets).widget(fast_julia_plot)
        elif mandelbrot: # non-interactive with mandelbrot
            return julia_helper(c_real, c_imag, x_center, y_center,
                                image_width, max_iteration, pixel_count,
                                level_sep, number_of_colors, base_color,
                                point_color)
        else: # non-interactive without mandelbrot
            return fast_julia_plot(c_real, c_imag, x_center, y_center,
                                   image_width, max_iteration, pixel_count,
                                   level_sep, number_of_colors, base_color)
Exemplo n.º 19
0
def h_toy_model_semi_analytic(u, theta, phi, a, r0, phi0, lam, Dphi, l_max=10):
    r"""
    Return the gravitational wave emitted by a matter blob orbiting a Kerr
    black hole (semi-analytic computation based on a toy model surface density).

    The surface density of the matter blob is that given by
    :func:`surface_density_toy_model`.

    The gravitational wave is computed according to the formula

    .. MATH::

        h = \frac{2\mu}{r} \, \sum_{\ell=2}^{\infty} \sum_{m=-\ell}^\ell
        \frac{Z^\infty_{\ell m}(r_0)}{(m\omega_0)^2} \;
        \text{sinc}\left( \frac{m}{2} \Delta\varphi \right) \,
        \text{sinc}\left( \frac{3}{4} \varepsilon \, m \omega_0
        (1-a\omega_0)u \right)
        e^{- i m (\omega_0 u + \phi_0)} \,
        _{-2}S_{\ell m}^{a m \omega_0}(\theta,\varphi)

    INPUT:

    - ``u`` -- retarded time coordinate of the observer (in units of `M`, the
      BH mass): `u = t - r_*`, where `t` is the Boyer-Lindquist time coordinate
      and `r_*` is the tortoise coordinate
    - ``theta`` -- Boyer-Lindquist colatitute  `\theta` of the observer
    - ``phi`` -- Boyer-Lindquist azimuthal coordinate `\phi`  of the observer
    - ``a`` -- BH angular momentum parameter (in units of `M`)
    - ``r0`` -- mean radius `r_0` of the matter blob (Boyer-Lindquist
      coordinate)
    - ``phi0`` -- mean azimuthal angle `\phi_0` of the matter blob
      (Boyer-Lindquist coordinate)
    - ``lam`` -- radial extent `\lambda` of the matter blob
    - ``Dphi``-- opening angle `\Delta\phi` of the matter blob
    - ``l_max`` -- (default: 10) upper bound in the summation over the harmonic
      degree `\ell`

    OUTPUT:

    - a pair ``(hp, hc)``, where ``hp`` (resp. ``hc``) is `(r / \mu) h_+`
      (resp. `(r / \mu) h_\times`), `\mu` being the blob's mass and
      `r` is the Boyer-Lindquist radial coordinate of the observer

    EXAMPLES:

    Schwarzschild black hole::

        sage: from kerrgeodesic_gw import h_toy_model_semi_analytic
        sage: a = 0
        sage: r0, phi0, lam, Dphi = 6.5, 0, 0.6, 0.1
        sage: u = 60.
        sage: h_toy_model_semi_analytic(u, pi/4, 0., a, r0, phi0, lam, Dphi)  # tol 1.0e-13
        (0.2999183296797872, 0.36916647790743246)
        sage: hp, hc = _

    Comparison with the exact value::

        sage: from kerrgeodesic_gw import (h_blob, blob_mass,
        ....:                              surface_density_toy_model)
        sage: param_surf_dens = [r0, phi0, lam, Dphi]
        sage: integ_range = [6.2, 6.8, -0.05, 0.05]
        sage: mu = blob_mass(a, surface_density_toy_model, param_surf_dens,
        ....:                integ_range)[0]
        sage: hp0 = h_blob(u, pi/4, 0., a, surface_density_toy_model,
        ....:              param_surf_dens, integ_range)[0] / mu
        sage: hc0 = h_blob(u, pi/4, 0., a, surface_density_toy_model,
        ....:              param_surf_dens, integ_range, mode='x')[0] / mu
        sage: hp0, hc0  # tol 1.0e-13
        (0.2951163078053617, 0.3743683023327848)
        sage: (hp - hp0) / hp0  # tol 1.0e-13
        0.01627162494047128
        sage: (hc - hc0) / hc0  # tol 1.0e-13
        -0.013894938201066784

    """
    import numpy
    from sage.rings.real_double import RDF
    from sage.rings.complex_double import CDF
    from sage.symbolic.all import i as I
    from .spin_weighted_spherical_harm import spin_weighted_spherical_harmonic
    from .spin_weighted_spheroidal_harm import spin_weighted_spheroidal_harmonic
    from .zinf import Zinf
    u = RDF(u)
    theta = RDF(theta)
    phi = RDF(phi)
    a = RDF(a)
    omega0 = RDF(1. / (r0**1.5 + a))
    eps = lam/r0
    resu = CDF(0)
    for l in range(2, l_max+1):
        for m in range(-l, l+1):
            if m == 0:    # m=0 is skipped
                continue  #
            m_omega0 = RDF(m*omega0)
            if a == 0:
                Slm = spin_weighted_spherical_harmonic(-2, l, m, theta, phi,
                                                       numerical=RDF)
            else:
                a = RDF(a)
                Slm = spin_weighted_spheroidal_harmonic(-2, l, m, a*m_omega0,
                                                        theta, phi)
            # Division by pi in the Sinc function due to the defintion used by numpy
            resu += Zinf(a, l, m, r0) / m_omega0**2 \
                    * numpy.sinc(m*Dphi/2./numpy.pi) \
                    * numpy.sinc(0.75*eps*m_omega0*(1-a*omega0)*u/numpy.pi) \
                    * CDF(exp(-I*(m_omega0*u + m*phi0))) * Slm
    resu *= 2
    return (resu.real(), -resu.imag())