def as_embedded_number_field_elements(algs): try: nf, elts, _ = number_field_elements_from_algebraics(algs, embedded=True) except NotImplementedError: # compatibility with Sage <= 9.3 nf, elts, emb = number_field_elements_from_algebraics(algs) if nf is not QQ: nf = NumberField(nf.polynomial(), nf.variable_name(), embedding=emb(nf.gen())) elts = [elt.polynomial()(nf.gen()) for elt in elts] nf, hom = good_number_field(nf) elts = [hom(elt) for elt in elts] assert [QQbar.coerce(elt) == alg for alg, elt in zip(algs, elts)] return nf, elts
def number_field_elements_from_algebraics(elts, name='a'): r""" The native Sage function ``number_field_elements_from_algebraics`` currently returns number field *without* embedding. This function return field with embedding! EXAMPLES:: sage: from flatsurf.geometry.subfield import number_field_elements_from_algebraics sage: z = QQbar.zeta(5) sage: c = z.real() sage: s = z.imag() sage: number_field_elements_from_algebraics((c,s)) (Number Field in a with defining polynomial y^4 - 5*y^2 + 5 with a = 1.902113032590308?, [1/2*a^2 - 3/2, 1/2*a]) sage: number_field_elements_from_algebraics([AA(1), AA(2/3)]) (Rational Field, [1, 2/3]) """ # case when all elements are rationals if all(x in QQ for x in elts): return QQ, [QQ(x) for x in elts] # general case from sage.rings.qqbar import number_field_elements_from_algebraics field, elts, phi = number_field_elements_from_algebraics(elts, minimal=True) polys = [x.polynomial() for x in elts] K = NumberField(field.polynomial(), name, embedding=AA(phi(field.gen()))) gen = K.gen() return K, [x.polynomial()(gen) for x in elts]
def regular_ngon(n): r""" Return a regular n-gon. EXAMPLES:: sage: from flatsurf.geometry.polygon import polygons sage: p = polygons.regular_ngon(17) sage: p Polygon: (0, 0), (1, 0), ..., (-1/2*a^14 + 15/2*a^12 - 45*a^10 + 275/2*a^8 - 225*a^6 + 189*a^4 - 70*a^2 + 15/2, 1/2*a) """ # The code below crashes for n=4! if n==4: return polygons.square(QQ(1)) from sage.rings.qqbar import QQbar c = QQbar.zeta(n).real() s = QQbar.zeta(n).imag() field, (c,s) = number_field_elements_from_algebraics((c,s)) cn = field.one() sn = field.zero() edges = [(cn,sn)] for _ in range(n-1): cn,sn = c*cn - s*sn, c*sn + s*cn edges.append((cn,sn)) return Polygons(field)(edges=edges)
def number_field_elements_from_algebraics(elts, name='a'): r""" The native Sage function ``number_field_elements_from_algebraics`` currently returns number field *without* embedding. This function return field with embedding! EXAMPLES:: sage: from flatsurf.geometry.polygon import number_field_elements_from_algebraics sage: z = QQbar.zeta(5) sage: c = z.real() sage: s = z.imag() sage: number_field_elements_from_algebraics((c,s)) (Number Field in a with defining polynomial y^4 - 5*y^2 + 5, [1/2*a^2 - 3/2, 1/2*a]) """ from sage.rings.qqbar import number_field_elements_from_algebraics from sage.rings.number_field.number_field import NumberField field,elts,phi = number_field_elements_from_algebraics(elts, minimal=True) polys = [x.polynomial() for x in elts] K = NumberField(field.polynomial(), name, embedding=AA(phi(field.gen()))) gen = K.gen() return K, [x.polynomial()(gen) for x in elts]
def to_pyflatsurf(S): r""" Given S a translation surface from sage-flatsurf return a flatsurf::FlatTriangulation from libflatsurf/pyflatsurf. """ if not isinstance(S, TranslationSurface): raise TypeError("S must be a translation surface") if not S.is_finite(): raise ValueError("the surface S must be finite") S = S.triangulate() # populate half edges and vectors n = sum(S.polygon(lab).num_edges() for lab in S.label_iterator()) half_edge_labels = {} # map: (face lab, edge num) in faltsurf -> integer vec = [] # vectors k = 1 # half edge label in {1, ..., n} for t0, t1 in S.edge_iterator(gluings=True): if t0 in half_edge_labels: continue half_edge_labels[t0] = k half_edge_labels[t1] = -k f0, e0 = t0 p = S.polygon(f0) vec.append(p.edge(e0)) k += 1 # compute vertex and face permutations vp = [None] * (n + 1) # vertex permutation fp = [None] * (n + 1) # face permutation for t in S.edge_iterator(gluings=False): e = half_edge_labels[t] j = (t[1] + 1) % S.polygon(t[0]).num_edges() fp[e] = half_edge_labels[(t[0], j)] vp[fp[e]] = -e # convert the vp permutation into cycles verts = _cycle_decomposition(vp) # find a finite SageMath base ring that contains all the coordinates base_ring = S.base_ring() if base_ring is AA: from sage.rings.qqbar import number_field_elements_from_algebraics from itertools import chain base_ring = number_field_elements_from_algebraics(list( chain(*[list(v) for v in vec])), embedded=True)[0] V = Vectors(base_ring) vec = [V(v).vector for v in vec] _check_data(vp, fp, vec) return make_surface(verts, vec)
def _number_field_from_algebraics(self): r""" Given a projective point defined over ``QQbar``, return the same point, but defined over a number field. This is only implemented for points of projective space. OUTPUT: scheme point EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1]) sage: S = Q._number_field_from_algebraics(); S (1/2*a^3 + a^2 - 1/2*a : 1) sage: S.codomain() Projective Space of dimension 1 over Number Field in a with defining polynomial y^4 + 1 with a = 0.7071067811865475? + 0.7071067811865475?*I The following was fixed in :trac:`23808`:: sage: R.<x> = PolynomialRing(QQ) sage: P.<x,y> = ProjectiveSpace(QQbar,1) sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1]);Q (-0.7071067811865475? + 1*I : 1) sage: S = Q._number_field_from_algebraics(); S (1/2*a^3 + a^2 - 1/2*a : 1) sage: T = S.change_ring(QQbar) # Used to fail sage: T (-0.7071067811865475? + 1.000000000000000?*I : 1) sage: Q[0] == T[0] True """ from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(self.codomain()): raise NotImplementedError("not implemented for subschemes") # Trac #23808: Keep the embedding info associated with the number field K # used below, instead of in the separate embedding map phi which is # forgotten. K_pre, P, phi = number_field_elements_from_algebraics(list(self)) if K_pre is QQ: K = QQ else: from sage.rings.number_field.number_field import NumberField K = NumberField(K_pre.polynomial(), embedding=phi(K_pre.gen()), name='a') psi = K_pre.hom([K.gen()], K) # Identification of K_pre with K P = [psi(p) for p in P] # The elements of P were elements of K_pre from sage.schemes.projective.projective_space import ProjectiveSpace PS = ProjectiveSpace(K, self.codomain().dimension_relative(), 'z') return PS(P)
def as_embedded_number_field_elements(algs): # number_field_elements_from_algebraics() now takes an embedded=... # argument, but doesn't yet support all cases we need nf, elts, emb = number_field_elements_from_algebraics(algs) if nf is not QQ: nf = NumberField(nf.polynomial(), nf.variable_name(), embedding=emb(nf.gen())) elts = [elt.polynomial()(nf.gen()) for elt in elts] nf, hom = good_number_field(nf) elts = [hom(elt) for elt in elts] # assert [QQbar.coerce(elt) == alg for alg, elt in zip(algs, elts)] return nf, elts
def right_triangle(angle,leg0=None, leg1=None, hypotenuse=None): r""" Return a right triangle in a numberfield with an angle of pi*m/n. You can specify the length of the first leg (leg0), the second leg (leg1), or the hypotenuse. """ from sage.rings.qqbar import QQbar z=QQbar.zeta(2*angle.denom())**angle.numer() c = z.real() s = z.imag() if not leg0 is None: c,s = leg0*c/c,leg0*s/c elif not leg1 is None: c,s = leg1*c/s,leg1*s/s elif not hypotenuse is None: c,s = hypotenuse*c, hypotenuse*s field, (c,s) = number_field_elements_from_algebraics((c,s)) return Polygons(field)(edges=[(c,field.zero()),(field.zero(),s),(-c,-s)])
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 xrange(3): ....: temp=vector([1 if k==i else 0 for k in xrange(3)]) ....: for j in xrange(-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 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: surface_to_octahedron(traj) [(-1, 0, 0), (0.?e-18, -0.8000000000000000?, -0.2000000000000000?), (0.2500000000000000?, -0.750000000000000?, 0.?e-18), (0.4000000000000000?, 0.?e-19, 0.6000000000000000?), (0.?e-19, 0.500000000000000?, 0.500000000000000?), (-2/5, 3/5, 0), (-0.2500000000000000?, 0.?e-18, -0.750000000000000?), (0.?e-18, -0.2000000000000000?, -0.8000000000000000?), (1, 0, 0)] sage: surface_to_octahedron(traj.segment(0)) ((-1, 0, 0), (0.?e-18, -0.8000000000000000?, -0.2000000000000000?)) sage: surface_to_octahedron(traj.segment(0).start()) ((-1, 0, 0), (2.886751345948129?, -2.309401076758503?, -0.5773502691896258?)) sage: # octahedron.plot(wireframe=None,frame=False)+line3d(surface_to_octahedron(traj),radius=0.01) """ 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 xrange(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=Polygons(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=Polygons(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=Polygons(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)
def wrapper(*args, **kwds): """ TESTS:: sage: from sage.rings.qqbar_decorators import handle_AA_and_QQbar sage: @handle_AA_and_QQbar ....: def return_base_ring(x): ....: return x.base_ring() sage: P.<x> = QQbar[] sage: return_base_ring(x) Rational Field sage: P.<y,z> = QQbar[] sage: return_base_ring(y) Rational Field sage: return_base_ring(ideal(y,z)) Rational Field """ from sage.misc.flatten import flatten from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.polynomial.multi_polynomial import MPolynomial from sage.rings.ideal import Ideal, Ideal_generic from sage.rings.qqbar import AlgebraicField_common, number_field_elements_from_algebraics if not any([ isinstance(a, (Polynomial, MPolynomial, Ideal_generic)) and isinstance(a.base_ring(), AlgebraicField_common) for a in args ]): return func(*args, **kwds) polynomials = [] for a in flatten(args, ltypes=(list, tuple, set)): if isinstance(a, Ideal_generic): polynomials.extend(a.gens()) elif isinstance(a, Polynomial): polynomials.append(a) elif isinstance(a, MPolynomial): polynomials.append(a) orig_elems = flatten([p.coefficients() for p in polynomials]) # We need minimal=True if these elements are over AA, because # same_field=True might trigger an exception otherwise. numfield, new_elems, morphism = number_field_elements_from_algebraics( orig_elems, same_field=True, minimal=True) elem_dict = dict(zip(orig_elems, new_elems)) def forward_map(item): if isinstance(item, Ideal_generic): return Ideal([forward_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif isinstance(item, MPolynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif isinstance(item, list): return map(forward_map, item) elif isinstance(item, tuple): return tuple(map(forward_map, item)) elif isinstance(item, set): return set(map(forward_map, list(item))) else: return item def reverse_map(item): if isinstance(item, Ideal_generic): return Ideal([reverse_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(morphism) elif isinstance(item, MPolynomial): return item.map_coefficients(morphism) elif isinstance(item, list): return map(reverse_map, item) elif isinstance(item, tuple): return tuple(map(reverse_map, item)) elif isinstance(item, set): return set(map(reverse_map, list(item))) else: return item args = map(forward_map, args) return reverse_map(func(*args, **kwds))
def wrapper(*args, **kwds): """ TESTS:: sage: from sage.rings.qqbar_decorators import handle_AA_and_QQbar sage: @handle_AA_and_QQbar ....: def return_base_ring(x): ....: return x.base_ring() sage: P.<x> = QQbar[] sage: return_base_ring(x) Rational Field sage: P.<y,z> = QQbar[] sage: return_base_ring(y) Rational Field sage: return_base_ring(ideal(y,z)) Rational Field Check that :trac:`29468` is fixed:: sage: J = QQbar['x,y'].ideal('x^2 - y') sage: type(J.groebner_basis()) <class 'sage.rings.polynomial.multi_polynomial_sequence.PolynomialSequence_generic'> sage: J.groebner_basis().is_immutable() True :: sage: @handle_AA_and_QQbar ....: def f(x): ....: print(x.ring().base_ring()) ....: return x sage: R.<x,y> = QQbar[] sage: s = Sequence([x, R(sqrt(2)) * y], immutable=True) sage: t = f(s) Number Field in a with defining polynomial y^2 - 2 sage: t.ring().base_ring() Algebraic Field sage: t.is_immutable() True sage: s == t True """ from sage.misc.flatten import flatten from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.polynomial.multi_polynomial import MPolynomial from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence, is_PolynomialSequence from sage.rings.ideal import Ideal, Ideal_generic from sage.rings.qqbar import is_AlgebraicField_common, number_field_elements_from_algebraics if not any( isinstance(a, (Polynomial, MPolynomial, Ideal_generic)) and is_AlgebraicField_common(a.base_ring()) or is_PolynomialSequence(a) and is_AlgebraicField_common(a.ring().base_ring()) for a in args): return func(*args, **kwds) polynomials = [] for a in flatten(args, ltypes=(list, tuple, set)): if isinstance(a, Ideal_generic): polynomials.extend(a.gens()) elif isinstance(a, Polynomial): polynomials.append(a) elif isinstance(a, MPolynomial): polynomials.append(a) orig_elems = flatten([p.coefficients() for p in polynomials]) # We need minimal=True if these elements are over AA, because # same_field=True might trigger an exception otherwise. numfield, new_elems, morphism = number_field_elements_from_algebraics( orig_elems, same_field=True, minimal=True) elem_dict = dict(zip(orig_elems, new_elems)) def forward_map(item): if isinstance(item, Ideal_generic): return Ideal([forward_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif isinstance(item, MPolynomial): return item.map_coefficients(elem_dict.__getitem__, new_base_ring=numfield) elif is_PolynomialSequence(item): return PolynomialSequence(map(forward_map, item), immutable=item.is_immutable()) elif isinstance(item, list): return list(map(forward_map, item)) elif isinstance(item, dict): return {k: forward_map(v) for k, v in item.items()} elif isinstance(item, tuple): return tuple(map(forward_map, item)) elif isinstance(item, set): return set(map(forward_map, list(item))) else: return item def reverse_map(item): if isinstance(item, Ideal_generic): return Ideal([reverse_map(g) for g in item.gens()]) elif isinstance(item, Polynomial): return item.map_coefficients(morphism) elif isinstance(item, MPolynomial): return item.map_coefficients(morphism) elif is_PolynomialSequence(item): return PolynomialSequence(map(reverse_map, item), immutable=item.is_immutable()) elif isinstance(item, list): return list(map(reverse_map, item)) elif isinstance(item, tuple): return tuple(map(reverse_map, item)) elif isinstance(item, set): return set(map(reverse_map, list(item))) else: return item args = forward_map(args) kwds = forward_map(kwds) return reverse_map(func(*args, **kwds))
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)