Example #1
0
def platonic_icosahedron():
    r"""Produce a triple consisting of a polyhedral version of the platonic icosahedron,
    the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface
    to the polyhedron.

    EXAMPLES::

    sage: from flatsurf.geometry.polyhedra import platonic_icosahedron
    sage: polyhedron,surface,surface_to_polyhedron = platonic_icosahedron()
    sage: TestSuite(surface).run()
    r"""
    vertices=[]
    phi=AA(1+sqrt(5))/2
    F=NumberField(phi.minpoly(),"phi",embedding=phi)
    phi=F.gen()
    for i in xrange(3):
        for s1 in xrange(-1,3,2):
            for s2 in xrange(-1,3,2):
                p=3*[None]
                p[i]=s1*phi
                p[(i+1)%3]=s2
                p[(i+2)%3]=0
                vertices.append(vector(F,p))
    p=Polyhedron(vertices=vertices)
    
    s,m = polyhedron_to_cone_surface(p)
    return p,s,m
Example #2
0
def platonic_dodecahedron():
    r"""Produce a triple consisting of a polyhedral version of the platonic dodecahedron,
    the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface
    to the polyhedron.

    EXAMPLES::

    sage: from flatsurf.geometry.polyhedra import platonic_dodecahedron
    sage: polyhedron,surface,surface_to_polyhedron = platonic_dodecahedron()
    sage: TestSuite(surface).run()
    r"""
    vertices=[]
    phi=AA(1+sqrt(5))/2
    F=NumberField(phi.minpoly(),"phi",embedding=phi)
    phi=F.gen()
    for x in xrange(-1,3,2):
        for y in xrange(-1,3,2):
            for z in xrange(-1,3,2):
                vertices.append(vector(F,(x,y,z)))
    for x in xrange(-1,3,2):
        for y in xrange(-1,3,2):
            vertices.append(vector(F,(0,x*phi,y/phi)))
            vertices.append(vector(F,(y/phi,0,x*phi)))
            vertices.append(vector(F,(x*phi,y/phi,0)))
    scale=AA(2/sqrt(1+(phi-1)**2+(1/phi-1)**2))
    p=Polyhedron(vertices=vertices)
    s,m = polyhedron_to_cone_surface(p,scaling_factor=scale)
    return p,s,m
    def _mpfr_(self, R):
        r"""
        TESTS::

            sage: RR(E(7) + E(7,6))
            1.24697960371747
            sage: 2*cos(2*pi/7).n()
            1.24697960371747
        """
        if not self.is_real():
            raise TypeError("self is not real")

        from sage.rings.qqbar import QQbar, AA
        return AA(QQbar(self))._mpfr_(R)
Example #4
0
def platonic_tetrahedron():
    r"""Produce a triple consisting of a polyhedral version of the platonic tetrahedron,
    the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface
    to the polyhedron.

    EXAMPLES::

    sage: from flatsurf.geometry.polyhedra import platonic_tetrahedron
    sage: polyhedron,surface,surface_to_polyhedron = platonic_tetrahedron()
    sage: TestSuite(surface).run()
    r"""
    vertices = []
    for x in range(-1, 3, 2):
        for y in range(-1, 3, 2):
            vertices.append(vector(QQ, (x, y, x * y)))
    p = Polyhedron(vertices=vertices)
    s, m = polyhedron_to_cone_surface(p, scaling_factor=AA(1 / sqrt(2)))
    return p, s, m
Example #5
0
    def abs(self):
        """
        Return the absolute value of ``self`` as an algebraic real number.

        EXAMPLES::

            sage: f = 5/2*E(3)+E(5)/7
            sage: f.abs()
            2.597760303873084?

        TESTS::

            sage: [E(n).abs() for n in range(1, 11)]
            [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
            sage: UniversalCyclotomicField().zero().abs()
            0
        """
        square = self * self.conjugate()
        return AA(square).sqrt()
Example #6
0
def platonic_octahedron():
    r"""Produce a triple consisting of a polyhedral version of the platonic octahedron,
    the associated cone surface, and a ConeSurfaceToPolyhedronMap from the surface
    to the polyhedron.

    EXAMPLES::

    sage: from flatsurf.geometry.polyhedra import platonic_octahedron
    sage: polyhedron,surface,surface_to_polyhedron = platonic_octahedron()
    sage: TestSuite(surface).run()
    r"""
    vertices = []
    for i in range(3):
        temp = vector(QQ, [1 if k == i else 0 for k in range(3)])
        for j in range(-1, 3, 2):
            vertices.append(j * temp)
    octahedron = Polyhedron(vertices=vertices)
    surface,surface_to_octahedron = \
        polyhedron_to_cone_surface(octahedron,scaling_factor=AA(sqrt(2)))
    return octahedron, surface, surface_to_octahedron
