def _LKB_matrix_(self, braid, variab): """ Compute the Lawrence-Krammer-Bigelow representation matrix. The variables of the matrix must be given. This actual computation is done in this helper method for caching purposes. INPUT: - ``braid`` -- tuple of integers. The Tietze list of the braid. - ``variab`` -- string. the names of the variables that will appear in the matrix. They must be given as a string, separated by a comma OUTPUT: The LKB matrix of the braid, with respect to the variables. TESTS:: sage: B=BraidGroup(3) sage: B._LKB_matrix_((2, 1, 2), 'x, y') [ 0 -x^4*y + x^3*y -x^4*y] [ 0 -x^3*y 0] [ -x^2*y x^3*y - x^2*y 0] sage: B._LKB_matrix_((1, 2, 1), 'x, y') [ 0 -x^4*y + x^3*y -x^4*y] [ 0 -x^3*y 0] [ -x^2*y x^3*y - x^2*y 0] sage: B._LKB_matrix_((-1, -2, -1, 2, 1, 2), 'x, y') [1 0 0] [0 1 0] [0 0 1] """ n = self.strands() if len(braid)>1: A = self._LKB_matrix_(braid[:1], variab) for i in braid[1:]: A = A*self._LKB_matrix_((i,), variab) return A l = list(Set(range(n)).subsets(2)) R = LaurentPolynomialRing(IntegerRing(), variab) q = R.gens()[0] t = R.gens()[1] if len(braid)==0: return identity_matrix(R, len(l), sparse=True) A = matrix(R, len(l), sparse=True) if braid[0]>0: i = braid[0]-1 for m in range(len(l)): j = min(l[m]) k = max(l[m]) if i==j-1: A[l.index(Set([i, k])), m] = q A[l.index(Set([i, j])), m] = q*q-q A[l.index(Set([j, k])), m] = 1-q elif i==j and not j==k-1: A[l.index(Set([j, k])), m] = 0 A[l.index(Set([j+1, k])), m] = 1 elif k-1==i and not k-1==j: A[l.index(Set([j, i])), m] = q A[l.index(Set([j, k])), m] = 1-q A[l.index(Set([i, k])), m] = (1-q)*q*t elif i==k: A[l.index(Set([j, k])), m] = 0 A[l.index(Set([j, k+1])), m] = 1 elif i==j and j==k-1: A[l.index(Set([j, k])), m] = -t*q*q else: A[l.index(Set([j, k])), m] = 1 return A else: i = -braid[0]-1 for m in range(len(l)): j = min(l[m]) k = max(l[m]) if i==j-1: A[l.index(Set([j-1, k])), m] = 1 elif i==j and not j==k-1: A[l.index(Set([j+1, k])), m] = q**(-1) A[l.index(Set([j, k])), m] = 1-q**(-1) A[l.index(Set([j, j+1])), m] = t**(-1)*q**(-1)-t**(-1)*q**(-2) elif k-1==i and not k-1==j: A[l.index(Set([j, k-1])), m] = 1 elif i==k: A[l.index(Set([j, k+1])), m] = q**(-1) A[l.index(Set([j, k])), m] = 1-q**(-1) A[l.index(Set([k, k+1])), m] = -q**(-1)+q**(-2) elif i==j and j==k-1: A[l.index(Set([j, k])), m] = -t**(-1)*q**(-2) else: A[l.index(Set([j, k])), m] = 1 return A
def Omega_ge(a, exponents): r""" Return `\Omega_{\ge}` of the expression specified by the input. To be more precise, calculate .. MATH:: \Omega_{\ge} \frac{\mu^a}{ (1 - z_0 \mu^{e_0}) \dots (1 - z_{n-1} \mu^{e_{n-1}})} and return its numerator and a factorization of its denominator. Note that `z_0`, ..., `z_{n-1}` only appear in the output, but not in the input. INPUT: - ``a`` -- an integer - ``exponents`` -- a tuple of integers OUTPUT: A pair representing a quotient as follows: Its first component is the numerator as a Laurent polynomial, its second component a factorization of the denominator as a tuple of Laurent polynomials, where each Laurent polynomial `z` represents a factor `1 - z`. The parents of these Laurent polynomials is always a Laurent polynomial ring in `z_0`, ..., `z_{n-1}` over `\ZZ`, where `n` is the length of ``exponents``. EXAMPLES:: sage: from sage.rings.polynomial.omega import Omega_ge sage: Omega_ge(0, (1, -2)) (1, (z0, z0^2*z1)) sage: Omega_ge(0, (1, -3)) (1, (z0, z0^3*z1)) sage: Omega_ge(0, (1, -4)) (1, (z0, z0^4*z1)) sage: Omega_ge(0, (2, -1)) (z0*z1 + 1, (z0, z0*z1^2)) sage: Omega_ge(0, (3, -1)) (z0*z1^2 + z0*z1 + 1, (z0, z0*z1^3)) sage: Omega_ge(0, (4, -1)) (z0*z1^3 + z0*z1^2 + z0*z1 + 1, (z0, z0*z1^4)) sage: Omega_ge(0, (1, 1, -2)) (-z0^2*z1*z2 - z0*z1^2*z2 + z0*z1*z2 + 1, (z0, z1, z0^2*z2, z1^2*z2)) sage: Omega_ge(0, (2, -1, -1)) (z0*z1*z2 + z0*z1 + z0*z2 + 1, (z0, z0*z1^2, z0*z2^2)) sage: Omega_ge(0, (2, 1, -1)) (-z0*z1*z2^2 - z0*z1*z2 + z0*z2 + 1, (z0, z1, z0*z2^2, z1*z2)) :: sage: Omega_ge(0, (2, -2)) (-z0*z1 + 1, (z0, z0*z1, z0*z1)) sage: Omega_ge(0, (2, -3)) (z0^2*z1 + 1, (z0, z0^3*z1^2)) sage: Omega_ge(0, (3, 1, -3)) (-z0^3*z1^3*z2^3 + 2*z0^2*z1^3*z2^2 - z0*z1^3*z2 + z0^2*z2^2 - 2*z0*z2 + 1, (z0, z1, z0*z2, z0*z2, z0*z2, z1^3*z2)) :: sage: Omega_ge(0, (3, 6, -1)) (-z0*z1*z2^8 - z0*z1*z2^7 - z0*z1*z2^6 - z0*z1*z2^5 - z0*z1*z2^4 + z1*z2^5 - z0*z1*z2^3 + z1*z2^4 - z0*z1*z2^2 + z1*z2^3 - z0*z1*z2 + z0*z2^2 + z1*z2^2 + z0*z2 + z1*z2 + 1, (z0, z1, z0*z2^3, z1*z2^6)) TESTS:: sage: Omega_ge(0, (2, 2, 1, 1, 1, 1, 1, -1, -1))[0].number_of_terms() # long time 27837 :: sage: Omega_ge(1, (2,)) (1, (z0,)) """ import logging logger = logging.getLogger(__name__) logger.info('Omega_ge: a=%s, exponents=%s', a, exponents) from sage.arith.misc import lcm from sage.arith.srange import srange from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.number_field.number_field import CyclotomicField if not exponents or any(e == 0 for e in exponents): raise NotImplementedError rou = sorted(set(abs(e) for e in exponents) - set([1])) ellcm = lcm(rou) B = CyclotomicField(ellcm, 'zeta') zeta = B.gen() z_names = tuple('z{}'.format(i) for i in range(len(exponents))) L = LaurentPolynomialRing(B, ('t', ) + z_names, len(z_names) + 1) t = L.gens()[0] Z = LaurentPolynomialRing(ZZ, z_names, len(z_names)) powers = {i: L(zeta**(ellcm // i)) for i in rou} powers[2] = L(-1) powers[1] = L(1) exponents_and_values = tuple( (e, tuple(powers[abs(e)]**j * z for j in srange(abs(e)))) for z, e in zip(L.gens()[1:], exponents)) x = tuple(v for e, v in exponents_and_values if e > 0) y = tuple(v for e, v in exponents_and_values if e < 0) def subs_power(expression, var, exponent): r""" Substitute ``var^exponent`` by ``var`` in ``expression``. It is assumed that ``var`` only occurs with exponents divisible by ``exponent``. """ p = tuple(var.dict().popitem()[0]).index( 1) # var is the p-th generator def subs_e(e): e = list(e) assert e[p] % exponent == 0 e[p] = e[p] // exponent return tuple(e) parent = expression.parent() result = parent( {subs_e(e): c for e, c in iteritems(expression.dict())}) return result def de_power(expression): expression = Z(expression) for e, var in zip(exponents, Z.gens()): if abs(e) == 1: continue expression = subs_power(expression, var, abs(e)) return expression logger.debug('Omega_ge: preparing denominator') factors_denominator = tuple( de_power(1 - factor) for factor in _Omega_factors_denominator_(x, y)) logger.debug('Omega_ge: preparing numerator') numerator = de_power(_Omega_numerator_(a, x, y, t)) logger.info('Omega_ge: completed') return numerator, factors_denominator
def Omega_ge(a, exponents): r""" Return `\Omega_{\ge}` of the expression specified by the input. To be more precise, calculate .. MATH:: \Omega_{\ge} \frac{\mu^a}{ (1 - z_0 \mu^{e_0}) \dots (1 - z_{n-1} \mu^{e_{n-1}})} and return its numerator and a factorization of its denominator. Note that `z_0`, ..., `z_{n-1}` only appear in the output, but not in the input. INPUT: - ``a`` -- an integer - ``exponents`` -- a tuple of integers OUTPUT: A pair representing a quotient as follows: Its first component is the numerator as a Laurent polynomial, its second component a factorization of the denominator as a tuple of Laurent polynomials, where each Laurent polynomial `z` represents a factor `1 - z`. The parents of these Laurent polynomials is always a Laurent polynomial ring in `z_0`, ..., `z_{n-1}` over `\ZZ`, where `n` is the length of ``exponents``. EXAMPLES:: sage: from sage.rings.polynomial.omega import Omega_ge sage: Omega_ge(0, (1, -2)) (1, (z0, z0^2*z1)) sage: Omega_ge(0, (1, -3)) (1, (z0, z0^3*z1)) sage: Omega_ge(0, (1, -4)) (1, (z0, z0^4*z1)) sage: Omega_ge(0, (2, -1)) (z0*z1 + 1, (z0, z0*z1^2)) sage: Omega_ge(0, (3, -1)) (z0*z1^2 + z0*z1 + 1, (z0, z0*z1^3)) sage: Omega_ge(0, (4, -1)) (z0*z1^3 + z0*z1^2 + z0*z1 + 1, (z0, z0*z1^4)) sage: Omega_ge(0, (1, 1, -2)) (-z0^2*z1*z2 - z0*z1^2*z2 + z0*z1*z2 + 1, (z0, z1, z0^2*z2, z1^2*z2)) sage: Omega_ge(0, (2, -1, -1)) (z0*z1*z2 + z0*z1 + z0*z2 + 1, (z0, z0*z1^2, z0*z2^2)) sage: Omega_ge(0, (2, 1, -1)) (-z0*z1*z2^2 - z0*z1*z2 + z0*z2 + 1, (z0, z1, z0*z2^2, z1*z2)) :: sage: Omega_ge(0, (2, -2)) (-z0*z1 + 1, (z0, z0*z1, z0*z1)) sage: Omega_ge(0, (2, -3)) (z0^2*z1 + 1, (z0, z0^3*z1^2)) sage: Omega_ge(0, (3, 1, -3)) (-z0^3*z1^3*z2^3 + 2*z0^2*z1^3*z2^2 - z0*z1^3*z2 + z0^2*z2^2 - 2*z0*z2 + 1, (z0, z1, z0*z2, z0*z2, z0*z2, z1^3*z2)) :: sage: Omega_ge(0, (3, 6, -1)) (-z0*z1*z2^8 - z0*z1*z2^7 - z0*z1*z2^6 - z0*z1*z2^5 - z0*z1*z2^4 + z1*z2^5 - z0*z1*z2^3 + z1*z2^4 - z0*z1*z2^2 + z1*z2^3 - z0*z1*z2 + z0*z2^2 + z1*z2^2 + z0*z2 + z1*z2 + 1, (z0, z1, z0*z2^3, z1*z2^6)) TESTS:: sage: Omega_ge(0, (2, 2, 1, 1, 1, 1, 1, -1, -1))[0].number_of_terms() # long time 27837 :: sage: Omega_ge(1, (2,)) (1, (z0,)) """ import logging logger = logging.getLogger(__name__) logger.info('Omega_ge: a=%s, exponents=%s', a, exponents) from sage.arith.all import lcm, srange from sage.rings.integer_ring import ZZ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.number_field.number_field import CyclotomicField if not exponents or any(e == 0 for e in exponents): raise NotImplementedError rou = sorted(set(abs(e) for e in exponents) - set([1])) ellcm = lcm(rou) B = CyclotomicField(ellcm, 'zeta') zeta = B.gen() z_names = tuple('z{}'.format(i) for i in range(len(exponents))) L = LaurentPolynomialRing(B, ('t',) + z_names, len(z_names) + 1) t = L.gens()[0] Z = LaurentPolynomialRing(ZZ, z_names, len(z_names)) powers = {i: L(zeta**(ellcm//i)) for i in rou} powers[2] = L(-1) powers[1] = L(1) exponents_and_values = tuple( (e, tuple(powers[abs(e)]**j * z for j in srange(abs(e)))) for z, e in zip(L.gens()[1:], exponents)) x = tuple(v for e, v in exponents_and_values if e > 0) y = tuple(v for e, v in exponents_and_values if e < 0) def subs_power(expression, var, exponent): r""" Substitute ``var^exponent`` by ``var`` in ``expression``. It is assumed that ``var`` only occurs with exponents divisible by ``exponent``. """ p = tuple(var.dict().popitem()[0]).index(1) # var is the p-th generator def subs_e(e): e = list(e) assert e[p] % exponent == 0 e[p] = e[p] // exponent return tuple(e) parent = expression.parent() result = parent({subs_e(e): c for e, c in iteritems(expression.dict())}) return result def de_power(expression): expression = Z(expression) for e, var in zip(exponents, Z.gens()): if abs(e) == 1: continue expression = subs_power(expression, var, abs(e)) return expression logger.debug('Omega_ge: preparing denominator') factors_denominator = tuple(de_power(1 - factor) for factor in _Omega_factors_denominator_(x, y)) logger.debug('Omega_ge: preparing numerator') numerator = de_power(_Omega_numerator_(a, x, y, t)) logger.info('Omega_ge: completed') return numerator, factors_denominator
def _LKB_matrix_(self, braid, variab): """ Compute the Lawrence-Krammer-Bigelow representation matrix. The variables of the matrix must be given. This actual computation is done in this helper method for caching purposes. INPUT: - ``braid`` -- tuple of integers. The Tietze list of the braid. - ``variab`` -- string. the names of the variables that will appear in the matrix. They must be given as a string, separated by a comma OUTPUT: The LKB matrix of the braid, with respect to the variables. TESTS:: sage: B=BraidGroup(3) sage: B._LKB_matrix_((2, 1, 2), 'x, y') [ 0 -x^4*y + x^3*y -x^4*y] [ 0 -x^3*y 0] [ -x^2*y x^3*y - x^2*y 0] sage: B._LKB_matrix_((1, 2, 1), 'x, y') [ 0 -x^4*y + x^3*y -x^4*y] [ 0 -x^3*y 0] [ -x^2*y x^3*y - x^2*y 0] sage: B._LKB_matrix_((-1, -2, -1, 2, 1, 2), 'x, y') [1 0 0] [0 1 0] [0 0 1] """ n = self.strands() if len(braid) > 1: A = self._LKB_matrix_(braid[:1], variab) for i in braid[1:]: A = A * self._LKB_matrix_((i, ), variab) return A l = list(Set(range(n)).subsets(2)) R = LaurentPolynomialRing(IntegerRing(), variab) q = R.gens()[0] t = R.gens()[1] if len(braid) == 0: return identity_matrix(R, len(l), sparse=True) A = matrix(R, len(l), sparse=True) if braid[0] > 0: i = braid[0] - 1 for m in range(len(l)): j = min(l[m]) k = max(l[m]) if i == j - 1: A[l.index(Set([i, k])), m] = q A[l.index(Set([i, j])), m] = q * q - q A[l.index(Set([j, k])), m] = 1 - q elif i == j and not j == k - 1: A[l.index(Set([j, k])), m] = 0 A[l.index(Set([j + 1, k])), m] = 1 elif k - 1 == i and not k - 1 == j: A[l.index(Set([j, i])), m] = q A[l.index(Set([j, k])), m] = 1 - q A[l.index(Set([i, k])), m] = (1 - q) * q * t elif i == k: A[l.index(Set([j, k])), m] = 0 A[l.index(Set([j, k + 1])), m] = 1 elif i == j and j == k - 1: A[l.index(Set([j, k])), m] = -t * q * q else: A[l.index(Set([j, k])), m] = 1 return A else: i = -braid[0] - 1 for m in range(len(l)): j = min(l[m]) k = max(l[m]) if i == j - 1: A[l.index(Set([j - 1, k])), m] = 1 elif i == j and not j == k - 1: A[l.index(Set([j + 1, k])), m] = q**(-1) A[l.index(Set([j, k])), m] = 1 - q**(-1) A[l.index(Set([j, j + 1])), m] = t**(-1) * q**(-1) - t**(-1) * q**(-2) elif k - 1 == i and not k - 1 == j: A[l.index(Set([j, k - 1])), m] = 1 elif i == k: A[l.index(Set([j, k + 1])), m] = q**(-1) A[l.index(Set([j, k])), m] = 1 - q**(-1) A[l.index(Set([k, k + 1])), m] = -q**(-1) + q**(-2) elif i == j and j == k - 1: A[l.index(Set([j, k])), m] = -t**(-1) * q**(-2) else: A[l.index(Set([j, k])), m] = 1 return A
def alexander_polynomial(self, multivar=True, v='no', method='default', norm=True, factored=False): """ Calculates the Alexander polynomial of the link. For links with one component, can evaluate the alexander polynomial at v:: sage: K = Link('4_1') sage: K.alexander_polynomial() t^2 - 3*t + 1 sage: K.alexander_polynomial(v=[4]) 5 sage: K = Link('L7n1') sage: K.alexander_polynomial(norm=False) t1^-1*t2^-1 + t1^-2*t2^-4 The default algorithm for *knots* is Bar-Natan's super-fast tangle-based algorithm. For links, we apply Fox calculus to a Wirtinger presentation for the link:: sage: L = Link('K13n123') sage: L.alexander_polynomial() == L.alexander_polynomial(method='wirtinger') True """ # sign normalization still missing, but when "norm=True" the # leading coefficient with respect to the first variable is made # positive. if method == 'snappy': try: return self.exterior().alexander_polynomial() except ImportError: raise RuntimeError('this method for alexander_polynomial ' + no_snappy_msg) else: comp = len(self.link_components) if comp < 2: multivar = False # If single variable, use the super-fast method of Bar-Natan. if comp == 1 and method == 'default' and norm: p = alexander.alexander(self) else: # Use a simple method based on the Wirtinger presentation. if method not in ['default', 'wirtinger']: raise ValueError( "Available methods are 'default' and 'wirtinger'") if (multivar): L = LaurentPolynomialRing( QQ, ['t%d' % (i + 1) for i in range(comp)]) t = list(L.gens()) else: L = LaurentPolynomialRing(QQ, 't') t = [L.gen()] M = self.alexander_matrix(mv=multivar) C = M[0] m = C.nrows() n = C.ncols() if n > m: k = m - 1 else: k = n - 1 subMatrix = C[0:k, 0:k] p = subMatrix.determinant() if p == 0: return 0 if multivar: t_i = M[1][-1] p = (p.factor()) / (t_i - 1) p = p.expand() if (norm): p = normalize_alex_poly(p, t) if v != 'no': return p(*v) if multivar and factored: # it's easier to view this way return p.factor() else: return p