def min_reol_maybe_with3gens(data): forms = data.relatively_prime_3forms_maybe() d = data.brackets_dict if forms is None: return None F = FreeModule(R, 2) f, g, h = forms e0, e1 = F.gens() a = d[(g, h)] b = -d[(f, h)] c = d[(f, g)] f = e0 * c g = e1 * c h = -(a * e0 + b * e1) if c.degree() > 0: n = smodule(f, h) idl = sideal(b) m = squotient(n, idl) else: m = smodule(f, g) wts = data.weight_of_basis() mls = list(takewhile(lambda l: any(x != 0 for x in l), slist(smres(m, 0)))) mls = [[list(v) for v in list(l)] for l in mls] wts_of_mls = [] wts_of_mls.append([degree_vec(v, wts) for v in mls[0]]) for i, l in enumerate(mls[1:]): wts = wts_of_mls[i] wts_of_mls.append([degree_vec(v, wts) for v in l]) return (mls, wts_of_mls, (forms[0], forms[1], c))
def __init__(self, base_ring, coordinate): self.coordinate = coordinate self.Vector = flatsurf.Vector._unwrapped[self.coordinate] self._isomorphic_vector_space = FreeModule(base_ring, 2) if isinstance(base_ring, real_embedded_number_field.RealEmbeddedNumberField): self._isomorphic_vector_space = FreeModule(base_ring.number_field, 2) Parent.__init__(self, base_ring, category=FreeModules(base_ring)) self.register_coercion(self._isomorphic_vector_space) self._isomorphic_vector_space.register_conversion(ConversionVectorSpace(self))
def is_product(n, den_tuple): r""" INPUT: - ``n`` - number of variables - ``den_tuple`` - tuple of pairs ``(vector, power)`` TESTS:: sage: from surface_dynamics.misc.generalized_multiple_zeta_values import is_product sage: is_product(3, [((1,0,0),2), ((0,1,0),5), ((1,1,0),1), ((0,0,1),3)]) [(2, (((0, 1), 5), ((1, 0), 2), ((1, 1), 1))), (1, (((1), 3),))] sage: is_product(3, [((1,0,0),2), ((0,1,0),3), ((1,0,1),1), ((0,0,1),5)]) [(2, (((0, 1), 5), ((1, 0), 2), ((1, 1), 1))), (1, (((1), 3),))] sage: is_product(3, [((1,1,1),3)]) is None True """ D = DisjointSet(n) assert all(len(v) == n for v, p in den_tuple), (n, den_tuple) # 1. product structure for v, _ in den_tuple: i0 = 0 while not v[i0]: i0 += 1 i = i0 + 1 while i < n: if v[i]: D.union(i0, i) i += 1 if D.number_of_subsets() == 1: # no way to split variables return # split variables Rdict = D.root_to_elements_dict() keys = sorted(Rdict.keys()) key_indices = {k: i for i, k in enumerate(keys)} values = [Rdict[k] for k in keys] values_indices = [{v: i for i, v in enumerate(v)} for v in values] n_list = [len(J) for J in values] F = [FreeModule(ZZ, nn) for nn in n_list] new_terms = [[] for _ in range(len(Rdict))] for v, p in den_tuple: i0 = 0 while not v[i0]: i0 += 1 i0 = D.find(i0) assert all(D.find(i) == i0 for i in range(n) if v[i]), (i0, [D.find(i) for i in range(n) if v[i]]) k = key_indices[i0] vv = F[k]() for i in range(n): if v[i]: vv[values_indices[k][i]] = v[i] vv.set_immutable() new_terms[k].append((vv, p)) return list(zip(n_list, [tuple(sorted(terms)) for terms in new_terms]))
def __init__(self, surface): if isinstance(surface, TranslationSurface): base_ring = surface.base_ring() from flatsurf.geometry.pyflatsurf_conversion import to_pyflatsurf self._surface = to_pyflatsurf(surface) else: from flatsurf.geometry.pyflatsurf_conversion import sage_base_ring base_ring, _ = sage_base_ring(surface) self._surface = surface # A model of the vector space R² in libflatsurf, e.g., to represent the # vector associated to a saddle connection. self.V2 = pyflatsurf.vector.Vectors(base_ring) # We construct a spanning set of edges, that is a subset of the # edges that form a basis of H_1(S, Sigma; Z) # It comes together with a projection matrix t, m = self._spanning_tree() assert set(t.keys()) == set(f[0] for f in self._faces()) self.spanning_set = [] v = set(t.values()) for e in self._surface.edges(): if e.positive() not in v and e.negative() not in v: self.spanning_set.append(e) self.d = len(self.spanning_set) assert 3*self.d - 3 == self._surface.size() assert m.rank() == self.d m = m.transpose() # projection matrix from Z^E to H_1(S, Sigma; Z) in the basis # of spanning edges self.proj = matrix(ZZ, [r for r in m.rows() if not r.is_zero()]) self.Omega = self._intersection_matrix(t, self.spanning_set) self.V = FreeModule(self.V2.base_ring(), self.d) self.H = matrix(self.V2.base_ring(), self.d, 2) for i in range(self.d): s = self._surface.fromHalfEdge(self.spanning_set[i].positive()) self.H[i] = self.V2._isomorphic_vector_space(self.V2(s)) self.Hdual = self.Omega * self.H # Note that we don't use Sage vector spaces because they are usually # way too slow (in particular we avoid calling .echelonize()) self._U = matrix(self.V2._algebraic_ring(), self.d) if self._U.base_ring() is not QQ: from sage.all import next_prime self._good_prime = self._U.base_ring().ideal(next_prime(2**30)).factor()[0][0] self._Ubar = matrix(self._good_prime.residue_field(), self.d) self._U_rank = 0 self.update_tangent_space_from_vector(self.H.transpose()[0]) self.update_tangent_space_from_vector(self.H.transpose()[1])
def test_skipper_cpa_enc(skipper=Skipper4, kyber=Kyber, t=128, l=None, exhaustive=False): if not exhaustive: for i in range(t): pk, sk = kyber.key_gen(seed=i) m0 = random_vector(GF(2), kyber.n) c = skipper.enc(kyber, pk, m0, seed=i, l=l) m1 = kyber.dec(sk, c) assert(m0 == m1) else: # exhaustive test for i in range(16): pk, sk = kyber.key_gen(seed=i) for m0 in FreeModule(GF(2), kyber.n): c = skipper.enc(kyber, pk, m0, seed=i, l=l) m1 = kyber.dec(sk, c) assert(m0 == m1)
def boundaries(self): r""" Return the list of boundaries (ie sum of edges around a triangular face). These are elements of H_1(S, Sigma; Z). TESTS:: sage: from flatsurf import polygons, similarity_surfaces sage: from flatsurf import GL2ROrbitClosure # optional: pyflatsurf sage: from itertools import product sage: for a in range(1,5): # optional: pyflatsurf ....: for b in range(a, 5): ....: for c in range(b, 5): ....: if gcd([a, b, c]) != 1 or (a,b,c) == (1,1,2): ....: continue ....: T = polygons.triangle(a, b, c) ....: S = similarity_surfaces.billiard(T) ....: S = S.minimal_cover(cover_type="translation") ....: O = GL2ROrbitClosure(S) ....: for b in O.boundaries(): ....: assert (O.proj * b).is_zero() """ n = self._surface.size() V = FreeModule(ZZ, n) B = [] for (f1,f2,f3) in self._faces(): i1 = f1.index() s1 = -1 if i1%2 else 1 i2 = f2.index() s2 = -1 if i2%2 else 1 i3 = f3.index() s3 = -1 if i3%2 else 1 i1 = f1.edge().index() i2 = f2.edge().index() i3 = f3.edge().index() v = [0] * n v[i1] = 1 v[i2] = s1 * s2 v[i3] = s1 * s3 B.append(V(v)) B[-1].set_immutable() return B
def linear_forms(d): r""" EXAMPLES:: sage: from surface_dynamics.misc.generalized_multiple_zeta_values import linear_forms sage: list(linear_forms(1)) [(1)] sage: list(linear_forms(2)) [(1, 0), (0, 1), (1, 1)] sage: L1 = list(linear_forms(3)) sage: L2 = L1[:] sage: L2.sort(key = lambda x: x[::-1]) sage: assert L1 == L2 """ F = FreeModule(ZZ, d) for n in range(1, 2**d): v = F(ZZ(n).digits(2, padto=d)) v.set_immutable() yield v
class Vectors(UniqueRepresentation, Parent): r""" The subspace of the vector space `\mathbb{R}^2` modeled by vectors with entries in ``base_ring``. Unlike normal SageMath vector spaces, this space is backed by flatsur's ``Vector`` class with entries in ``coordinate``. EXAMPLES:: sage: from pyflatsurf.vector import Vectors sage: K = NumberField(x**2 - 2, 'a', embedding=sqrt(AA(2))) sage: V = Vectors(K) sage: v = V((1,K.gen() - 3/2)) sage: v (1, (a-3/2 ~ -0.085786438)) sage: VectorSpace(K, 2)(v) (1, a - 3/2) sage: from pyeantic import RealEmbeddedNumberField sage: R = RealEmbeddedNumberField(K) sage: Vectors(R) is V True sage: Vectors(R.renf) is V True sage: from gmpxxyy import mpz, mpq sage: Vectors(QQ) is Vectors(mpq) True sage: Vectors(ZZ) is Vectors(mpz) True """ Element = Vector @staticmethod def __classcall__(cls, base_ring): if base_ring in [ZZ, mpz]: base_ring = ZZ coordinate = mpz elif base_ring in [QQ, mpq]: base_ring = QQ coordinate = mpq elif isinstance(base_ring, real_embedded_number_field.RealEmbeddedNumberField): coordinate = cppyy.gbl.eantic.renf_elem_class elif isinstance(base_ring, eantic.renf_class): base_ring = real_embedded_number_field.RealEmbeddedNumberField(base_ring) coordinate = cppyy.gbl.eantic.renf_elem_class elif isinstance(base_ring, SageNumberField): base_ring = real_embedded_number_field.RealEmbeddedNumberField(base_ring) coordinate = cppyy.gbl.eantic.renf_elem_class elif isinstance(base_ring, ExactReals): coordinate = base_ring._element_factory else: raise NotImplementedError("unsupported base_ring {!r}".format(base_ring)) return super(Vectors, cls).__classcall__(cls, base_ring, coordinate) def __init__(self, base_ring, coordinate): self.coordinate = coordinate self.Vector = flatsurf.Vector._unwrapped[self.coordinate] self._isomorphic_vector_space = FreeModule(base_ring, 2) if isinstance(base_ring, real_embedded_number_field.RealEmbeddedNumberField): self._isomorphic_vector_space = FreeModule(base_ring.number_field, 2) Parent.__init__(self, base_ring, category=FreeModules(base_ring)) self.register_coercion(self._isomorphic_vector_space) self._isomorphic_vector_space.register_conversion(ConversionVectorSpace(self)) def _algebraic_ring(self): r""" Return the Sage algebraic ring which underlies our base ring; typically this is (an isomorphic ring to) the base ring itself but for non-algebraic rings such as ``ExactReals``, this gives the coefficient ring. EXAMPLES:: sage: from pyflatsurf.vector import Vectors sage: from pyexactreal import ExactReals sage: Vectors(QQ)._algebraic_ring() Rational Field sage: R = ExactReals() sage: Vectors(R)._algebraic_ring() Rational Field sage: K = NumberField(x^3 - 2, 'a', embedding=AA(2)**(1/3)) sage: Vectors(K)._algebraic_ring() Number Field in a with defining polynomial x^3 - 2 with a = 1.259921049894873? sage: R = ExactReals(K) sage: Vectors(R)._algebraic_ring() Number Field in a with defining polynomial x^3 - 2 with a = 1.259921049894873? """ algebraic_ring = self.base_ring() if isinstance(algebraic_ring, ExactReals): algebraic_ring = algebraic_ring.base_ring() if algebraic_ring not in [ZZ, QQ]: algebraic_ring = algebraic_ring.number_field return algebraic_ring def decomposition(self, vector, base=None): r""" Write ``vector`` as a shortest sum `v = \sum a_i v_i` where the `a_i` are coefficients from our ``base_ring()`` and the `v_i` are vectors with entries in ``base``. EXAMPLES:: sage: from pyflatsurf.vector import Vectors sage: V = Vectors(ZZ) sage: V.decomposition([13, 37]) [(1, (13, 37))] sage: V.decomposition([0, 0]) [] :: sage: from pyexactreal import ExactReals sage: R = ExactReals() sage: V = Vectors(R) sage: V.decomposition([R.random_element(), R.random_element()]) [(ℝ(0.178808…), (1, 0)), (ℝ(0.478968…), (0, 1))] :: sage: from pyeantic import RealEmbeddedNumberField sage: K = NumberField(x^3 - 2, 'a', embedding=AA(2)**(1/3)) sage: V = Vectors(K) sage: V.decomposition([0, 1]) [(1, (0, 1))] :: sage: R = ExactReals(K) sage: V = Vectors(R) sage: V.decomposition([K.random_element() * R.random_element(), R.random_element(), R.random_element()]) [(ℝ(0.621222…), (0, 1, 0)), (ℝ(0.673083…), (0, 0, 1)), (ℝ(0.782515…), (-1/2*a^2 - 4, 0, 0))] """ vector = tuple(self._to_coordinate(x) for x in vector) if not any(vector): return [] if base is None: base = self._algebraic_ring() if base is self.base_ring(): return [(self.base_ring().one(), tuple(map(base, vector)))] if hasattr(self.base_ring(), "number_field") and self.base_ring().number_field is base: return [(self.base_ring().one(), tuple(base(self.base_ring()(x)) for x in vector))] if isinstance(self.base_ring(), ExactReals): from functools import reduce span = type(vector[0].module()).span module = reduce(lambda x,y: span(x, y.module()), vector, vector[0].module()) vector = [entry.promote(module).coefficients() for entry in vector] vector = [[base(Vectors(base).base_ring()(c)) for c in coefficients] for coefficients in vector] V = base**len(vector) return [(self.base_ring()(module.gen(i)), tuple(V(coefficients))) for (i, coefficients) in enumerate(zip(*vector)) if any(coefficients)] raise NotImplementedError("cannot decompose vector in %s over %s"%(self, base)) def _to_coordinate(self, x): r""" Convert ``x`` to something that the flatsurf backend for this vector type understands. EXAMPLES:: sage: from pyflatsurf.vector import Vectors sage: V = Vectors(QQ) sage: type(V._to_coordinate(1)) <class cppyy.gbl.__gmp_expr<__mpz_struct[1],__mpz_struct[1]> at ...> sage: type(V._to_coordinate(1/2)) <class cppyy.gbl.__gmp_expr<__mpq_struct[1],__mpq_struct[1]> at ...> """ if isinstance(x, self.coordinate): return x if isinstance(x, cppyy.gbl.mpz_class): return x if isinstance(x, cppyy.gbl.mpq_class): return x if isinstance(self.base_ring(), real_embedded_number_field.RealEmbeddedNumberField): # We create a copy of the coordinate. Otherwise, cppyy (as of # 1.9.5) treats this as an r-value and moves x out even if it is # referenced elsewhere. value = self.base_ring()(x).renf_elem return type(value)(value) if isinstance(self.base_ring(), ExactReals): return self.base_ring()(x)._backend if x in ZZ: return cppyy.gbl.mpz_class(str(x)) if x in QQ: return cppyy.gbl.mpq_class(str(x)) raise NotImplementedError("Cannot convert %s to something the flatsurf backend understands yet, i.e., cannot convert a %s into a %s"%(x, type(x), type(self.coordinate))) def _repr_(self): r""" Return a printable representation of this vector space. EXAMPLES:: sage: from pyflatsurf.vector import Vectors sage: V = Vectors(QQ) sage: V Flatsurf Vectors over Rational Field """ return "Flatsurf Vectors over %r"%(self.base_ring(),) def an_element(self): return self((1,0)) def zero(self): r""" Return the zero vector. EXAMPLES:: sage: from pyflatsurf.vector import Vectors sage: V = Vectors(QQ) sage: V.zero() (0, 0) """ return self((0,0))
def is_reducible(n, den_tuple): r""" If (x1+x2+...+xd) is not present, use a linear relation to create it. Then try to kill using other forms. Then try to write as P(x1,x2,...,x_{d-1}) (x1+x2+...+xd) and recurse. Should solve all Tonrheim EXAMPLES:: sage: from surface_dynamics.misc.generalized_multiple_zeta_values import linear_forms, is_reducible sage: va, vb, vd, vc, ve, vf, vg = linear_forms(3) sage: is_reducible(3, ((va,3),(vb,3),(vc,3))) [(1, (((0, 0, 1), 3), ((0, 1, 1), 3), ((1, 1, 1), 3))), (1, (((0, 1, 0), 3), ((0, 1, 1), 3), ((1, 1, 1), 3))), (3, (((0, 0, 1), 2), ((0, 1, 1), 4), ((1, 1, 1), 3))), (3, (((0, 1, 0), 2), ((0, 1, 1), 4), ((1, 1, 1), 3))), ... (90, (((1, 0, 0), 1), ((1, 0, 1), 1), ((1, 1, 1), 7))), (90, (((0, 1, 0), 1), ((1, 1, 0), 1), ((1, 1, 1), 7))), (90, (((1, 0, 0), 1), ((1, 1, 0), 1), ((1, 1, 1), 7)))] """ F = FreeModule(ZZ, n) vmax = F([1] * n) vmax.set_immutable() if len(den_tuple) == 1: return # force the max vector (1,1,...,1) to appear imax = None for i, (v, p) in enumerate(den_tuple): if v == vmax: imax = i break if imax is None: imax = len(den_tuple) den_tuple = den_tuple + ((vmax, 0), ) if imax != len(den_tuple) - 1: den_tuple = list(den_tuple) den_tuple.append(den_tuple.pop(imax)) den_tuple = tuple(den_tuple) imax = len(den_tuple) - 1 assert den_tuple[imax][0] == vmax M = matrix(QQ, [v for v, p in den_tuple if v != vmax]) try: relation = M.solve_left(vmax) except ValueError: if den_tuple[-1][1] == 0: return den_tuple2 = den_tuple[:-1] variables = set().union(*[[i for i in range(n) if v[i]] for v, p in den_tuple2]) if len(variables) == n: return killed = [(1, den_tuple)] else: killed = kill_relation(n, den_tuple, imax, list(relation) + [0]) ans = defaultdict(QQ) for coeff, den_tuple2 in killed: assert den_tuple2[-1][0] == vmax pmax = den_tuple2[-1][1] assert pmax den_tuple2 = den_tuple2[:-1] variables = set().union(*[[i for i in range(n) if v[i]] for v, p in den_tuple2]) if len(variables) == n: # removing the maximal vector (1,1,...,1) is not enough to make a variable disappear data = is_reducible(n, den_tuple2) if data is None: data = [(1, den_tuple2)] else: # less variables! nn = len(variables) variables = sorted(variables) new_indices = {j: i for i, j in enumerate(variables)} G = FreeModule(ZZ, nn) new_den_tuple2 = [] for v, p in den_tuple2: vv = G([v[i] for i in variables]) vv.set_immutable() new_den_tuple2.append((vv, p)) new_den_tuple2 = tuple(new_den_tuple2) data = is_reducible(nn, new_den_tuple2) if data is None: data = [(1, new_den_tuple2)] # lift to the n variables version new_data = [] for coeff3, den_tuple3 in data: den_tuple3 = [(F([ v[new_indices[j]] if j in new_indices else 0 for j in range(n) ]), p) for v, p in den_tuple3] for v, p in den_tuple3: v.set_immutable() new_data.append((coeff3, tuple(sorted(den_tuple3)))) data = new_data # update the answer for coeff3, den_tuple3 in data: imax = None for i, (v, p) in enumerate(den_tuple3): if v == vmax: imax = i break if imax is None: den_tuple3 = den_tuple3 + ((vmax, pmax), ) else: den_tuple3 = den_tuple3[:imax] + ( (vmax, den_tuple3[imax][1] + pmax), ) + den_tuple3[imax + 1:] ans[den_tuple3] += coeff * coeff3 if len(ans) > 1: return [(coeff, den_tuple) for den_tuple, coeff in ans.items()]