Example #7
0
def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0):
    """
    Compute the complex roots of a given polynomial with exact
    coefficients (integer, rational, Gaussian rational, and algebraic
    coefficients are supported).  Returns a list of pairs of a root
    and its multiplicity.

    Roots are returned as a ComplexIntervalFieldElement; each interval
    includes exactly one root, and the intervals are disjoint.

    By default, the algorithm will do a squarefree decomposition
    to get squarefree polynomials.  The skip_squarefree parameter
    lets you skip this step.  (If this step is skipped, and the polynomial
    has a repeated root, then the algorithm will loop forever!)

    You can specify retval='interval' (the default) to get roots as
    complex intervals.  The other options are retval='algebraic' to
    get elements of QQbar, or retval='algebraic_real' to get only
    the real roots, and to get them as elements of AA.

    EXAMPLES::

        sage: from sage.rings.polynomial.complex_roots import complex_roots
        sage: x = polygen(ZZ)
        sage: complex_roots(x^5 - x - 1)
        [(1.167303978261419?, 1), (-0.764884433600585? - 0.352471546031727?*I, 1), (-0.764884433600585? + 0.352471546031727?*I, 1), (0.181232444469876? - 1.083954101317711?*I, 1), (0.181232444469876? + 1.083954101317711?*I, 1)]
        sage: v=complex_roots(x^2 + 27*x + 181)

    Unfortunately due to numerical noise there can be a small imaginary part to each
    root depending on CPU, compiler, etc, and that affects the printing order. So we
    verify the real part of each root and check that the imaginary part is small in
    both cases::

        sage: v # random
        [(-14.61803398874990?..., 1), (-12.3819660112501...? + 0.?e-27*I, 1)]
        sage: sorted((v[0][0].real(),v[1][0].real()))
        [-14.61803398874989?, -12.3819660112501...?]
        sage: v[0][0].imag() < 1e25
        True
        sage: v[1][0].imag() < 1e25
        True

        sage: K.<im> = NumberField(x^2 + 1)
        sage: eps = 1/2^100
        sage: x = polygen(K)
        sage: p = (x-1)*(x-1-eps)*(x-1+eps)*(x-1-eps*im)*(x-1+eps*im)

    This polynomial actually has all-real coefficients, and is very, very
    close to (x-1)^5::

        sage: [RR(QQ(a)) for a in list(p - (x-1)^5)]
        [3.87259191484932e-121, -3.87259191484932e-121]
        sage: rts = complex_roots(p)
        sage: [ComplexIntervalField(10)(rt[0] - 1) for rt in rts]
        [-7.8887?e-31, 0, 7.8887?e-31, -7.8887?e-31*I, 7.8887?e-31*I]

    We can get roots either as intervals, or as elements of QQbar or AA.

    ::

        sage: p = (x^2 + x - 1)
        sage: p = p * p(x*im)
        sage: p
        -x^4 + (im - 1)*x^3 + im*x^2 + (-im - 1)*x + 1

    Two of the roots have a zero real component; two have a zero
    imaginary component.  These zero components will be found slightly
    inaccurately, and the exact values returned are very sensitive to
    the (non-portable) results of NumPy.  So we post-process the roots
    for printing, to get predictable doctest results.

    ::

        sage: def tiny(x):
        ....:     return x.contains_zero() and x.absolute_diameter() <  1e-14
        sage: def smash(x):
        ....:     x = CIF(x[0]) # discard multiplicity
        ....:     if tiny(x.imag()): return x.real()
        ....:     if tiny(x.real()): return CIF(0, x.imag())
        sage: rts = complex_roots(p); type(rts[0][0]), sorted(map(smash, rts))
        (<type 'sage.rings.complex_interval.ComplexIntervalFieldElement'>, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?])
        sage: rts = complex_roots(p, retval='algebraic'); type(rts[0][0]), sorted(map(smash, rts))
        (<class 'sage.rings.qqbar.AlgebraicNumber'>, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?])
        sage: rts = complex_roots(p, retval='algebraic_real'); type(rts[0][0]), rts
        (<class 'sage.rings.qqbar.AlgebraicReal'>, [(-1.618033988749895?, 1), (0.618033988749895?, 1)])

    TESTS:

    Verify that :trac:`12026` is fixed::

        sage: f = matrix(QQ, 8, lambda i, j: 1/(i + j + 1)).charpoly()
        sage: from sage.rings.polynomial.complex_roots import complex_roots
        sage: len(complex_roots(f))
        8
    """

    if skip_squarefree:
        factors = [(p, 1)]
    else:
        factors = p.squarefree_decomposition()

    prec = 53
    while True:
        CC = ComplexField(prec)
        CCX = CC['x']

        all_rts = []
        ok = True

        for (factor, exp) in factors:
            cfac = CCX(factor)
            rts = cfac.roots(multiplicities=False)
            # Make sure the number of roots we found is the degree. If
            # we don't find that many roots, it's because the
            # precision isn't big enough and though the (possibly
            # exact) polynomial "factor" is squarefree, it is not
            # squarefree as an element of CCX.
            if len(rts) < factor.degree():
                ok = False
                break
            irts = interval_roots(factor, rts, max(prec, min_prec))
            if irts is None:
                ok = False
                break
            if retval != 'interval':
                factor = QQbar.common_polynomial(factor)
            for irt in irts:
                all_rts.append((irt, factor, exp))

        if ok and intervals_disjoint([rt for (rt, fac, mult) in all_rts]):
            all_rts = sort_complex_numbers_for_display(all_rts)
            if retval == 'interval':
                return [(rt, mult) for (rt, fac, mult) in all_rts]
            elif retval == 'algebraic':
                return [(QQbar.polynomial_root(fac, rt), mult) for (rt, fac, mult) in all_rts]
            elif retval == 'algebraic_real':
                rts = []
                for (rt, fac, mult) in all_rts:
                    qqbar_rt = QQbar.polynomial_root(fac, rt)
                    if qqbar_rt.imag().is_zero():
                        rts.append((AA(qqbar_rt), mult))
                return rts

        prec = prec * 2
