def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time """ self._matrix = coxeter_matrix self._index_set = index_set n = ZZ(coxeter_matrix.nrows()) MS = MatrixSpace(base_ring, n, sparse=True) # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty if base_ring is UniversalCyclotomicField(): val = lambda x: base_ring.gen(2*x) + ~base_ring.gen(2*x) if x != -1 else base_ring(2) else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: base_ring(2*cos(pi / x)) if x != -1 else base_ring(2) gens = [MS.one() + MS({(i, j): val(coxeter_matrix[i, j]) for j in range(n)}) for i in range(n)] FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring, gens, category=CoxeterGroups())
def _create_prod_set(self, image, n, p): """ A helper function. Creates all products of matrices up to and including length `p`. """ import itertools M = MatrixSpace(self.base_field().algebraic_closure(), n, sparse=True) for i in range(1, p + 1): for matrices in itertools.product(image, repeat=i): result = M.one() for m in matrices: result = result * M(m) yield result
def is_irred_rep(self, image, n, force=False): """ Returns `True` if the map generated by mapping the generators to the matrices defined in `image` is an `n`-dimensional irreducible representation of `self`, and `False` otherwise. Like above, the entries of `image` must be `n`-by-`n` matrices with entries in the algebraic closure of the base field of `self`. Its length must match the number of generators of `self.` Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError( 'Base field must be computable. If %s is computable' % self.base_field() + ' then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') if not self.is_rep(image, n): return False from sage.matrix.all import Matrix from sage.rings.number_field.number_field import is_NumberField import math M = MatrixSpace(self.base_field().algebraic_closure(), n, sparse=True) image = [M(image[i]).list() for i in range(len(image))] if n <= 6 and is_NumberField(self.base_field()): p = 2 * n else: p = int( math.floor(n * math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) + n / float(2) - 3)) prod_set = list(self._create_prod_set(image, n, p)) prod_set.append(M.one()) vector = [mat.list() for mat in prod_set] return 0 not in Matrix(vector).echelon_form().diagonal()
def HS_minimal(f, return_transformation=False, D=None): r""" Compute a minimal model for the given projective dynamical system. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representative with minimal resultant in the conjugacy class of ``f`` returned. INPUT: - ``f`` -- dynamical system on the projective line with minimal resultant - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the `PGL_2` transformation to conjugate this map to the calculated models - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: - a dynamical system - (optional) a `2 \times 2` matrix EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y]) sage: m = matrix(QQ,2,2,[5,1,0,1]) sage: g = f.conjugate(m) sage: g.normalize_coordinates() sage: g.resultant().factor() 2^4 * 3^4 * 5^12 sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_minimal sage: HS_minimal(g).resultant().factor() 2^4 * 3^4 sage: HS_minimal(g, D=[2]).resultant().factor() 2^4 * 3^4 * 5^12 sage: F,m = HS_minimal(g, return_transformation=True) sage: g.conjugate(m) == F True """ F = copy(f) d = F.degree() F.normalize_coordinates() MS = MatrixSpace(ZZ, 2, 2) m = MS.one() prev = copy(m) res = ZZ(F.resultant()) if D is None: D = res.prime_divisors() # minimize for each prime for p in D: vp = res.valuation(p) minimal = False while not minimal: if (d % 2 == 0 and vp < d) or (d % 2 == 1 and vp < 2 * d): # must be minimal minimal = True break minimal = True t = MS([1, 0, 0, p]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = F1.resultant() vp1 = res1.valuation(p) if vp1 < vp: # check if smaller F = F1 vp = vp1 m = m * t # keep track of conjugation minimal = False else: # still search for smaller for b in range(p): t = matrix(ZZ, 2, 2, [p, b, 0, 1]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 < vp: # check if smaller F = F1 m = m * t # keep track of transformation minimal = False vp = vp1 break # exit for loop if return_transformation: return F, m return F
def BM_all_minimal(vp, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. This function modifies the Bruin-Molnar algorithm ([BM2012]_) to solve in the inequalities as ``<=`` instead of ``<``. Among the list of solutions is all conjugations that preserve the resultant. From that list the `SL(2,\ZZ)` orbits are identified and one representative from each orbit is returned. This function assumes that the given model is a minimal model. INPUT: - ``vp`` -- a minimal model of a dynamical system on the projective line - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 13^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: BM_all_minimal(f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 169*y^3 : x*y^2), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (13*x^3 - y^3 : x*y^2)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: BM_all_minimal(f, D=[3]) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x*y^2), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (3*x^3 - 4*y^3 : x*y^2)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 4^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: cl = BM_all_minimal(f, return_transformation=True) sage: all(f.conjugate(m) == g for g, m in cl) True """ mp = copy(vp) mp.normalize_coordinates() BR = mp.domain().base_ring() MS = MatrixSpace(QQ, 2) M_Id = MS.one() d = mp.degree() F, G = list(mp) #coordinate polys aff_map = mp.dehomogenize(1) f, g = aff_map[0].numerator(), aff_map[0].denominator() z = aff_map.domain().gen(0) dg = f.parent()(g).degree() Res = mp.resultant() ##### because of how the bound is compute in lemma 3.3 from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine h = f - z * g A = AffineSpace(BR, 1, h.parent().variable_name()) res = DynamicalSystem_affine([h / g], domain=A).homogenize(1).resultant() if D is None: D = ZZ(Res).prime_divisors() # get the conjugations for each prime independently # these are returning (p,k,b) so that the matrix is [p^k,b,0,1] all_pM = [] for p in D: # all_orbits used to scale inequalities to equalities all_pM.append(Min(mp, p, res, M_Id, all_orbits=True)) # need the identity for each prime if [p, 0, 0] not in all_pM[-1]: all_pM[-1].append([p, 0, 0]) #combine conjugations for all primes all_M = [M_Id] for prime_data in all_pM: #these are (p,k,b) so that the matrix is [p^k,b,0,1] new_M = [] if prime_data: p = prime_data[0][0] for m in prime_data: mat = MS([m[0]**m[1], m[2], 0, 1]) new_map = mp.conjugate(mat) new_map.normalize_coordinates() # make sure the resultant didn't change and that it is a different SL(2,ZZ) orbit if (mat == M_Id) or (new_map.resultant().valuation(p) == Res.valuation(p) and mat.det() not in [1, -1]): new_M.append(m) if new_M: all_M = [ m1 * MS([m[0]**m[1], m[2], 0, 1]) for m1 in all_M for m in new_M ] #get all models with same resultant all_maps = [] for M in all_M: new_map = mp.conjugate(M) new_map.normalize_coordinates() if not [new_map, M] in all_maps: all_maps.append([new_map, M]) #Split into conjugacy classes #We just keep track of the two matrices that come from #the original to get the conjugation that goes between these!! classes = [] for funct, mat in all_maps: if not classes: classes.append([funct, mat]) else: found = False for Func, Mat in classes: #get conjugation M = mat.inverse() * Mat assert funct.conjugate(M) == Func if M.det() in [1, -1]: #same SL(2,Z) orbit found = True break if found is False: classes.append([funct, mat]) if return_transformation: return classes else: return [funct for funct, matr in classes]
def HS_all_minimal(f, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representative in each distinct `SL(2,\ZZ)` orbit is returned. The input ``f`` must have minimal resultant in its conguacy class. INPUT: - ``f`` -- dynamical system on the projective line with minimal resultant - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: List of pairs ``[f, m]``, where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal sage: HS_all_minimal(f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (9*x^3 - 12*y^3 : 9*x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (4*x^3 - 18*y^3 : 4*x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (36*x^3 - 6*y^3 : 36*x^2*y)] sage: HS_all_minimal(f, D=[3]) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (9*x^3 - 12*y^3 : 9*x^2*y)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal sage: cl = HS_all_minimal(f, return_transformation=True) sage: all([f.conjugate(m) == g for g,m in cl]) True """ MS = MatrixSpace(ZZ, 2) m = MS.one() F = copy(f) F.normalize_coordinates() if F.degree() == 1: raise ValueError("function must be degree at least 2") if f.degree() % 2 == 0: #there is only one orbit for even degree if return_transformation: return [[f, m]] else: return [f] if D is None: res = ZZ(F.resultant()) D = res.prime_divisors() M = [[F, m]] for p in D: # get p-orbits Mp = HS_all_minimal_p(p, F, m, return_transformation=True) # combine with previous orbits representatives M = [[g.conjugate(t), t*s] for g,s in M for G,t in Mp] if return_transformation: return M else: return [funct for funct, matr in M]
def HS_all_minimal_p(p, f, m=None, return_transformation=False): r""" Find a representative in each distinct `SL(2,\ZZ)` orbit with minimal `p`-resultant. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representatives in each distinct `SL(2,\ZZ)` orbit with minimal valuation with respect to the prime ``p`` is returned. The input ``f`` must have minimal resultant in its conguacy class. INPUT: - ``p`` -- a prime - ``f`` -- dynamical system on the projective line with minimal resultant - ``m`` -- (optional) `2 \times 2` matrix associated with ``f`` - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model OUTPUT: List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^5 - 6^4*y^5, x^2*y^3]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal_p sage: HS_all_minimal_p(2, f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^5 - 1296*y^5 : x^2*y^3), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (4*x^5 - 162*y^5 : x^2*y^3)] sage: cl = HS_all_minimal_p(2, f, return_transformation=True) sage: all([f.conjugate(m) == g for g,m in cl]) True """ count = 0 prev = 0 # no exclusions F = copy(f) res = ZZ(F.resultant()) vp = res.valuation(p) MS = MatrixSpace(ZZ, 2) if m is None: m = MS.one() if f.degree() % 2 == 0 or vp == 0: # there is only one orbit for even degree # nothing to do if the prime doesn't divide the resultant if return_transformation: return [[f, m]] else: return [f] to_do = [[F, m, prev]] # repns left to check reps = [[F, m]] # orbit representatives for f while to_do: F, m, prev = to_do.pop() # there are at most two directions preserving the resultant if prev == 0: count = 0 else: count = 1 if prev != 2: # [p,a,0,1] t = MS([1, 0, 0, p]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 == vp: count += 1 # we have a new representative reps.append([F1, m*t]) # need to check if it has any neighbors to_do.append([F1, m*t, 1]) for b in range(p): if not (b == 0 and prev == 1): t = MS([p, b, 0, 1]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 == vp: count += 1 # we have a new representative reps.append([F1, m*t]) # need to check if it has any neighbors to_do.append([F1, m*t, 2]) if count >= 2: # at most two neighbors break if return_transformation: return reps else: return [funct for funct, matr in reps]
def HS_minimal(f, return_transformation=False, D=None): r""" Compute a minimal model for the given projective dynamical system. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representative with minimal resultant in the conjugacy class of ``f`` returned. INPUT: - ``f`` -- dynamical system on the projective line with minimal resultant - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the `PGL_2` transformation to conjugate this map to the calculated models - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: - a dynamical system - (optional) a `2 \times 2` matrix EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y]) sage: m = matrix(QQ,2,2,[5,1,0,1]) sage: g = f.conjugate(m) sage: g.normalize_coordinates() sage: g.resultant().factor() 2^4 * 3^4 * 5^12 sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_minimal sage: HS_minimal(g).resultant().factor() 2^4 * 3^4 sage: HS_minimal(g, D=[2]).resultant().factor() 2^4 * 3^4 * 5^12 sage: F,m = HS_minimal(g, return_transformation=True) sage: g.conjugate(m) == F True """ F = copy(f) d = F.degree() F.normalize_coordinates() MS = MatrixSpace(ZZ, 2, 2) m = MS.one() prev = copy(m) res = ZZ(F.resultant()) if D is None: D = res.prime_divisors() # minimize for each prime for p in D: vp = res.valuation(p) minimal = False while not minimal: if (d % 2 == 0 and vp < d) or (d % 2 == 1 and vp < 2 * d): # must be minimal minimal = True break minimal = True t = MS([1, 0, 0, p]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = F1.resultant() vp1 = res1.valuation(p) if vp1 < vp: # check if smaller F = F1 vp = vp1 m = m * t # keep track of conjugation minimal = False else: # still search for smaller for b in range(p): t = matrix(ZZ,2,2,[p, b, 0, 1]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 < vp: # check if smaller F = F1 m = m * t # keep track of transformation minimal = False vp = vp1 break # exit for loop if return_transformation: return F, m return F
def BM_all_minimal(vp, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. This function modifies the Bruin-Molnar algorithm ([BM2012]_) to solve in the inequalities as ``<=`` instead of ``<``. Among the list of solutions is all conjugations that preserve the resultant. From that list the `SL(2,\ZZ)` orbits are identified and one representative from each orbit is returned. This function assumes that the given model is a minimal model. INPUT: - ``vp`` -- a minimal model of a dynamical system on the projective line - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 13^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: BM_all_minimal(f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 169*y^3 : x*y^2), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (13*x^3 - y^3 : x*y^2)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: BM_all_minimal(f, D=[3]) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x*y^2), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (3*x^3 - 4*y^3 : x*y^2)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 4^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import BM_all_minimal sage: cl = BM_all_minimal(f, return_transformation=True) sage: all([f.conjugate(m) == g for g,m in cl]) True """ mp = copy(vp) mp.normalize_coordinates() BR = mp.domain().base_ring() MS = MatrixSpace(QQ, 2) M_Id = MS.one() d = mp.degree() F, G = list(mp) #coordinate polys aff_map = mp.dehomogenize(1) f, g = aff_map[0].numerator(), aff_map[0].denominator() z = aff_map.domain().gen(0) dg = f.parent()(g).degree() Res = mp.resultant() ##### because of how the bound is compute in lemma 3.3 from sage.dynamics.arithmetic_dynamics.affine_ds import DynamicalSystem_affine h = f - z*g A = AffineSpace(BR, 1, h.parent().variable_name()) res = DynamicalSystem_affine([h/g], domain=A).homogenize(1).resultant() if D is None: D = ZZ(Res).prime_divisors() # get the conjugations for each prime independently # these are returning (p,k,b) so that the matrix is [p^k,b,0,1] all_pM = [] for p in D: # all_orbits used to scale inequalities to equalities all_pM.append(Min(mp, p, res, M_Id, all_orbits=True)) # need the identity for each prime if [p, 0, 0] not in all_pM[-1]: all_pM[-1].append([p, 0, 0]) #combine conjugations for all primes all_M = [M_Id] for prime_data in all_pM: #these are (p,k,b) so that the matrix is [p^k,b,0,1] new_M = [] if prime_data: p = prime_data[0][0] for m in prime_data: mat = MS([m[0]**m[1], m[2], 0, 1]) new_map = mp.conjugate(mat) new_map.normalize_coordinates() # make sure the resultant didn't change and that it is a different SL(2,ZZ) orbit if (mat == M_Id) or (new_map.resultant().valuation(p) == Res.valuation(p) and mat.det() not in [1,-1]): new_M.append(m) if new_M: all_M = [m1 * MS([m[0]**m[1], m[2], 0, 1]) for m1 in all_M for m in new_M] #get all models with same resultant all_maps = [] for M in all_M: new_map = mp.conjugate(M) new_map.normalize_coordinates() if not [new_map, M] in all_maps: all_maps.append([new_map, M]) #Split into conjugacy classes #We just keep track of the two matrices that come from #the original to get the conjugation that goes between these!! classes = [] for funct, mat in all_maps: if not classes: classes.append([funct, mat]) else: found = False for Func, Mat in classes: #get conjugation M = mat.inverse() * Mat assert funct.conjugate(M) == Func if M.det() in [1,-1]: #same SL(2,Z) orbit found = True break if found is False: classes.append([funct, mat]) if return_transformation: return classes else: return [funct for funct, matr in classes]
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty if base_ring is UniversalCyclotomicField(): val = lambda x: base_ring.gen(2*x) + ~base_ring.gen(2*x) if x != -1 else base_ring(2) else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: base_ring(2*cos(pi / x)) if x != -1 else base_ring(2) gens = [MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[index_set[i], index_set[j]]) for j in range(n)}, coerce=True, copy=True) for i in range(n)] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty E = UniversalCyclotomicField().gen if base_ring is UniversalCyclotomicField(): def val(x): if x == -1: return 2 else: return E(2 * x) + ~E(2 * x) elif is_QuadraticField(base_ring): def val(x): if x == -1: return 2 else: return base_ring( (E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x == -1: return 2 else: return base_ring(2 * cos(pi / x)) gens = [ MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[index_set[i], index_set[j]]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] # Make the generators dense matrices for consistency and speed gens = [g.dense_matrix() for g in gens] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() self._index_set_inverse = { i: ii for ii, i in enumerate(self._matrix.index_set()) } FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. The authors can be reached at: [email protected] and [email protected]. ==================================================================== """ from sage.all import QQ from sage.matrix.matrix_space import MatrixSpace from sage.matrix.special import block_diagonal_matrix M = MatrixSpace(QQ, 2) one = M.one() matrices = [M.random_element() for i in range(2000)] def test_m_tensor_one(): for m in matrices: m.tensor_product(one, subdivide=False) def test_one_tensor_m(): for m in matrices: one.tensor_product(m, subdivide=False) def test_m_tensor_one_subdivide(): for m in matrices:
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix self._index_set = index_set n = ZZ(coxeter_matrix.nrows()) # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty if base_ring is UniversalCyclotomicField(): val = lambda x: base_ring.gen(2 * x) + ~base_ring.gen(2 * x) if x != -1 else base_ring(2) else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: base_ring(2 * cos(pi / x)) if x != -1 else base_ring(2) gens = [ MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[i, j]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. # This describes the bilinear form corresponding to this # Coxeter system, and might lead us out of our base ring. base_field = base_ring.fraction_field() MS2 = MatrixSpace(base_field, n, sparse=True) MC2 = MS2._get_matrix_class() self._bilinear = MC2( MS2, entries={ (i, j): val(coxeter_matrix[i, j]) / base_field(-2) for i in range(n) for j in range(n) if coxeter_matrix[i, j] != 2 }, coerce=True, copy=True, ) self._bilinear.set_immutable() category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. is_finite = self._finite_recognition() if is_finite: category = category.Finite() else: category = category.Infinite() FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring, gens, category=category)
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite irreducible coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite irreducible coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite irreducible coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite irreducible coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) one = MS.one() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty E = UniversalCyclotomicField().gen if base_ring is UniversalCyclotomicField(): def val(x): if x == -1: return 2 else: return E(2 * x) + ~E(2 * x) elif is_QuadraticField(base_ring): def val(x): if x == -1: return 2 else: return base_ring((E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x == -1: return 2 else: return base_ring(2 * cos(pi / x)) gens = [one + MS([SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]])) for j in range(n)]) for i in range(n)] # Make the generators dense matrices for consistency and speed gens = [g.dense_matrix() for g in gens] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() if all(self._matrix._matrix[i, j] == 2 for i in range(n) for j in range(i)): category = category.Commutative() if self._matrix.is_irreducible(): category = category.Irreducible() self._index_set_inverse = {i: ii for ii, i in enumerate(self._matrix.index_set())} FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
def HS_all_minimal_p(p, f, m=None, return_transformation=False): r""" Find a representative in each distinct `SL(2,\ZZ)` orbit with minimal `p`-resultant. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representatives in each distinct `SL(2,\ZZ)` orbit with minimal valuation with respect to the prime ``p`` is returned. The input ``f`` must have minimal resultant in its conjugacy class. INPUT: - ``p`` -- a prime - ``f`` -- dynamical system on the projective line with minimal resultant - ``m`` -- (optional) `2 \times 2` matrix associated with ``f`` - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model OUTPUT: List of pairs ``[f, m]`` where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^5 - 6^4*y^5, x^2*y^3]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal_p sage: HS_all_minimal_p(2, f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^5 - 1296*y^5 : x^2*y^3), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (4*x^5 - 162*y^5 : x^2*y^3)] sage: cl = HS_all_minimal_p(2, f, return_transformation=True) sage: all(f.conjugate(m) == g for g, m in cl) True """ count = 0 prev = 0 # no exclusions F = copy(f) res = ZZ(F.resultant()) vp = res.valuation(p) MS = MatrixSpace(ZZ, 2) if m is None: m = MS.one() if f.degree() % 2 == 0 or vp == 0: # there is only one orbit for even degree # nothing to do if the prime doesn't divide the resultant if return_transformation: return [[f, m]] else: return [f] to_do = [[F, m, prev]] # repns left to check reps = [[F, m]] # orbit representatives for f while to_do: F, m, prev = to_do.pop() # there are at most two directions preserving the resultant if prev == 0: count = 0 else: count = 1 if prev != 2: # [p,a,0,1] t = MS([1, 0, 0, p]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 == vp: count += 1 # we have a new representative reps.append([F1, m * t]) # need to check if it has any neighbors to_do.append([F1, m * t, 1]) for b in range(p): if not (b == 0 and prev == 1): t = MS([p, b, 0, 1]) F1 = F.conjugate(t) F1.normalize_coordinates() res1 = ZZ(F1.resultant()) vp1 = res1.valuation(p) if vp1 == vp: count += 1 # we have a new representative reps.append([F1, m * t]) # need to check if it has any neighbors to_do.append([F1, m * t, 2]) if count >= 2: # at most two neighbors break if return_transformation: return reps else: return [funct for funct, matr in reps]
def HS_all_minimal(f, return_transformation=False, D=None): r""" Determine a representative in each `SL(2,\ZZ)` orbit with minimal resultant. This function implements the algorithm in Hutz-Stoll [HS2018]_. A representative in each distinct `SL(2,\ZZ)` orbit is returned. The input ``f`` must have minimal resultant in its conjugacy class. INPUT: - ``f`` -- dynamical system on the projective line with minimal resultant - ``return_transformation`` -- (default: ``False``) boolean; this signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to the calculated minimal model - ``D`` -- a list of primes, in case one only wants to check minimality at those specific primes OUTPUT: List of pairs ``[f, m]``, where ``f`` is a dynamical system and ``m`` is a `2 \times 2` matrix. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x^2*y]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal sage: HS_all_minimal(f) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (9*x^3 - 12*y^3 : 9*x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (4*x^3 - 18*y^3 : 4*x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (36*x^3 - 6*y^3 : 36*x^2*y)] sage: HS_all_minimal(f, D=[3]) [Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^3 - 36*y^3 : x^2*y), Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (9*x^3 - 12*y^3 : 9*x^2*y)] :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem([x^3 - 6^2*y^3, x*y^2]) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import HS_all_minimal sage: cl = HS_all_minimal(f, return_transformation=True) sage: all(f.conjugate(m) == g for g, m in cl) True """ MS = MatrixSpace(ZZ, 2) m = MS.one() F = copy(f) F.normalize_coordinates() if F.degree() == 1: raise ValueError("function must be degree at least 2") if f.degree() % 2 == 0: #there is only one orbit for even degree if return_transformation: return [[f, m]] else: return [f] if D is None: res = ZZ(F.resultant()) D = res.prime_divisors() M = [[F, m]] for p in D: # get p-orbits Mp = HS_all_minimal_p(p, F, m, return_transformation=True) # combine with previous orbits representatives M = [[g.conjugate(t), t * s] for g, s in M for G, t in Mp] if return_transformation: return M else: return [funct for funct, matr in M]
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix self._index_set = index_set n = ZZ(coxeter_matrix.nrows()) # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) MC = MS._get_matrix_class() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty if base_ring is UniversalCyclotomicField(): val = lambda x: base_ring.gen(2 * x) + ~base_ring.gen( 2 * x) if x != -1 else base_ring(2) else: from sage.functions.trig import cos from sage.symbolic.constants import pi val = lambda x: base_ring(2 * cos(pi / x) ) if x != -1 else base_ring(2) gens = [ MS.one() + MC(MS, entries={(i, j): val(coxeter_matrix[i, j]) for j in range(n)}, coerce=True, copy=True) for i in range(n) ] # Compute the matrix with entries `- \cos( \pi / m_{ij} )`. # This describes the bilinear form corresponding to this # Coxeter system, and might lead us out of our base ring. base_field = base_ring.fraction_field() MS2 = MatrixSpace(base_field, n, sparse=True) MC2 = MS2._get_matrix_class() self._bilinear = MC2(MS2, entries={ (i, j): val(coxeter_matrix[i, j]) / base_field(-2) for i in range(n) for j in range(n) if coxeter_matrix[i, j] != 2 }, coerce=True, copy=True) self._bilinear.set_immutable() category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. is_finite = self._finite_recognition() if is_finite: category = category.Finite() else: category = category.Infinite() FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring, gens, category=category)