Example #1
0
def angle(u, v, numerical=False, assume_rational=False):
    r"""
    Return the angle between the vectors ``u`` and ``v`` divided by `2 \pi`.

    INPUT:

    - ``u``, ``v`` - vectors

    - ``numerical`` - boolean, whether to return floating point numbers

    - ``assume_rational`` - whether we assume that the angle is a multiple
      rational of ``pi``. By default it is ``False`` but if it is known in
      advance that the result is rational then setting it to ``True`` might be
      much faster.

    EXAMPLES::

        sage: from flatsurf.geometry.matrix_2x2 import angle

    As the implementation is dirty, we at least check that it works for all
    denominator up to 20::

        sage: u = vector((AA(1),AA(0)))
        sage: for n in xsrange(1,20):       # long time  (10 sec)
        ....:     for k in xsrange(1,n):
        ....:         v = vector((AA(cos(2*k*pi/n)), AA(sin(2*k*pi/n))))
        ....:         assert angle(u,v) == k/n

    The numerical version (working over floating point numbers)::

        sage: import math
        sage: u = (1, 0)
        sage: for n in xsrange(1,20):
        ....:     for k in xsrange(1,n):
        ....:         a = 2 * k * math.pi / n
        ....:         v = (math.cos(a), math.sin(a))
        ....:         assert abs(angle(u,v,numerical=True) * 2 * math.pi - a) < 1.e-10

    And we test up to 50 when setting ``assume_rational`` to ``True``::

        sage: for n in xsrange(1,20):       # long time
        ....:     for k in xsrange(1,n):
        ....:         v = vector((AA(cos(2*k*pi/n)), AA(sin(2*k*pi/n))))
        ....:         assert angle(u,v,assume_rational=True) == k/n

    If the angle is not rational, then the method returns an element in the real
    lazy field::

        sage: v = vector((AA(sqrt(2)), AA(sqrt(3))))
        sage: a = angle(u, v)
        sage: a    # abs tol 1e-14
        0.14102355421224375
        sage: exp(2*pi.n()*CC(0,1)*a)
        0.632455532033676 + 0.774596669241483*I
        sage: v / v.norm()
        (0.6324555320336758?, 0.774596669241484?)
    """
    if not assume_rational and not numerical:
        sqnorm_u = u[0] * u[0] + u[1] * u[1]
        sqnorm_v = v[0] * v[0] + v[1] * v[1]

        if sqnorm_u != sqnorm_v:
            uu = vector(AA, u)
            vv = (AA(sqnorm_u) / AA(sqnorm_v)).sqrt() * vector(AA, v)
        else:
            uu = u
            vv = v

        cos_uv = (uu[0] * vv[0] + uu[1] * vv[1]) / sqnorm_u
        sin_uv = (uu[0] * vv[1] - uu[1] * vv[0]) / sqnorm_u

        is_rational = is_cosine_sine_of_rational(cos_uv, sin_uv)
    elif assume_rational:
        is_rational = True

    import math

    u0 = float(u[0])
    u1 = float(u[1])
    v0 = float(v[0])
    v1 = float(v[1])

    cos_uv = (u0 * v0 + u1 * v1) / math.sqrt(
        (u0 * u0 + u1 * u1) * (v0 * v0 + v1 * v1))
    if cos_uv < -1.0:
        assert cos_uv > -1.0000001
        cos_uv = -1.0
    elif cos_uv > 1.0:
        assert cos_uv < 1.0000001
        cos_uv = 1.0
    angle = math.acos(cos_uv) / (2 * math.pi)  # rat number between 0 and 1/2

    if numerical or not is_rational:
        return 1.0 - angle if u0 * v1 - u1 * v0 < 0 else angle
    else:
        # fast and dirty way using floating point approximation
        # (see below for a slow but exact method)
        angle_rat = RR(angle).nearby_rational(0.00000001)
        if angle_rat.denominator() > 100:
            raise NotImplementedError(
                "the numerical method used is not smart enough!")
        return 1 - angle_rat if u0 * v1 - u1 * v0 < 0 else angle_rat
Example #2
0
def angle(u, v, assume_rational=False):
    r"""
    Return the angle between the vectors ``u`` and ``v`` divided by `2 \pi`.

    INPUT:

    - ``u``, ``v`` - vectors

    - ``assume_rational`` - whether we assume that the angle is a multiple
      rational of ``pi``. By default it is ``False`` but if it is known in
      advance that the result is rational then setting it to ``True`` might be
      much faster.

    EXAMPLES::

        sage: from flatsurf.geometry.matrix_2x2 import angle

    As the implementation is dirty, we at least check that it works for all
    denominator up to 20::

        sage: u = vector((AA(1),AA(0)))
        sage: for n in xsrange(1,20):       # long time  (10 sec)
        ....:     for k in xsrange(1,n):
        ....:         v = vector((AA(cos(2*k*pi/n)), AA(sin(2*k*pi/n))))
        ....:         assert angle(u,v) == k/n

    And we test up to 50 when setting ``assume_rational`` to ``True``::

        sage: for n in xsrange(1,20):       # long time
        ....:     for k in xsrange(1,n):
        ....:         v = vector((AA(cos(2*k*pi/n)), AA(sin(2*k*pi/n))))
        ....:         assert angle(u,v,assume_rational=True) == k/n

    If the angle is not rational, then the method returns an element in the real
    lazy field::

        sage: v = vector((AA(sqrt(2)), AA(sqrt(3))))
        sage: a = angle(u,v)
        sage: a
        0.1410235542122437?
        sage: exp(2*pi.n()*CC(0,1)*a.n())
        0.632455532033676 + 0.774596669241483*I
        sage: v / v.norm()
        (0.6324555320336758?, 0.774596669241484?)
    """
    if not assume_rational:
        sqnorm_u = u[0] * u[0] + u[1] * u[1]
        sqnorm_v = v[0] * v[0] + v[1] * v[1]

        if sqnorm_u != sqnorm_v:
            uu = u.change_ring(AA)
            vv = (AA(sqnorm_u) / AA(sqnorm_v)).sqrt() * v.change_ring(AA)
        else:
            uu = u
            vv= v

        cos_uv = (uu[0]*vv[0] + uu[1]*vv[1]) / sqnorm_u
        sin_uv = (uu[0]*vv[1] - uu[1]*vv[0]) / sqnorm_u

        is_rational = is_cosine_sine_of_rational(cos_uv, sin_uv)

    else:
        is_rational = True

    if is_rational:
        # fast and dirty way using floating point approximation
        # (see below for a slow but exact method)
        from math import acos,asin,sqrt

        u0 = float(u[0]); u1 = float(u[1])
        v0 = float(v[0]); v1 = float(v[1])

        cos_uv = (u0*v0 + u1*v1) / sqrt((u0*u0 + u1*u1)*(v0*v0 + v1*v1))
        angle = acos(float(cos_uv)) / (2*pi_float)   # rat number between 0 and 1/2
        angle_rat = RR(angle).nearby_rational(0.00000001)
        if angle_rat.denominator() > 100:
            raise NotImplementedError("the numerical method used is not smart enough!")
        if u0*v1 - u1*v0 < 0:
            return 1 - angle_rat
        return angle_rat

    else:
        from sage.functions.trig import acos
        from sage.rings.real_lazy import RLF
        from sage.symbolic.constants import pi

        if sin_uv > 0:
            return acos(RLF(cos_uv)) / RLF(2*pi)
        else:
            return -acos(RLF(cos_uv)) / RLF(2*pi)