Example #8
0
def polyhedron_to_cone_surface(polyhedron, use_AA=False, scaling_factor=ZZ(1)):
    r"""Construct the Euclidean Cone Surface associated to the surface of a polyhedron and a map
    from the cone surface to the polyhedron.
    
    INPUT:

    - ``polyhedron`` -- A 3-dimensional polyhedron, which should be define over something that coerces into AA

    - ``use_AA`` -- If True, the surface returned will be defined over AA. If false, the algorithm will find the smallest NumberField and write the field there.
    
    - ``scaling_factor`` -- The surface returned will have a metric scaled by multiplication by this factor (compared with the original polyhendron). This can be used to produce a surface defined over a smaller NumberField.
    
    OUTPUT:
    
    A pair consisting of a ConeSurface and a ConeSurfaceToPolyhedronMap.

    EXAMPLES::

    sage: from flatsurf.geometry.polyhedra import *
    sage: vertices=[]
    sage: for i in range(3):
    ....:     temp=vector([1 if k==i else 0 for k in range(3)])
    ....:     for j in range(-1,3,2):
    ....:         vertices.append(j*temp)
    sage: octahedron=Polyhedron(vertices=vertices)
    sage: surface,surface_to_octahedron = \
    ....:     polyhedron_to_cone_surface(octahedron,scaling_factor=AA(1/sqrt(2)))
    sage: TestSuite(surface).run()
    sage: TestSuite(surface_to_octahedron).run(skip="_test_pickling")
    sage: surface.num_polygons()
    8
    sage: surface.base_ring()
    Number Field in a with defining polynomial y^2 - 3 with a = 1.732050807568878?
    sage: sqrt3=surface.base_ring().gen()
    sage: tangent_bundle=surface.tangent_bundle()
    sage: v=tangent_bundle(0,(0,0),(sqrt3,2))
    sage: traj=v.straight_line_trajectory()
    sage: traj.flow(10)
    sage: traj.is_saddle_connection()
    True
    sage: traj.combinatorial_length()
    8
    sage: path3d = surface_to_octahedron(traj)
    sage: len(path3d)
    9
    sage: # We will show that the length of the path is sqrt(42):
    sage: total_length = 0
    sage: for i in range(8):
    ....:     start = path3d[i]
    ....:     end = path3d[i+1]
    ....:     total_length += (vector(end)-vector(start)).norm()
    sage: ZZ(total_length**2)
    42
    """
    assert polyhedron.dim() == 3
    c = polyhedron.center()
    vertices = polyhedron.vertices()
    vertex_order = {}
    for i, v in enumerate(vertices):
        vertex_order[v] = i
    faces = polyhedron.faces(2)
    face_order = {}
    face_edges = []
    face_vertices = []
    face_map_data = []
    for i, f in enumerate(faces):
        face_order[f] = i
        edges = f.as_polyhedron().faces(1)
        face_edges_temp = set()
        for edge in edges:
            edge_temp = set()
            for vertex in edge.vertices():
                v = vertex.vector()
                v.set_immutable()
                edge_temp.add(v)
            face_edges_temp.add(frozenset(edge_temp))

        last_edge = next(iter(face_edges_temp))
        v = next(iter(last_edge))
        face_vertices_temp = [v]
        for j in range(len(face_edges_temp) - 1):
            for edge in face_edges_temp:
                if v in edge and edge != last_edge:
                    # bingo
                    last_edge = edge
                    for vv in edge:
                        if vv != v:
                            v = vv
                            face_vertices_temp.append(vv)
                            break
                    break

        v0 = face_vertices_temp[0]
        v1 = face_vertices_temp[1]
        v2 = face_vertices_temp[2]
        n = (v1 - v0).cross_product(v2 - v0)
        if (v0 - c).dot_product(n) < 0:
            n = -n
            face_vertices_temp.reverse()
            v0 = face_vertices_temp[0]
            v1 = face_vertices_temp[1]
            v2 = face_vertices_temp[2]

        face_vertices.append(face_vertices_temp)
        n = n / AA(n.norm())
        w = v1 - v0
        w = w / AA(w.norm())
        m = 1 / scaling_factor * matrix(
            AA, [w, n.cross_product(w), n]).transpose()
        mi = ~m
        mis = mi.submatrix(0, 0, 2, 3)
        face_map_data.append((
            v0,  # translation to bring origin in plane to v0
            m.submatrix(0, 0, 3, 2),
            -mis * v0,
            mis))

        it = iter(face_vertices_temp)
        v_last = next(it)
        face_edge_dict = {}
        j = 0
        for v in it:
            edge = frozenset([v_last, v])
            face_edge_dict[edge] = j
            j += 1
            v_last = v
        v = next(iter(face_vertices_temp))
        edge = frozenset([v_last, v])
        face_edge_dict[edge] = j
        face_edges.append(face_edge_dict)

    gluings = {}
    for p1, face_edge_dict1 in enumerate(face_edges):
        for edge, e1 in face_edge_dict1.items():
            found = False
            for p2, face_edge_dict2 in enumerate(face_edges):
                if p1 != p2 and edge in face_edge_dict2:
                    e2 = face_edge_dict2[edge]
                    gluings[(p1, e1)] = (p2, e2)
                    found = True
                    break
            if not found:
                print(p1)
                print(e1)
                print(edge)
                raise RuntimeError("Failed to find glued edge")
    polygon_vertices_AA = []
    for p, vs in enumerate(face_vertices):
        trans = face_map_data[p][2]
        m = face_map_data[p][3]
        polygon_vertices_AA.append([trans + m * v for v in vs])

    if use_AA == True:
        Polys = ConvexPolygons(AA)
        polygons = []
        for vs in polygon_vertices_AA:
            polygons.append(Polys(vertices=vs))
        S = ConeSurface(
            surface_list_from_polygons_and_gluings(polygons, gluings))
        return S, \
            ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data)
    else:
        elts = []
        for vs in polygon_vertices_AA:
            for v in vs:
                elts.append(v[0])
                elts.append(v[1])

        # Find the best number field:
        field, elts2, hom = number_field_elements_from_algebraics(elts,
                                                                  minimal=True)
        if field == QQ:
            # Defined over the rationals!
            polygon_vertices_field2 = []
            j = 0
            for vs in polygon_vertices_AA:
                vs2 = []
                for v in vs:
                    vs2.append(vector(field, [elts2[j], elts2[j + 1]]))
                    j = j + 2
                polygon_vertices_field2.append(vs2)
            Polys = ConvexPolygons(field)
            polygons = []
            for vs in polygon_vertices_field2:
                polygons.append(Polys(vertices=vs))
            S = ConeSurface(
                surface_list_from_polygons_and_gluings(polygons, gluings))
            return S, \
                ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data)

        else:
            # Unfortunately field doesn't come with an real embedding (which is given by hom!)
            # So, we make a copy of the field, and add the embedding.
            field2 = NumberField(field.polynomial(),
                                 name="a",
                                 embedding=hom(field.gen()))
            # The following converts from field to field2:
            hom2 = field.hom(im_gens=[field2.gen()])

            polygon_vertices_field2 = []
            j = 0
            for vs in polygon_vertices_AA:
                vs2 = []
                for v in vs:
                    vs2.append(
                        vector(field2, [hom2(elts2[j]),
                                        hom2(elts2[j + 1])]))
                    j = j + 2
                polygon_vertices_field2.append(vs2)
            Polys = ConvexPolygons(field2)
            polygons = []
            for vs in polygon_vertices_field2:
                polygons.append(Polys(vertices=vs))
            S = ConeSurface(
                surface_list_from_polygons_and_gluings(polygons, gluings))
            return S, \
                ConeSurfaceToPolyhedronMap(S,polyhedron,face_map_data)