def twisted_cochain_complex(self): """ Returns chain complex of the presentation CW complex of the given group with coefficients twisted by self. """ gens, rels, rho = self.generators, self.relators, self d1 = [[fox_derivative(R, rho, g) for g in gens] for R in rels] d1 = block_matrix(d1, nrows=len(rels), ncols=len(gens)) d0 = [rho(g) - 1 for g in gens] d0 = block_matrix(d0, nrow=len(gens), ncols=1) C = ChainComplex({0: d0, 1: d1}, check=True) return C
def twisted_alexander_polynomial(alpha, reduced=False): """ In HKL, alpha is epsilon x rho; in nsagetools, it would be called phialpha with phi being epsilon. If reduced is True, the answer is divided by (t - 1). Here, we duplicate the calculation of Section 10.2 of [HKL]. sage: M = Manifold('K12a169') sage: G = M.fundamental_group() sage: A = matrix(GF(5), [[0, 4], [1, 4]]) sage: rho = cyclic_rep(G, A) sage: chi = lambda v:3*v[0] sage: alpha = induced_rep_from_twisted_cocycle(3, rho, chi, (0, 0, 1, 0)) sage: -twisted_alexander_polynomial(alpha, reduced=True) 4*t^2 + (z^3 + z^2 + 5)*t + 4 """ F = alpha('a').base_ring().base_ring() epsilon = alpha.epsilon gens, rels = alpha.generators, alpha.relators k = len(gens) # Make sure this special algorithm applies. assert len(rels) == len(gens) - 1 and epsilon.range().rank() == 1 # Want the first variable to be homologically nontrivial i0 = [i for i, g in enumerate(gens) if epsilon(g) != 0][0] gens = gens[i0:] + gens[:i0] # Boundary maps for chain complex d2 = [[fox_derivative_with_involution(R, alpha, g) for R in rels] for g in gens] d2 = block_matrix(d2, nrows=k, ncols=k - 1) d1 = [alpha(g.swapcase()) - 1 for g in gens] d1 = block_matrix(d1, nrows=1, ncols=k) assert d1 * d2 == 0 T = last_square_submatrix(d2) B = first_square_submatrix(d1) T = normalize_polynomial(fast_determinant_of_laurent_poly_matrix(T)) B = normalize_polynomial(fast_determinant_of_laurent_poly_matrix(B)) q, r = T.quo_rem(B) assert r == 0 ans = normalize_polynomial(q) if reduced: t = ans.parent().gen() ans, r = ans.quo_rem(t - 1) assert r == 0 return ans
def twisted_chain_complex(self): """ Returns chain complex of the presentation CW complex of the given group with coefficients twisted by self. """ gens, rels, rho = self.generators, self.relators, self d2 = [[fox_derivative_with_involution(R, rho, g) for R in rels] for g in gens] d2 = block_matrix(d2, nrows=len(gens), ncols=len(rels)) d1 = [rho(g.swapcase()) - 1 for g in gens] d1 = block_matrix(d1, nrows=1, ncols=len(gens)) C = ChainComplex({1: d1, 2: d2}, degree_of_differential=-1, check=True) return C
def generic_prod_mat(dim_tuple): if dim_tuple in gen_prod_mat: return gen_prod_mat[dim_tuple] k = len(dim_tuple) first_column = zero_matrix(ZZ, k, 1) last_column = matrix(ZZ, k, 1, [d for d in dim_tuple]) # If dim_list is all zeros, then return a single matrix of zeros. if first_column == last_column: return [first_column] current_batch = [first_column] next_batch = [] gpms = [] while len(current_batch) != 0: for m in current_batch: l = m.ncols() next_column_options = [range(m[r, l - 1], min(m[r, l - 1] + 2, dim_tuple[r] + 1)) for r in range(k)] new_column_iterator = itertools.product(*next_column_options) # we don't want the same column again. drop = next(new_column_iterator) for next_column_tuple in new_column_iterator: next_column = matrix(ZZ, k, 1, next_column_tuple) mm = block_matrix([[m, matrix(ZZ, k, 1, next_column)]], subdivide=False) if next_column == last_column: gpms += [mm] else: next_batch += [mm] current_batch = next_batch next_batch = [] gen_prod_mat[dim_tuple] = gpms return gpms
def gen_tau_RNE( do_varify, rbt, usemotordyn=True, varify_trig = True ): Si = _gen_rbt_Si(rbt) IIi = range(0,rbt.dof+1) ### Linear dynamic parameters: for i in range(1,rbt.dof+1): IIi[i] = block_matrix( [ rbt.Ifi[i] , skew(rbt.mli[i]) , -skew(rbt.mli[i]) , rbt.mi[i]*identity_matrix(3) ] ,subdivide=False) ### Not linear dynamic parameters: #for i in range(1,dof+1): IIi[i] = block_matrix( [ rbt.Ifi_from_Ici[i] , skew(rbt.mli_e[i]) , -skew(rbt.mli_e[i]) , rbt.mi[i]*identity_matrix(3) ] ,subdivide=False) if do_varify: auxvars = [] def custom_simplify( expression ): # return trig_reduce(expression.expand()).simplify_rational() return expression.simplify_rational() def varify_func(exp,varrepr): if varify_trig : exp = exp.subs( LoP_to_D(rbt.LoP_trig_f2v) ) exp = custom_simplify( exp ) return m_varify(exp,auxvars,condition_func=is_compound) #return v6R_varify(exp,auxvars,varrepr) else: def varify_func(exp,varrepr): return exp Vi, dVi = _forward_RNE( rbt, Si, varify_func ) tau = _backward_RNE( rbt, IIi, Si, Vi, dVi, usemotordyn, None, varify_func = varify_func ) if do_varify: if varify_trig : auxvars = rbt.LoP_trig_v2f + auxvars return auxvars,tau else: return tau
def adjdual(g, h): from sage.all import block_matrix, zero_matrix wg = g[0:3, 0] vg = g[3:6, 0] return block_matrix( [[skew(wg), zero_matrix(3, 3)], [skew(vg), skew(wg)]], subdivide=False).transpose() * h
def semidirect_rep_from_twisted_cocycle(self, cocycle): """ Given a representation rho to GL(R, n) and a rho-twisted 1-cocycle, construct the representation to GL(R, n + 1) corresponding to the semidirect product. Note: Since we prefer to stick to left-actions only, unlike [HLK] this is the semidirect produce associated to the left action of GL(R, n) on V = R^n. That is, pairs (v, A) with v in V and A in GL(R, n) where (v, A) * (w, B) = (v + A*w, A*B):: sage: G = Manifold('K12a169').fundamental_group() sage: A = matrix(GF(5), [[0, 4], [1, 4]]) sage: rho = cyclic_rep(G, A) sage: cocycle = vector(GF(5), (0, 0, 1, 0)) sage: rho_til = rho.semidirect_rep_from_twisted_cocycle(cocycle) sage: rho_til('abAB') [1 0 4] [0 1 1] [0 0 1] """ gens, rels, rho = self.generators, self.relators, self n = rho.dim assert len(cocycle) == len(gens) * n new_mats = [] for i, g in enumerate(gens): v = matrix([cocycle[i * n:(i + 1) * n]]).transpose() zeros = matrix(n * [0]) one = matrix([[1]]) A = block_matrix([[rho(g), v], [zeros, one]]) new_mats.append(A) target = MatrixSpace(rho.base_ring, n + 1) return MatrixRepresentation(gens, rels, target, new_mats)
def StrictlyPositiveOrthant(d): if d == 0: return Polyhedron(ambient_dim=0, vertices=[()], base_ring=QQ) else: return Polyhedron(ieqs=block_matrix([[ matrix(QQ, d, 1, [-1 for i in range(d)]), identity_matrix(QQ, d) ]]))
def gen_regressor_RNE( do_varify, rbt, usemotordyn = True, usefricdyn = True, varify_trig = True ): from copy import copy Si = _gen_rbt_Si(rbt) if usefricdyn: fric = gen_Dyn_fricterm(rbt) def custom_simplify( expression ): #return trig_reduce(expression.expand()).simplify_rational() return expression.simplify_rational() varify_func = None if do_varify: auxvars = [] def varify_func(exp,varrepr): if varify_trig : exp = exp.subs( LoP_to_D(rbt.LoP_trig_f2v) ) exp = custom_simplify( exp ) return m_varify(exp, auxvars, poolrepr='auxY', condition_func=is_compound) Vi, dVi = _forward_RNE( rbt, Si, varify_func ) P = rbt.Parms(usemotordyn,usefricdyn) Y = matrix(SR,rbt.dof,P.nrows()) for p in range(P.nrows()) : select = subsm( P, zero_matrix(P.nrows(),1) ) select.update( {P[p,0]:Integer(1)} ) IIi = range(0,rbt.dof+1) for i in range(1,rbt.dof+1): IIi[i] = block_matrix( [ rbt.Ifi[i].subs(select) , skew(rbt.mli[i].subs(select)) , -skew(rbt.mli[i].subs(select)) , rbt.mi[i].subs(select)*identity_matrix(3) ] ,subdivide=False) if usemotordyn: Imzi = deepcopy(rbt.Imzi) for i,Im in enumerate(Imzi): Imzi[i] = Im.subs(select) else: Imzi = None Y[:,p] = _backward_RNE( rbt, IIi, Si, Vi, dVi, usemotordyn, Imzi, varify_func ) if usefricdyn: Y[:,p] += fric.subs(select) if do_varify: if varify_trig : auxvars = rbt.LoP_trig_v2f + auxvars return auxvars , Y else: return Y
def preserves_hermitian_form(SL2C_matrices): """ >>> CC = ComplexField(100) >>> A = matrix(CC, [[1, 1], [1, 2]]); >>> B = matrix(CC, [[0, 1], [-1, 0]]) >>> C = matrix(CC, [[CC('I'),0], [0, -CC('I')]]) >>> ans, sig, form = preserves_hermitian_form([A, B]) >>> ans True >>> sig 'indefinite' >>> form.change_ring(ComplexField(10)) [ 0.00 -1.0*I] [ 1.0*I 0.00] >>> preserves_hermitian_form([A, B, C]) (False, None, None) >>> ans, sig, form = preserves_hermitian_form([B, C]) >>> sig 'definite' """ M = block_matrix(len(SL2C_matrices), 1, [ left_mult_by_adjoint(A) - right_mult_by_inverse(A) for A in SL2C_matrices ]) CC = M.base_ring() mp.prec = CC.prec() RR = RealField(CC.prec()) epsilon = RR(2)**(-int(0.8 * mp.prec)) U, S, V = mp.svd(sage_matrix_to_mpmath(M)) S = list(mp.chop(S, epsilon)) if mp.zero not in S: return False, None, None elif S.count(mp.zero) > 1: for i, A in enumerate(SL2C_matrices): for B in SL2C_matrices[i + 1:]: assert (A * B - B * A).norm() < epsilon sig = 'indefinite' if any(abs(A.trace()) > 2 for A in SL2C_matrices) else 'both' return True, sig, None else: in_kernel = list(mp.chop(V.H.column(S.index(mp.zero)))) J = mp.matrix([in_kernel[:2], in_kernel[2:]]) iJ = mp.mpc(imag=1) * J J1, J2 = J + J.H, iJ + iJ.H J = J1 if mp.norm(J1) >= mp.norm(J2) else J2 J = (1 / mp.sqrt(abs(mp.det(J)))) * J J = mpmath_matrix_to_sage(J) assert all((A.conjugate_transpose() * J * A - J).norm() < epsilon for A in SL2C_matrices) sig = 'definite' if J.det() > 0 else 'indefinite' return True, sig, J
def gen_massmatrix_RNE( do_varify, rbt, usemotordyn = True, varify_trig = True ): from copy import deepcopy Si = _gen_rbt_Si(rbt) IIi = range(0,rbt.dof+1) ### Linear dynamic parameters: for i in range(1,rbt.dof+1): IIi[i] = block_matrix( [ rbt.Ifi[i] , skew(rbt.mli[i]) , -skew(rbt.mli[i]) , rbt.mi[i]*identity_matrix(3) ] ,subdivide=False) ### Not linear dynamic parameters: #for i in range(1,dof+1): IIi[i] = block_matrix( [ rbt.Ifi_from_Ici[i] , skew(rbt.mli_e[i]) , -skew(rbt.mli_e[i]) , rbt.mi[i]*identity_matrix(3) ] ,subdivide=False) def custom_simplify( expression ): #return trig_reduce(expression.expand()).simplify_rational() return expression.simplify_rational() varify_func = None if do_varify: auxvars = [] def varify_func(exp,varrepr): if varify_trig : exp = exp.subs( LoP_to_D(rbt.LoP_trig_f2v) ) exp = custom_simplify( exp ) return m_varify(exp, auxvars, poolrepr='auxM', condition_func=is_compound) M = matrix(SR,rbt.dof,rbt.dof) rbttmp = deepcopy(rbt) rbttmp.grav = zero_matrix(3,1) rbttmp.dq = zero_matrix(rbttmp.dof,1) for i in range(M.nrows()): rbttmp.ddq = zero_matrix(rbttmp.dof,1) rbttmp.ddq[i,0] = 1.0 rbttmp.gen_geom() Vi, dVi = _forward_RNE( rbttmp, Si, varify_func ) Mcoli = _backward_RNE( rbttmp, IIi, Si, Vi, dVi, usemotordyn, None, varify_func = varify_func ) # Do like this since M is symmetric: M[:,i] = matrix(SR,i,1,M[i,:i].list()).stack( Mcoli[i:,:] ) if do_varify: if varify_trig : auxvars = rbt.LoP_trig_v2f + auxvars return auxvars , M else: return M
def prep_cmb_row((i, n, pmt, nn_to_pm, prod_mats, pm_to_nn)): row = zero_matrix(ZZ, 1, pmt) p_cols = nn_to_pm[i].columns() for j in range(i + 1, pmt): q = nn_to_pm[j] both_cols = list(set(p_cols + q.columns())) both_cols.sort() combined = block_matrix([[matrix(ZZ, n, 1, list(v)) for v in both_cols]], subdivide=False) combined.set_immutable() if combined in prod_mats: row[0, j] = pm_to_nn[combined] else: row[0, j] = -1 if verbose: if i % 100 == 0: print 'Finished row ' + str(i) return [row]
def r_n_m_iter(A1, A2): ''' A1: n by n half-integral, symmetric matrix. A2: m by m half-integral, symmetric matrix. Return a generator of the tuple of n by m matrice R and mat = block_matrix([[A1, R/2], [R^t/2, A2]]) such that mat is half-integral, semi positive definite. ''' n = A1.ncols() m = A2.ncols() r_iters = [_r_iter(a1, a2) for a1, a2 in itertools.product( [A1[i, i] for i in range(n)], [A2[i, i] for i in range(m)])] for rs in itertools.product(*r_iters): R = matrix(n, [ZZ(r) for r in rs]) mat = block_matrix([[A1, R / ZZ(2)], [R.transpose() / ZZ(2), A2]]) if is_semi_positive_definite(mat): yield (R, mat)
def _adaptive_newton_step(hyperbolicStructure, errors_with_norm, verbose=False): errors, errors_norm = errors_with_norm num_edges = len(hyperbolicStructure.mcomplex.Edges) penalties, penalty_derivative = _large_angle_penalties_and_derivatives( hyperbolicStructure, verbose=verbose) all_errors = vector(errors + penalties) jacobian = hyperbolicStructure.jacobian() penalty_derivative_matrix = matrix(RDF, penalty_derivative, ncols=num_edges) m = block_matrix([[jacobian], [penalty_derivative_matrix]]) mInv = _pseudo_inverse(m, verbose=verbose) mInvErrs = mInv * all_errors for i in range(14): step_size = RDF(0.5)**i new_edge_params = list( vector(hyperbolicStructure.edge_lengths) - step_size * mInvErrs) try: newHyperbolicStructure = HyperbolicStructure( hyperbolicStructure.mcomplex, new_edge_params) except BadDihedralAngleError: continue new_errors_with_norm = _compute_errors_with_norm( newHyperbolicStructure) if new_errors_with_norm[1] < errors_norm: return (newHyperbolicStructure, new_errors_with_norm) raise NewtonStepError()
def compute_torsion(G, bits_prec, alpha=None, phi=None, phialpha=None, return_parts=False, return_as_poly=True, wada_conventions=False, symmetry_test=True): if alpha: F = alpha('a').base_ring() elif phialpha: F = phialpha('a').base_ring().base_ring() epsilon = ZZ(2)**(-bits_prec // 3) if not F.is_exact() else None big_epsilon = ZZ(2)**(-bits_prec // 5) if not F.is_exact() else None gens, rels = G.generators(), G.relators() k = len(gens) if phi == None: phi = MapToGroupRingOfFreeAbelianization(G, F) # Make sure this special algorithm applies. assert len(rels) == len(gens) - 1 and len(phi.range().gens()) == 1 # Want the first variable to be homologically nontrivial i0 = [i for i, g in enumerate(gens) if phi(g) != 1][0] gens = gens[i0:] + gens[:i0] if phialpha == None: phialpha = PhiAlpha(phi, alpha) # Boundary maps for chain complex if not wada_conventions: # Using the conventions of our paper. d2 = [[fox_derivative_with_involution(R, phialpha, g) for R in rels] for g in gens] d2 = block_matrix(d2, nrows=k, ncols=k - 1) d1 = [phialpha(g.swapcase()) - 1 for g in gens] d1 = block_matrix(d1, nrows=1, ncols=k) dsquared = d1 * d2 else: # Using those implicit in Wada's paper. d2 = [[fox_derivative(R, phialpha, g) for g in gens] for R in rels] d2 = block_matrix(sum(d2, []), nrows=k - 1, ncols=k) d1 = [phialpha(g) - 1 for g in gens] d1 = block_matrix(d1, nrows=k, ncols=1) dsquared = d2 * d1 if not matrix_has_small_entries(dsquared, epsilon): raise TorsionComputationError("(boundary)^2 != 0") T = last_square_submatrix(d2) if return_as_poly: T = fast_determinant_of_laurent_poly_matrix(T) else: T = det(T) B = first_square_submatrix(d1) B = det(B) if return_as_poly: T = clean_laurent_to_poly(T, epsilon) B = clean_laurent_to_poly(B, epsilon) else: T = clean_laurent(T, epsilon) B = clean_laurent(B, epsilon) if return_parts: return (T, B) q, r = T.quo_rem(B) ans = clean_laurent_to_poly(q, epsilon) if univ_abs(r) > epsilon: raise TorsionComputationError("Division failed") # Now do a quick sanity check if symmetry_test: coeffs = ans.coefficients() error = max( [univ_abs(a - b) for a, b in zip(coeffs, reversed(coeffs))]) if error > epsilon: raise TorsionComputationError( "Torsion polynomial doesn't seem symmetric") return ans
def Adjdual(G, g): from sage.all import block_matrix, zero_matrix R = G[0:3, 0:3] p = G[0:3, 3] return block_matrix([[R, zero_matrix(3, 3)], [skew(p) * R, R]], subdivide=False).transpose() * g
def prune_dg_module_on_poset(dgm, ab, verbose=False, assume_sorted=False): a, b = ab tv = dgm.cat.objects[0] ring = dgm.ring cat = dgm.target_cat #diff_dict = {d:dgm.differential(tv, (d,)) for d in range(a - 1, b + 1)} diff_dict = {} for d in range(a - 1, b + 1): if verbose: print('computing differential in degree ' + str(d)) diff_dict[d] = dgm.differential(tv, (d,)) if verbose: print('original differentials computed') # Since we assume that cat is a poset, # we may convert the differentials to usual sagemath matrices # as long as we keep track of the row labels. # # triv = cat.trivial_representation(ring) # m_dict = {} # for d in range(a - 1, b + 1): # m_dict[d] = triv(diff_dict[d]) m_dict = {} for d in range(a - 1, b + 1): if verbose: print('Expanding the differential in degree ' + str(d)) entries = [] z = 0 dv = diff_dict[d].data_vector source = diff_dict[d].source target = diff_dict[d].target for x in source: for y in target: if len(cat.hom(x, y)) == 1: entries += [dv[z]] z += 1 else: entries += [ring(0)] m_dict[d] = matrix(ring, len(source), len(target), entries) # This dict will keep track of the row labels m_source = {} # and dict will keep track of the target labels m_target = {} if assume_sorted: for d in range(a - 1, b + 1): for x in cat.objects: m_source[d, x] = 0 m_target[d, x] = 0 for y in diff_dict[d].source: if x == y: m_source[d, x] += 1 for y in diff_dict[d].target: if x == y: m_target[d, x] += 1 source_assumed = [x for x in cat.objects for _ in range(m_source[d, x])] if diff_dict[d].source != source_assumed: raise ValueError('This dgModule is not sorted in degree ' + str(d)) else: # Time to sort the rows for d in range(a - 1, b + 1): targ = m_dict[d].ncols() rows = m_dict[d].rows() new_rows = [] for x in cat.objects: m_source[d, x] = 0 for i, r in enumerate(rows): if diff_dict[d].source[i] == x: m_source[d, x] += 1 new_rows += [r] if len(new_rows) == 0: m_dict[d] = zero_matrix(ring, 0, targ) else: m_dict[d] = block_matrix(ring, [[matrix(ring, 1, targ, list(r))] for r in new_rows]) # and now the columns for d in range(a - 1, b + 1): sour = m_dict[d].nrows() cols = m_dict[d].columns() new_cols = [] for x in cat.objects: m_target[d, x] = 0 for i, c in enumerate(cols): if diff_dict[d].target[i] == x: m_target[d, x] += 1 new_cols += [c] if len(new_cols) == 0: m_dict[d] = zero_matrix(ring, sour, 0) else: m_dict[d] = block_matrix(ring, [[matrix(ring, sour, 1, list(c)) for c in new_cols]]) # if verbose: # for d in range(a - 1, b + 1): # print # print [m_source[d, x] for x in cat.objects] # for r in m_dict[d]: # print r # print [m_target[d, x] for x in cat.objects] # Find the desired row- and column-operations # and change the labels (slightly prematurely) for d in range(a, b): for x in cat.objects: upper_left = m_dict[d][:m_source[d, x], :m_target[d, x]] if verbose: print('Computing Smith form of a matrix with dimensions ' + str(upper_left.dimensions())) dropped, sc, sr, tc, tr = prune_matrix(upper_left) if verbose: print('Dropping ' + str(dropped) + ' out of ' + str(m_source[d, x]) + \ ' occurrences of ' + str(x) + ' in degree ' + str(d - 1)) m_target[d - 1, x] -= dropped m_source[d + 1, x] -= dropped cid = m_dict[d - 1].ncols() - sc.nrows() zul = zero_matrix(ring, sc.nrows(), cid) zlr = zero_matrix(ring, cid, sc.ncols()) m_dict[d - 1] = m_dict[d - 1] * block_matrix([[zul, sc], [identity_matrix(ring, cid), zlr]]) rid = m_dict[d + 1].nrows() - tr.ncols() zul = zero_matrix(ring, rid, tr.ncols()) zlr = zero_matrix(ring, tr.nrows(), rid) m_dict[d + 1] = block_matrix([[zul, identity_matrix(ring, rid)], [tr, zlr]]) * m_dict[d + 1] row_rest = m_dict[d].nrows() - m_source[d, x] col_rest = m_dict[d].ncols() - m_target[d, x] m_dict[d] = block_diagonal_matrix([sr, identity_matrix(ring, row_rest)]) * m_dict[d] m_dict[d] = m_dict[d] * block_diagonal_matrix([tc, identity_matrix(ring, col_rest)]) rest_rest = m_dict[d][m_source[d, x]:, m_target[d, x]:] rest_dropped = m_dict[d][m_source[d, x]:, :dropped] dropped_rest = m_dict[d][:dropped, m_target[d, x]:] rest_kept = m_dict[d][m_source[d, x]:, dropped:m_target[d, x]] kept_rest = m_dict[d][dropped:m_source[d, x], m_target[d, x]:] kept_kept = m_dict[d][dropped:m_source[d, x], dropped:m_target[d, x]] m_dict[d] = block_matrix(ring, [[rest_rest - rest_dropped * dropped_rest, rest_kept], [kept_rest, kept_kept]]) m_source[d, x] -= dropped m_target[d, x] -= dropped for d in range(a - 1, b + 1): source = [x for x in cat.objects for _ in range(m_source[d, x])] target = [x for x in cat.objects for _ in range(m_target[d, x])] dv = [w for i, r in enumerate(m_dict[d].rows()) for j, w in enumerate(r) if len(cat.hom(source[i], target[j])) == 1] data_vector = vector(ring, dv) diff_dict[d] = CatMat(ring, cat, source, data_vector, target) def pruned_f_law(d_singleton, x, f, y): d = d_singleton[0] if d in range(a - 1, b + 1): return CatMat.identity_matrix(ring, cat, diff_dict[d].source) return dgm.module_in_degree((d,))(x, f, y) def pruned_d_law(x, d_singleton): d = d_singleton[0] if d in range(a - 1, b + 1): return diff_dict[d] return dgm.differential(x, (d,)) return dgModule(TerminalCategory, ring, pruned_f_law, [pruned_d_law], target_cat=cat)
def inverse_T(T): from sage.all import block_matrix, zero_matrix, Integer return block_matrix( [[T[0:3, 0:3].transpose(), -T[0:3, 0:3].transpose() * T[0:3, 3]], [zero_matrix(1, 3), Integer(1)]], subdivide=False)
def PositiveOrthant(d): if d == 0: return Polyhedron(ambient_dim=0, eqns=[], ieqs=[(0, )], base_ring=QQ) else: return Polyhedron(ieqs=block_matrix( [[matrix(QQ, d, 1), identity_matrix(QQ, d)]]))
def conf_model(n, X, ring=ZZ, output_file_name=None, verbose=False, parallelize=True, display_degree=7): # Set up our graphs vertices = range(1, n + 1) edges = [(i, j) for i, j in Combinations(vertices, 2)] graphs = list(Subsets(edges)) # Define the poset G(n) as a category def G_one(x): return '*' def G_hom(x, y): if x.issubset(y): return ['*'] return [] def G_comp(x, f, y, g, z): return '*' G = FiniteCategory(graphs, G_one, G_hom, G_comp) Gop = G.op() # Print out all the homsets if verbose: for x in G.objects: for y in G.objects: print 'Hom(' + str(x) + ', ' + str(y) + ') = ' + str(G.hom(x, y)) # Build the vertices of X^n # Given a tuple of dimensions, this function builds all integer matrices with first column zero, # last column dim_tuple, all columns distinct, and where consecutive entries in a row have difference 0 or 1. gen_prod_mat = {} def generic_prod_mat(dim_tuple): if dim_tuple in gen_prod_mat: return gen_prod_mat[dim_tuple] k = len(dim_tuple) first_column = zero_matrix(ZZ, k, 1) last_column = matrix(ZZ, k, 1, [d for d in dim_tuple]) # If dim_list is all zeros, then return a single matrix of zeros. if first_column == last_column: return [first_column] current_batch = [first_column] next_batch = [] gpms = [] while len(current_batch) != 0: for m in current_batch: l = m.ncols() next_column_options = [range(m[r, l - 1], min(m[r, l - 1] + 2, dim_tuple[r] + 1)) for r in range(k)] new_column_iterator = itertools.product(*next_column_options) # we don't want the same column again. drop = next(new_column_iterator) for next_column_tuple in new_column_iterator: next_column = matrix(ZZ, k, 1, next_column_tuple) mm = block_matrix([[m, matrix(ZZ, k, 1, next_column)]], subdivide=False) if next_column == last_column: gpms += [mm] else: next_batch += [mm] current_batch = next_batch next_batch = [] gen_prod_mat[dim_tuple] = gpms return gpms # This set will contain a list of all the Gamma-conf matrices # where Gamma is the empty graph on n nodes. prod_mats = set() nonempty_faces = X.face_iterator(increasing=True) # This line pops off the empty face next(nonempty_faces) for simplex_tuple in itertools.product(nonempty_faces, repeat=n): dim_tuple = tuple(s.dimension() for s in simplex_tuple) for gpm in generic_prod_mat(dim_tuple): l = gpm.ncols() m = matrix(ZZ, n, l, [simplex_tuple[r][gpm[r, c]] for r in range(n) for c in range(l)]) m.set_immutable() prod_mats.add(m) # Each prod matrix gets assigned a number. Build the translation dicts both ways. # While we're looping, we also compute the row-distinctness graph for each prod matrix pm_to_nn = {} nn_to_pm = {} gammas = {} nns_by_gamma = {graph: [] for graph in graphs} for nn, pm in enumerate(prod_mats): pm_to_nn[pm] = nn nn_to_pm[nn] = pm graph = Set((i, j) for i, j in edges if pm.row(vertices.index(i)) != pm.row(vertices.index(j))) gammas[nn] = graph nns_by_gamma[graph] += [nn] # The matrix cmb is the combination table. The (i, j)-entry gives the index of the smallest prod mat that # contains the columns of the ith and jth prod mat. If no such prod mat exists, the table gives -1. pmt = len(prod_mats) if verbose: print 'Preparing combination table' print 'Total number of rows: ' + str(pmt) # def prep_cmb_row(i): # row = zero_matrix(ZZ, 1, pmt) # p_cols = nn_to_pm[i].columns() # for j in range(i + 1, pmt): # q = nn_to_pm[j] # both_cols = list(set(p_cols + q.columns())) # both_cols.sort() # combined = block_matrix([[matrix(ZZ, n, 1, list(v)) for v in both_cols]], subdivide=False) # combined.set_immutable() # if combined in prod_mats: # row[0, j] = pm_to_nn[combined] # else: # row[0, j] = -1 # if verbose: # if i % 100 == 0: # print 'Finished row ' + str(i) # return [row] if parallelize: pool = Pool() def arg_gen(zed): num = 0 while num < zed: yield (num, n, pmt, nn_to_pm, prod_mats, pm_to_nn) num += 1 cmb_row_list = pool.map(prep_cmb_row, arg_gen(pmt)) pool.close() pool.join() else: cmb_row_list = [prep_cmb_row(i) for i in range(pmt)] cmb = block_matrix(cmb_row_list) cmb = cmb + cmb.transpose() + diagonal_matrix(range(pmt)) # Check if a list of prod matrices can have their columns assembled into a single prod matrix def index_compatible(list_of_indices): if len(list_of_indices) <= 1: return True cur = list_of_indices[0] for i in list_of_indices[1:]: cur = cmb[cur, i] if cur == -1: return False return True # For each graph Gamma, compute the minimal prod matrices of type Gamma. min_prod_mat_indices = [] min_prod_mat_indices_by_gamma = {} for gamma in graphs: def leq(i, j): return cmb[i, j] == j poset = Poset((nns_by_gamma[gamma], leq)) min_prod_mat_indices_by_gamma[gamma] = poset.minimal_elements() min_prod_mat_indices += min_prod_mat_indices_by_gamma[gamma] # Build the resulting simplicial complex model for the product if verbose: print print 'Building the souped-up model for the product' print # My copy of sagemath has a verbose flag for SimplicialComplex, but this is not standard yet prod_model = SimplicialComplex(from_characteristic_function=(index_compatible, min_prod_mat_indices)) dim = prod_model.dimension() # for graph in graphs: # Y = SimplicialComplex(from_characteristic_function=(index_compatible, # [i for gamma in graphs if graph.issubset(gamma) for i in min_prod_mat_indices_by_gamma[gamma]])) # print 'Graph ' + str(graph) # for d in range(Y.dimension() + 1): # print 'Faces of dimension ' + str(d) + ': ' + str(len(Y.n_faces(d))) # print Y.homology() # print # # # sys.exit(0) if verbose: print 'Computing generator degrees' z = 1 zz = sum(len(prod_model.n_faces(d)) for d in range(dim + 1)) sorted_basis = {} for d in range(dim + 1): sorted_basis[d] = {graph: [] for graph in graphs} for f in prod_model.n_faces(d): if verbose: print '\r' + str(z) + '/' + str(zz), sys.stdout.flush() z += 1 gamma = Set(set(edges).intersection(*[set(gammas[m]) for m in f])) sorted_basis[d][gamma] += [f] print # In degree d, this dict holds a list of faces of prod_model basis = {} # labels[d] has the same length as basis[d] and tells you which graph labels = {} for d in range(-1, dim + 2): basis[d] = [] labels[d] = [] if d in range(dim + 1): for graph in graphs: batch = sorted_basis[d][graph] basis[d] += batch labels[d] += [graph] * len(batch) def f_law((d,), x, f, y): if d in labels: return CatMat.identity_matrix(ring, Gop, labels[d]) else: return CatMat.identity_matrix(ring, Gop, [])
def se3skew(g): from sage.all import block_matrix, zero_matrix, Integer w = g[0:3, 0] v = g[3:6, 0] return block_matrix( [[skew(w), v], [zero_matrix(1, 3), Integer(0)]], subdivide=False)