def gen_ccfterm_RNE( do_varify, rbt, usemotordyn=True, varify_trig=True ): from copy import deepcopy rbttmp = deepcopy(rbt) rbttmp.grav = zero_matrix(3,1) rbttmp.ddq = zero_matrix(rbttmp.dof,1) rbttmp.gen_geom() return gen_tau_RNE( do_varify, rbttmp, usemotordyn=usemotordyn, varify_trig=varify_trig )
def tau_to_regressor(tau,P,verify=False): ''' given symbolic vectors tau and P, returns matrix Y which verifies Y * P = tau ''' from sage.all import zero_matrix, SR m = tau.nrows() n = P.nrows() Y = copy( zero_matrix(SR,m,n) ) for i in range(0 ,m): for j in range(0 ,P.nrows()): if verify: coeffs = ( tau[i,0 ].coeffs(P[j,0 ]) ) for coeff in coeffs: if coeff[1] > 1 or coeff[1] < 0 : raise Exception('gen_dyn_lineqs: '+P[j,0 ].__repr__()+' is not linear') if coeff[1] == 1 : Y[i,j] = coeff[0] else: Y[i,j] = tau[i,0].coeff( P[j,0] ) if verify and bool( ( Y * P - tau ).expand() != zero_matrix(m,1) ): raise Exception('gen_dyn_lineqs: linearity not verified') return Y
def _gen_gravterm_altRNE(do_varify, rbt, varify_trig=True): from copy import deepcopy rbttmp = deepcopy(rbt) rbttmp.dq = zero_matrix(rbttmp.dof,1) rbttmp.ddq = zero_matrix(rbttmp.dof,1) rbttmp.gen_geom() return _gen_tau_altRNE(do_varify, rbttmp, varify_trig)
def positive_lineality_space(n): A = kalmanson_matrix(n) n = A.nrows() c = A.ncols() eq = zero_matrix(n,1).augment(A).rows() ieq = zero_matrix(c,1).augment(identity_matrix(c)).rows() return Polyhedron(eqns=eq, ieqs=ieq)
def gen_kinem( self ): self.Jpi = range(0 ,self.dof+1 ) self.Jpi[0] = zero_matrix(SR,3,self.dof) for l in range(1 ,self.dof+1 ): self.Jpi[l] = matrix(SR,3 ,self.dof) for j in range(1 ,l+1 ): self.Jpi[l][0 :3 ,j-1 ] = utils.skew(self.zi[j-1 ]) * ( self.pi[l] - self.pi[j-1 ] ) #Jpi[i] = Jpi[i].simplify_rational() #Jpi[i] = trig_reduce(Jpi[i]) #Jpi[i] = Jpi[i].simplify() self.Joi = range(0 ,self.dof+1 ) self.Joi[0] = zero_matrix(SR,3,self.dof) for l in range(1 ,self.dof+1 ): self.Joi[l] = matrix(SR,3 ,self.dof) for j in range(1 ,l+1 ): self.Joi[l][0 :3 ,j-1 ] = (self.zi[j-1 ]) #Joi[i] = Joi[i].simplify_rational() #Joi[i] = Joi[i].simplify() self.Jcpi = range(0 ,self.dof+1 ) self.Jcoi = self.Joi for l in range(1 ,self.dof+1 ): self.Jcpi[l] = self.Jpi[l] - utils.skew( self.Ri[l]*self.li[l] ) * self.Joi[l] return self
def test_symplectic(self): # the symmetric transformation matrix should be symplectic Pa = self.atrott Pb = self.btrott R = involution_matrix(Pa, Pb) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H,Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-4) g,g = Pa.dimensions() J = zero_matrix(ZZ,2*g,2*g) Ig = identity_matrix(ZZ, g, g) J[:g,g:] = Ig J[g:,:g] = -Ig self.assertEqual(Gamma.T*J*Gamma,J) Pa = self.aklein Pb = self.bklein R = involution_matrix(Pa, Pb, tol=1e-3) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H,Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-3) g,g = Pa.dimensions() J = zero_matrix(ZZ,2*g,2*g) Ig = identity_matrix(ZZ, g, g) J[:g,g:] = Ig J[g:,:g] = -Ig self.assertEqual(Gamma.T*J*Gamma,J) Pa = self.afermat Pb = self.bfermat R = involution_matrix(Pa, Pb, tol=1e-3) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H,Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-3) g,g = Pa.dimensions() J = zero_matrix(ZZ,2*g,2*g) Ig = identity_matrix(ZZ, g, g) J[:g,g:] = Ig J[g:,:g] = -Ig self.assertEqual(Gamma.T*J*Gamma,J) Pa = self.a6 Pb = self.b6 R = involution_matrix(Pa, Pb) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H,Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-4) g,g = Pa.dimensions() J = zero_matrix(ZZ,2*g,2*g) Ig = identity_matrix(ZZ, g, g) J[:g,g:] = Ig J[g:,:g] = -Ig self.assertEqual(Gamma.T*J*Gamma,J)
def test_symplectic(self): # the symmetric transformation matrix should be symplectic Pa = self.atrott Pb = self.btrott R = involution_matrix(Pa, Pb) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H, Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-4) g, g = Pa.dimensions() J = zero_matrix(ZZ, 2 * g, 2 * g) Ig = identity_matrix(ZZ, g, g) J[:g, g:] = Ig J[g:, :g] = -Ig self.assertEqual(Gamma.T * J * Gamma, J) Pa = self.aklein Pb = self.bklein R = involution_matrix(Pa, Pb, tol=1e-3) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H, Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-3) g, g = Pa.dimensions() J = zero_matrix(ZZ, 2 * g, 2 * g) Ig = identity_matrix(ZZ, g, g) J[:g, g:] = Ig J[g:, :g] = -Ig self.assertEqual(Gamma.T * J * Gamma, J) Pa = self.afermat Pb = self.bfermat R = involution_matrix(Pa, Pb, tol=1e-3) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H, Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-3) g, g = Pa.dimensions() J = zero_matrix(ZZ, 2 * g, 2 * g) Ig = identity_matrix(ZZ, g, g) J[:g, g:] = Ig J[g:, :g] = -Ig self.assertEqual(Gamma.T * J * Gamma, J) Pa = self.a6 Pb = self.b6 R = involution_matrix(Pa, Pb) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S) H, Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-4) g, g = Pa.dimensions() J = zero_matrix(ZZ, 2 * g, 2 * g) Ig = identity_matrix(ZZ, g, g) J[:g, g:] = Ig J[g:, :g] = -Ig self.assertEqual(Gamma.T * J * Gamma, J)
def tau_2_v(robot,tau,g=None): Dg = robot._D_grav_2_zero() if Dg: v = tau.subs( dict( utils.subsm(robot.ddq,zero_matrix(robot.dof,1 )).items() + Dg.items() ) ) else: if not g: raise Exception('tau_2_v: needs the g parameter') v = tau.subs( dict( utils.subsm(robot.ddq,zero_matrix(robot.dof,1 )) ) ) - g return v
def cp_d_law(x, dd): try: d = dd[0] except TypeError: d = dd if d == 0: return zero_matrix(QQ, 1, len(x)) if d == 1: return zero_matrix(QQ, len(x), max(0, len(x) - 1)) if d == 2: return zero_matrix(max(0, len(x) - 1), 0) if d >= 3: return zero_matrix(QQ, 0, 0)
def gen_geom(self): self.Tdhi = range(self.dof+1) self.Tdhi[0] = identity_matrix(SR,4 ) self.Tdhi_inv = range(self.dof+1) self.Tdhi_inv[0] = identity_matrix(SR,4 ) self.Rdhi = range(self.dof+1 ) self.Rdhi[0] = identity_matrix(SR,3 ) self.pdhi = range(self.dof+1 ) self.pdhi[0] = zero_matrix(SR,3,1 ) self.pdhfi = range(self.dof+1 ) self.pdhfi[0] = zero_matrix(SR,3,1 ) p_dhf = ( self.T_dh[0 :3 ,0 :3 ].transpose() * self.T_dh[0 :3 ,3 ] ).simplify_trig() T_dh_inv = utils.inverse_T(self.T_dh).simplify_trig() for l in range(self.dof): D = utils.LoP_to_D(zip(self.params_dh , self.links_dh[l])) self.Tdhi[l+1] = self.T_dh.subs(D) self.Tdhi_inv[l+1] = T_dh_inv.subs(D) self.Rdhi[l+1] = self.Tdhi[l+1][0 :3 ,0 :3 ] self.pdhi[l+1] = self.Tdhi[l+1][0 :3 ,3 ] self.pdhfi[l+1] = p_dhf.subs(D) self.Ti = range(self.dof+1 ) self.Ti[0] = identity_matrix(SR,4 ) for l in range(1 , self.dof+1 ): self.Ti[l] = self.Ti[l-1 ] * self.Tdhi[l] #Ti[l] = Ti[l].simplify_rational() #Ti[l] = trig_reduce(Ti[l]) #Ti[l] = Ti[l].simplify() self.Ri = range(0 ,self.dof+1 ) self.pi = range(0 ,self.dof+1 ) self.zi = range(0 ,self.dof+1 ) for l in range(0 ,self.dof+1 ): self.Ri[l] = self.Ti[l][0 :3 ,0 :3 ] self.pi[l] = self.Ti[l][0 :3 ,3 ] self.zi[l] = self.Ri[l][0 :3 ,2 ] return self
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 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 symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-4): r"""Returns the symplectic matrix `\Gamma` mapping the period matrices `Pa,Pb` to a symmetric period matrices. A helper function to :func:`symmetrize_periods`. Parameters ---------- Pa : complex matrix A `g x g` a-period matrix. Pb : complex matrix A `g x g` b-period matrix. S : integer matrix Integer kernel basis matrix. H : integer matrix Topological type classification matrix. Q : integer matrix The transformation matrix from `symmetric_block_diagonalize`. tol : double (Default: 1e-4) Tolerance used to verify integrality of intermediate matrices. Dependent on precision of period matrices. Returns ------- Gamma : integer matrix A `2g x 2g` symplectic matrix. """ # compute A and B g,g = Pa.dimensions() rhs = S*Q.change_ring(ZZ) A = rhs[:g,:g].T B = rhs[g:,:g].T H = H.change_ring(ZZ) # compute C and D half = QQ(1)/QQ(2) temp = (A*Re(Pa) + B*Re(Pb)).inverse() CT = half*A.T*H - Re(Pb)*temp CT_ZZ = CT.round().change_ring(ZZ) C = CT_ZZ.T DT = half*B.T*H + Re(Pa)*temp DT_ZZ = DT.round().change_ring(ZZ) D = DT_ZZ.T # sanity checks: make sure C and D are integral C_error = (CT.round() - CT).norm() D_error = (DT.round() - DT).norm() if (C_error > tol) or (D_error > tol): raise ValueError("The symmetric transformation matrix is not integral. " "Try increasing the precision of the input period " "matrices.") # construct Gamma Gamma = zero_matrix(ZZ, 2*g, 2*g) Gamma[:g,:g] = A Gamma[:g,g:] = B Gamma[g:,:g] = C Gamma[g:,g:] = D return Gamma
def kalmanson_matrix(n, aug=False): r,c = triu_indices(n, 1) k = len(r) inds = np.arange(k) row_ind = lambda (i,j): inds[np.logical_and(r==i-1, c==j-1)] upright = lambda (i,j): (i,j) if i<j else (j,i) get_ind = lambda ind_lst: np.array(map(row_ind, map(upright, grouper(2, ind_lst)))) rows = [] for i in range(1,n-2): for j in range(i+2, n): ind_lst = (i, j+1, i+1, j, i, j, i+1, j+1) indices = get_ind(ind_lst) row = np.zeros(k, dtype=np.int) row[indices] = [-1, -1, 1, 1] rows.append(row) sub = len(rows) for i in range(2, n-1): ind_lst = (i, 1, i+1, n, i, n, i+1, 1) indices = get_ind(ind_lst) row = np.zeros(k, dtype=np.int) row[indices] = [-1, -1, 1, 1] rows.append(row) mat = matrix(np.vstack(rows)) mat.subdivide(sub, None) if aug: return zero_matrix(len(rows),1).augment(mat) else: return mat
def gen_gravterm_EL(robot, potential_energy=None): U = potential_energy if potential_energy else gen_potential_energy(robot) from copy import copy g = copy(zero_matrix(SR, robot.dof, 1)) for i in range(0, robot.dof): g[i, 0] = U.derivative(robot.q[i, 0]) return g
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 _backward_RNE( rbt, IIi, Si, Vi, dVi, usemotordyn=True, Imzi=None, varify_func=None ) : from copy import copy dof = rbt.dof Tdhi_inv = copy(rbt.Tdhi_inv) Tdhi_inv.append(identity_matrix(4)) Fi = range(0,dof+2) tau = matrix(SR,dof,1) Fi[dof+1] = copy(zero_matrix(SR,6,1)) if usemotordyn: nfi_motor = [zero_matrix(3,1) for i in xrange(dof+1)] for mi,m in enumerate(rbt.motors): if Imzi == None: Im = m[0] else: Im = Imzi[mi] qm = m[1]; l = m[2]; zm = m[3] dqm = qm.subs(rbt.D_q_v2f).derivative(rbt.t).subs(rbt.D_q_f2v) ddqm = dqm.subs(rbt.D_q_v2f).derivative(rbt.t).subs(rbt.D_q_f2v) nfi_motor[l] += ddqm * Im * zm + dqm * Im * cross_product( Vi[l][:3,:] , zm ) # Backward for i in range(dof,0,-1): Fi[i] = Adjdual( Tdhi_inv[i+1], Fi[i+1] ) + IIi[i] * dVi[i] - adjdual( Vi[i], IIi[i] * Vi[i] ) if usemotordyn: Fi[i][:3,:] += nfi_motor[i] if varify_func: Fi[i] = varify_func( Fi[i] , 'F_'+str(i) ) tau[i-1,0] = ( Si[i].transpose() * Fi[i] )[0,0] if usemotordyn: for mi,m in enumerate(rbt.motors): if Imzi == None: Im = m[0] else: Im = Imzi[mi] qm = m[1]; l = m[2]; zm = m[3] dqm = qm.subs(rbt.D_q_v2f).derivative(rbt.t).subs(rbt.D_q_f2v) ddqm = dqm.subs(rbt.D_q_v2f).derivative(rbt.t).subs(rbt.D_q_f2v) km = qm.coeff(rbt.q[i-1,0]) if km != 0.0: tau[i-1,0] += km * Im * ( ( dVi[l][:3,:] + ddqm * zm + dqm * cross_product( Vi[l][:3,:], zm ) ).transpose() * zm )[0,0] return tau
def sympytosage(sm): from copy import copy from sage.all import zero_matrix, SR m = copy(zero_matrix(SR, sm.shape[0], sm.shape[1])) for i in range(m.nrows()): for j in range(m.ncols()): m[i, j] = sm[i, j]._sage_() return m
def symmetrize_periods(Pa, Pb, tol=1e-4): r"""Returns symmetric a- and b-periods `Pa_symm` and `Pb_symm`, as well as the corresponding symplectic operator `Gamma` such that `Gamma [Pa \\ Pb] = [Pa_symm \\ Pb_symm]`. Parameters ---------- Pa : complex matrix Pb : complex matrix The a- and b-periods, respectively, of a genus `g` Riemann surface. tol : double (Default: 1e-4) Tolerance used to verify integrality of intermediate matrices. Dependent on precision of period matrices. Returns ------- Gamma : integer matrix The symplectic transformation operator. Pa : complex matrix Pb : complex matrix Symmetric a- and b-periods, respectively, of a genus `g` Riemann surface. Notes ----- The algorithm described in Kalla, Klein actually operates on the transposes of the a- and b-period matrices. """ # coerce from numpy, if necessary if isinstance(Pa, numpy.ndarray): Pa = Matrix(CDF, numpy.ascontiguousarray(Pa)) if isinstance(Pb, numpy.ndarray): Pb = Matrix(CDF, numpy.ascontiguousarray(Pb)) # use the transposes of the period matrices and coerce to Sage matrices Pa = Pa.T Pb = Pb.T # use above functions to obtain topological type matrix g, g = Pa.dimensions() R = involution_matrix(Pa, Pb, tol=tol) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S, tol=tol) H, Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=tol) # compute the corresponding symmetric periods stacked_periods = zero_matrix(CDF, 2 * g, g) stacked_periods[:g, :] = Pa stacked_periods[g:, :] = Pb stacked_symmetric_periods = Gamma * stacked_periods Pa_symm = stacked_symmetric_periods[:g, :] Pb_symm = stacked_symmetric_periods[g:, :] # transpose results back Pa_symm = Pa_symm.T Pb_symm = Pb_symm.T return Pa_symm, Pb_symm
def symmetrize_periods(Pa, Pb, tol=1e-4): r"""Returns symmetric a- and b-periods `Pa_symm` and `Pb_symm`, as well as the corresponding symplectic operator `Gamma` such that `Gamma [Pa \\ Pb] = [Pa_symm \\ Pb_symm]`. Parameters ---------- Pa : complex matrix Pb : complex matrix The a- and b-periods, respectively, of a genus `g` Riemann surface. tol : double (Default: 1e-4) Tolerance used to verify integrality of intermediate matrices. Dependent on precision of period matrices. Returns ------- Gamma : integer matrix The symplectic transformation operator. Pa : complex matrix Pb : complex matrix Symmetric a- and b-periods, respectively, of a genus `g` Riemann surface. Notes ----- The algorithm described in Kalla, Klein actually operates on the transposes of the a- and b-period matrices. """ # coerce from numpy, if necessary if isinstance(Pa, numpy.ndarray): Pa = Matrix(CDF, numpy.ascontiguousarray(Pa)) if isinstance(Pb, numpy.ndarray): Pb = Matrix(CDF, numpy.ascontiguousarray(Pb)) # use the transposes of the period matrices and coerce to Sage matrices Pa = Pa.T Pb = Pb.T # use above functions to obtain topological type matrix g,g = Pa.dimensions() R = involution_matrix(Pa, Pb, tol=tol) S = integer_kernel_basis(R) N1 = N1_matrix(Pa, Pb, S, tol=tol) H,Q = symmetric_block_diagonalize(N1) Gamma = symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=tol) # compute the corresponding symmetric periods stacked_periods = zero_matrix(CDF, 2*g, g) stacked_periods[:g,:] = Pa stacked_periods[g:,:] = Pb stacked_symmetric_periods = Gamma*stacked_periods Pa_symm = stacked_symmetric_periods[:g,:] Pb_symm = stacked_symmetric_periods[g:,:] # transpose results back Pa_symm = Pa_symm.T Pb_symm = Pb_symm.T return Pa_symm, Pb_symm
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 _forward_RNE( rbt, Si, varify_func=None ): from copy import copy dof = rbt.dof Vi = range(0,dof+1) dVi = range(0,dof+1) Vi[0] = copy(zero_matrix(SR,6,1)) dVi[0] = - zero_matrix(SR,3,1).stack( rbt.grav ) # Forward for i in range(1,dof+1): Vi[i] = Adj( rbt.Tdhi_inv[i], Vi[i-1] ) + Si[i] * rbt.dq[i-1,0] if varify_func: Vi[i] = varify_func( Vi[i] , 'V_'+str(i) ) dVi[i] = Si[i] * rbt.ddq[i-1,0] + Adj( rbt.Tdhi_inv[i], dVi[i-1] ) + adj( Adj( rbt.Tdhi_inv[i], Vi[i-1] ), Si[i] * rbt.dq[i-1,0] ) if varify_func: dVi[i] = varify_func( dVi[i] , 'dV_'+str(i) ) return Vi, dVi
def _gen_ccfmatrix_EL(robot, massmatrix=None, usemotordyn=True): M = massmatrix if massmatrix else gen_massmatrix_EL(robot, usemotordyn) from copy import copy C = copy(zero_matrix(SR, robot.dof, robot.dof)) for i, j in [(i, j) for i in range(robot.dof) for j in range(robot.dof)]: C[i, j] = 0 for k in range(robot.dof): C[i, j] += 1.0 / 2.0 * ( M[i, j].derivative(robot.q[k, 0]) + M[i, k].derivative(robot.q[j, 0]) - M[j, k].derivative(robot.q[i, 0])) * robot.dq[k, 0] return C
def _gen_massmatrix_EL(robot, usemotordyn=True): print "Warning: result uses elementary dynamic parameters (opposed to grouped parameters to which dynamic model is linear)." if usemotordyn: raise Exception( 'gen_massmatrix_EL has no implementation for usemotordyn=True yet') from copy import copy M = copy(zero_matrix(SR, robot.dof, robot.dof)) for i in range(1, robot.dof + 1): M = M + robot.mi[i] * robot.Jcpi[i].transpose( ) * robot.Jcpi[i] + robot.Jcoi[i].transpose() * robot.Ri[ i] * robot.Ici[i] * robot.Ri[i].transpose() * robot.Jcoi[i] #for i in range(1 ,robot.dof+1 ): #M = M + robot.mi[i]*robot.Jcpi[i].transpose()*robot.Jcpi[i] + robot.Jcoi[i].transpose()*robot.Ri[i]*( robot.Ifi[i] - robot.mi[i] * utils.skew( robot.Ri[i]*robot.li[i] ).transpose() * utils.skew( robot.Ri[i]*robot.li[i] ) )*robot.Ri[i].transpose()*robot.Jcoi[i] return M
def tau_2_M(robot,tau,g=None): from sage.all import zero_matrix, SR from copy import copy M = copy(zero_matrix(SR,robot.dof,robot.dof)) Dg = robot._D_grav_2_zero() if Dg: for i in range(0 ,robot.dof) : M[:,i] = tau( dict( utils.subsm( robot.ddq , identity_matrix(robot.dof)[:,i] ).items() + utils.subsm( robot.dq , zero_matrix(robot.dof,1 ) ).items() + Dg.items() ) ) else: if not g: raise Exception('tau_2_M: needs the g parameter') for i in range(0 ,robot.dof) : M[:,i] = tau( dict( utils.subsm( robot.ddq , identity_matrix(robot.dof)[:,i] ).items() + utils.subsm( robot.dq , zero_matrix(robot.dof,1 ) ).items() ) ) - g return M
def QuadraticForm_from_quadric(Q): """ On input a homogeneous polynomial of degree 2 return the Quadratic Form corresponding to it. """ R = Q.parent() assert all([sum(e)==2 for e in Q.exponents()]) M = copy(zero_matrix(R.ngens(),R.ngens())) for i in range(R.ngens()): for j in range(R.ngens()): if i==j: M[i,j]=2*Q.coefficient(R.gen(i)*R.gen(j)) else: M[i,j]=Q.coefficient(R.gen(i)*R.gen(j)) return QuadraticForm(M)
def compute_R(Gamma, H): H = H.change_ring(ZZ) g,g = H.dimensions() A = Gamma[:g,:g] B = Gamma[:g,g:] C = Gamma[g:,:g] D = Gamma[g:,g:] Ig = identity_matrix(ZZ,g) R = zero_matrix(ZZ,2*g,2*g) R[:g,:g] = (2*C.T*B - A.T*H*B + Ig).T R[:g,g:] = 2*D.T*B - B.T*H*B R[g:,:g] = -2*C.T*A + A.T*H*A R[g:,g:] = -(2*C.T*B - A.T*H*B + Ig) return R
def compute_R(Gamma, H): H = H.change_ring(ZZ) g, g = H.dimensions() A = Gamma[:g, :g] B = Gamma[:g, g:] C = Gamma[g:, :g] D = Gamma[g:, g:] Ig = identity_matrix(ZZ, g) R = zero_matrix(ZZ, 2 * g, 2 * g) R[:g, :g] = (2 * C.T * B - A.T * H * B + Ig).T R[:g, g:] = 2 * D.T * B - B.T * H * B R[g:, :g] = -2 * C.T * A + A.T * H * A R[g:, g:] = -(2 * C.T * B - A.T * H * B + Ig) return R
def parm_identbase(Y,P): from sage.all import zero_matrix m = Y.nrows() n = P.nrows() nonzerol = range(0,n) for i in range(0,n) : #if Y[:,i].is_zero() : if bool(Y[:,i] == zero_matrix(m,1)) : nonzerol.remove(i) PB = P[nonzerol,:] YB = Y[:,nonzerol] return YB,PB
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 N1_matrix(Pa, Pb, S, tol=1e-4): r"""Returns the matrix `N1` from the integer kernel of the anti-holomorphic involution matrix. This matrix `N1` is used directly to determine the topological type of a Riemann surface. Used as input in `symmetric_block_diagonalize`. Paramters --------- S : integer matrix A `2g x g` Z-basis of the kernel of the anti-holomorphic involution. (See `integer_kernel_basis`.) tol : double (Default: 1e-4) Tolerance used to veryify integrality of the matrix. Dependent on precision of period matrices. Returns ------- N1 : GF(2) matrix A `g x g` matrix from which we can compute the topological type. """ # compute the Smith normal form of S, itself g = S.ncols() S1 = S[:g,:] S2 = S[g:,:] ES, US, VS = S.smith_form() # construct the matrix N1 piece by piece Nper = zero_matrix(RDF, 2*g,g) Nper[:g,:] = -Re(Pb)[:,:] Nper[g:,:] = Re(Pa)[:,:] Nhat = (S1.T*Re(Pa) + S2.T*Re(Pb)).inverse() Ntilde = 2*US*Nper*Nhat N1_RDF = VS*Ntilde[:g,:] N1 = N1_RDF.round().change_ring(GF(2)) # sanity check: N1 should be integral error = (N1_RDF.round() - N1_RDF).norm() if error > tol: raise ValueError("The N1 matrix is not integral. Try increasing the " "precision of the input period matrices.") return N1
def N1_matrix(Pa, Pb, S, tol=1e-4): r"""Returns the matrix `N1` from the integer kernel of the anti-holomorphic involution matrix. This matrix `N1` is used directly to determine the topological type of a Riemann surface. Used as input in `symmetric_block_diagonalize`. Paramters --------- S : integer matrix A `2g x g` Z-basis of the kernel of the anti-holomorphic involution. (See `integer_kernel_basis`.) tol : double (Default: 1e-4) Tolerance used to veryify integrality of the matrix. Dependent on precision of period matrices. Returns ------- N1 : GF(2) matrix A `g x g` matrix from which we can compute the topological type. """ # compute the Smith normal form of S, itself g = S.ncols() S1 = S[:g, :] S2 = S[g:, :] ES, US, VS = S.smith_form() # construct the matrix N1 piece by piece Nper = zero_matrix(RDF, 2 * g, g) Nper[:g, :] = -Re(Pb)[:, :] Nper[g:, :] = Re(Pa)[:, :] Nhat = (S1.T * Re(Pa) + S2.T * Re(Pb)).inverse() Ntilde = 2 * US * Nper * Nhat N1_RDF = VS * Ntilde[:g, :] N1 = N1_RDF.round().change_ring(GF(2)) # sanity check: N1 should be integral error = (N1_RDF.round() - N1_RDF).norm() if error > tol: raise ValueError("The N1 matrix is not integral. Try increasing the " "precision of the input period matrices.") return N1
def _convert_kappa_basis(self,r): """ Returns a matrix that when multiplied on the right of the FZ_matrix with pushforward of psi basis will give the FZ_matrix for the kappa monomial basis. """ M = zero_matrix(len(self._dstrata[r])) for Gind,G in enumerate(self._dstrata[r].keys()): #print G #Gf = forget_decorations(G) R, kappa = PsiKappaVars(G,kappa_only = True) kappa_dec = 1 for v in range(1, G.num_vertices()+1): #print kappa, G.M[v,0] kappa_dec *= X_to_kappa(G.M[v,0],lambda a: kappa[(a,v)]) #print G.M[v,0], X_to_kappa(G.M[v,0],lambda a: kappa[(a,v)]) #print "kd", kappa_dec if kappa_dec == 1: M[Gind,Gind] = 1 else: for mon, coef in kappa_dec.dict().items(): Gd = decorate_with_monomial(R, G.forget_kappas(), mon) M[Gind, self._dstrata[r][Gd]] += coef return M #.transpose()
def symmetric_transformation_matrix(Pa, Pb, S, H, Q, tol=1e-4): r"""Returns the symplectic matrix `\Gamma` mapping the period matrices `Pa,Pb` to a symmetric period matrices. A helper function to :func:`symmetrize_periods`. Parameters ---------- Pa : complex matrix A `g x g` a-period matrix. Pb : complex matrix A `g x g` b-period matrix. S : integer matrix Integer kernel basis matrix. H : integer matrix Topological type classification matrix. Q : integer matrix The transformation matrix from `symmetric_block_diagonalize`. tol : double (Default: 1e-4) Tolerance used to verify integrality of intermediate matrices. Dependent on precision of period matrices. Returns ------- Gamma : integer matrix A `2g x 2g` symplectic matrix. """ # compute A and B g, g = Pa.dimensions() rhs = S * Q.change_ring(ZZ) A = rhs[:g, :g].T B = rhs[g:, :g].T H = H.change_ring(ZZ) # compute C and D half = QQ(1) / QQ(2) temp = (A * Re(Pa) + B * Re(Pb)).inverse() CT = half * A.T * H - Re(Pb) * temp CT_ZZ = CT.round().change_ring(ZZ) C = CT_ZZ.T DT = half * B.T * H + Re(Pa) * temp DT_ZZ = DT.round().change_ring(ZZ) D = DT_ZZ.T # sanity checks: make sure C and D are integral C_error = (CT.round() - CT).norm() D_error = (DT.round() - DT).norm() if (C_error > tol) or (D_error > tol): raise ValueError( "The symmetric transformation matrix is not integral. " "Try increasing the precision of the input period " "matrices." ) # construct Gamma Gamma = zero_matrix(ZZ, 2 * g, 2 * g) Gamma[:g, :g] = A Gamma[:g, g:] = B Gamma[g:, :g] = C Gamma[g:, g:] = D return Gamma
def symmetric_block_diagonalize(N1): r"""Returns matrices `H` and `Q` such that `N1 = Q*H*Q.T` and `H` is block diagonal. The algorithm used here is as follows. Whenever a row operation is performed (via multiplication on the left by a transformation matrix `q`) the corresponding symmetric column operation is also performed via multiplication on the right by `q^T`. For each column `j` of `N1`: 1. If column `j` consists only of zeros then swap with the last column with non-zero entries. 2. If there is a `1` in position `j` of the column (i.e. a `1` lies on the diagonal in this column) then eliminate further entries below as in standard Gaussian elimination. 3. Otherwise, if there is a `1` in the column, but not in position `j` then rows are swapped in a way that it appears in the position `j+1` of the column. Eliminate further entries below as in standard Gaussian elimination. 4. After elimination, if `1` lies on the diagonal in column `j` then increment `j` by one. If instead the block matrix `[0 1 \\ 1 0]` lies along the diagonal then eliminate under the `(j,j+1)` element (the upper right element) of this `2 x 2` block and increment `j` by two. 5. Repeat until `j` passes the final column or until further columns consists of all zeros. 6. Finally, perform the appropriate transformations such that all `2 x 2` blocks in `H` appear first in the diagonalization. (Uses the `diagonal_locations` helper function.) Parameters ---------- N1 : GF(2) matrix Returns ------- H : GF(2) matrix Symmetric `g x g` matrix where the diagonal elements consist of either a "1" or a `2 x 2` block matrix `[0 1 \\ 1 0]`. Q : GF(2) matrix The corresponding transformation matrix. """ g = N1.nrows() H = zero_matrix(GF(2), g) Q = identity_matrix(GF(2), g) # if N1 is the zero matrix the H is also the zero matrix (and Q is the # identity transformation) if (N1 % 2) == 0: return H, Q # perform the "modified gaussian elimination" B = Matrix(GF(2), [[0, 1], [1, 0]]) H = N1.change_ring(GF(2)) j = 0 while (j < g) and (H[:, j:] != 0): # if the current column is zero then swap with the last non-zero column if H.column(j) == 0: last_non_zero_col = max(k for k in range(j, g) if H.column(k) != 0) Q.swap_columns(j, last_non_zero_col) H = Q.T * N1 * Q # if the current diagonal element is 1 then gaussian eliminate as # usual. otherwise, swap rows so that a "1" appears in H[j+1,j] and # then eliminate from H[j+1,j] if H[j, j] == 1: rows_to_eliminate = (r for r in range(g) if H[r, j] == 1 and r != j) for r in rows_to_eliminate: Q.add_multiple_of_column(r, j, 1) H = Q.T * N1 * Q else: # find the first non-zero element in the column after the diagonal # element and swap rows with this element first_non_zero = min(k for k in range(j, g) if H[k, j] != 0) Q.swap_columns(j + 1, first_non_zero) H = Q.T * N1 * Q # eliminate *all* other ones in the column, including those above # the element (j,j+1) rows_to_eliminate = (r for r in range(g) if H[r, j] == 1 and r != j + 1) for r in rows_to_eliminate: Q.add_multiple_of_column(r, j + 1, 1) H = Q.T * N1 * Q # increment the column based on the diagonal element if H[j, j] == 1: j += 1 elif H[j : (j + 2), j : (j + 2)] == B: # in the block diagonal case, need to eliminate below the j+1 term rows_to_eliminate = (r for r in range(g) if H[r, j + 1] == 1 and r != j) for r in rows_to_eliminate: Q.add_multiple_of_column(r, j, 1) H = Q.T * N1 * Q j += 2 # finally, check if there are blocks of "special" form. that is, shift all # blocks such that they occur first along the diagonal of H index_one, index_B = diagonal_locations(H) while index_one < index_B: j = index_B Qtilde = zero_matrix(GF(2), g) Qtilde[0, 0] = 1 Qtilde[j, 0] = 1 Qtilde[j + 1, 0] = 1 Qtilde[0, j] = 1 Qtilde[0, j + 1] = 1 Qtilde[j : (j + 2), j : (j + 2)] = B Q = Q * Qtilde H = Q.T * N1 * Q # continue until none are left index_one, index_B = diagonal_locations(H) # above, we used Q to store column operations on N1. switch to rows # operations on H so that N1 = Q*H*Q.T Q = Q.T.inverse() return H, Q
def t1_prime(self, n=5, p=65521): """ Return a multiple of element t1 of the Hecke algebra mod 2, computed using the Hecke operator $T_n$, where n is self.n. To make computation faster we only check if ...==0 mod p. Hence J will contain more elements, hence we get a multiple. INPUT: - `n` -- integer (optional default=5) - `p` -- prime (optional default=65521) OUTPUT: - a mod 2 matrix EXAMPLES:: sage: from mdsage import * sage: C = KamiennyCriterion(29) sage: C.t1_prime() 22 x 22 dense matrix over Finite Field of size 2 (use the '.str()' method to see the entries) sage: C.t1_prime(n=3) == 1 True sage: C = KamiennyCriterion(37) sage: C.t1_prime()[0] (1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0) """ if self.verbose: tm = cputime(); mem = get_memory_usage(); print "t1 start" T = self.S.hecke_matrix(n) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "hecke 1" f = self.hecke_polynomial(n) # this is the same as T.charpoly() if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "char 1" Fint = f.factor() if all(i[1]!=1 for i in Fint): return matrix_modp(zero_matrix(T.nrows())) # raise ValueError("T_%s needs to be a generator of the hecke algebra"%n) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "factor 1, Fint = %s"%(Fint) R = f.parent().change_ring(GF(p)) F = Fint.base_change(R) # Compute the iterators of T acting on the winding element. e = self.M([0, oo]).element().dense_vector().change_ring(GF(p)) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "wind" t = matrix_modp(self.M.hecke_matrix(n).dense_matrix(), p) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "hecke 2" g = t.charpoly() if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "char 2" Z = t.iterates(e, t.nrows(), rows=True) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "iter" # We find all factors F[i][0] for f such that # (g/F[i][0])(t) * e = 0. # We do this by computing the polynomial # h = g/F[i][0], # turning it into a vector v, and computing # the matrix product v * Z. If the product # is 0, then e is killed by h(t). J = [] for i in range(len(F)): if F[i][1]!=1: J.append(i) continue h, r = g.quo_rem(F[i][0] ** F[i][1]) assert r == 0 v = vector(GF(p), h.padded_list(t.nrows())) if v * Z == 0: J.append(i) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "zero check" if self.verbose: print "J =", J if len(J) == 0: # The annihilator of e is the 0 ideal. return matrix_modp(identity_matrix(T.nrows())) # Finally compute t1. I'm concerned about how # long this will take, so we reduce T mod 2 first. # It is important to call "self.T(2)" to get the mod-2 # reduction of T2 with respect to the right basis (e.g., the # integral basis in case use_integral_structure is true. Tmod2 = self.T(n) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "hecke mod" g = prod(Fint[i][0].change_ring(GF(2)) ** Fint[i][1] for i in J) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "g has degree %s"%(g.degree()) t1 = g(Tmod2) if self.verbose: print "time and mem", cputime(tm), get_memory_usage(mem), "t1 finnished" return t1
def symmetric_block_diagonalize(N1): r"""Returns matrices `H` and `Q` such that `N1 = Q*H*Q.T` and `H` is block diagonal. The algorithm used here is as follows. Whenever a row operation is performed (via multiplication on the left by a transformation matrix `q`) the corresponding symmetric column operation is also performed via multiplication on the right by `q^T`. For each column `j` of `N1`: 1. If column `j` consists only of zeros then swap with the last column with non-zero entries. 2. If there is a `1` in position `j` of the column (i.e. a `1` lies on the diagonal in this column) then eliminate further entries below as in standard Gaussian elimination. 3. Otherwise, if there is a `1` in the column, but not in position `j` then rows are swapped in a way that it appears in the position `j+1` of the column. Eliminate further entries below as in standard Gaussian elimination. 4. After elimination, if `1` lies on the diagonal in column `j` then increment `j` by one. If instead the block matrix `[0 1 \\ 1 0]` lies along the diagonal then eliminate under the `(j,j+1)` element (the upper right element) of this `2 x 2` block and increment `j` by two. 5. Repeat until `j` passes the final column or until further columns consists of all zeros. 6. Finally, perform the appropriate transformations such that all `2 x 2` blocks in `H` appear first in the diagonalization. (Uses the `diagonal_locations` helper function.) Parameters ---------- N1 : GF(2) matrix Returns ------- H : GF(2) matrix Symmetric `g x g` matrix where the diagonal elements consist of either a "1" or a `2 x 2` block matrix `[0 1 \\ 1 0]`. Q : GF(2) matrix The corresponding transformation matrix. """ g = N1.nrows() H = zero_matrix(GF(2), g) Q = identity_matrix(GF(2), g) # if N1 is the zero matrix the H is also the zero matrix (and Q is the # identity transformation) if (N1 % 2) == 0: return H,Q # perform the "modified gaussian elimination" B = Matrix(GF(2),[[0,1],[1,0]]) H = N1.change_ring(GF(2)) j = 0 while (j < g) and (H[:,j:] != 0): # if the current column is zero then swap with the last non-zero column if H.column(j) == 0: last_non_zero_col = max(k for k in range(j,g) if H.column(k) != 0) Q.swap_columns(j,last_non_zero_col) H = Q.T*N1*Q # if the current diagonal element is 1 then gaussian eliminate as # usual. otherwise, swap rows so that a "1" appears in H[j+1,j] and # then eliminate from H[j+1,j] if H[j,j] == 1: rows_to_eliminate = (r for r in range(g) if H[r,j] == 1 and r != j) for r in rows_to_eliminate: Q.add_multiple_of_column(r,j,1) H = Q.T*N1*Q else: # find the first non-zero element in the column after the diagonal # element and swap rows with this element first_non_zero = min(k for k in range(j,g) if H[k,j] != 0) Q.swap_columns(j+1,first_non_zero) H = Q.T*N1*Q # eliminate *all* other ones in the column, including those above # the element (j,j+1) rows_to_eliminate = (r for r in range(g) if H[r,j] == 1 and r != j+1) for r in rows_to_eliminate: Q.add_multiple_of_column(r,j+1,1) H = Q.T*N1*Q # increment the column based on the diagonal element if H[j,j] == 1: j += 1 elif H[j:(j+2),j:(j+2)] == B: # in the block diagonal case, need to eliminate below the j+1 term rows_to_eliminate = (r for r in range(g) if H[r,j+1] == 1 and r != j) for r in rows_to_eliminate: Q.add_multiple_of_column(r,j,1) H = Q.T*N1*Q j += 2 # finally, check if there are blocks of "special" form. that is, shift all # blocks such that they occur first along the diagonal of H index_one, index_B = diagonal_locations(H) while index_one < index_B: j = index_B Qtilde = zero_matrix(GF(2), g) Qtilde[0,0] = 1 Qtilde[j,0] = 1; Qtilde[j+1,0] = 1 Qtilde[0,j] = 1; Qtilde[0,j+1] = 1 Qtilde[j:(j+2),j:(j+2)] = B Q = Q*Qtilde H = Q.T*N1*Q # continue until none are left index_one, index_B = diagonal_locations(H) # above, we used Q to store column operations on N1. switch to rows # operations on H so that N1 = Q*H*Q.T Q = Q.T.inverse() return H,Q
def upper_bound_tate(cp, frob_matrix, precision, over_Qp=False, pedantic=True): """ Return a upper bound for Tate classes over characteristic 0 TODO: improove documentation """ p = cp.list()[-1].prime_factors()[0] # it would be nice to use QpLF OK = ZpCA(p, prec=precision) # adjust precision frob_matrix = matrix(OK, frob_matrix) # get the p-adic eigenvalues _, _, cyc_factorization = rank_fieldextension(cp) # a bit hacky val = [ min(elt.valuation() for elt in col) for col in frob_matrix.columns() ] projection_cols = frob_matrix.ncols() - val.index(0) assert set(val[-projection_cols:]) == {0} # P1 = | zero matrix | # | Identity | P1 = zero_matrix(frob_matrix.ncols() - projection_cols, projection_cols).stack(identity_matrix(projection_cols)) # computing a kernel, either via smith form or howell form # involves some kind of gauss elimination, # and thus having the columns with lowest valuation first improves # the numerical stability of the algorithms P1.reverse_rows_and_columns() frob_matrix.reverse_rows_and_columns() @cached_function def frob_power(k): if k == 0: return identity_matrix(frob_matrix.ncols()) elif k == 1: return frob_matrix else: return frob_matrix * frob_power(k - 1) factor_i = [] dim_Ti = [] obsi = [] dim_Li = [] for cyc_fac, cyc_exp in cyc_factorization: factor_i.append((cyc_fac, cyc_exp)) Ti = matrix(0, frob_matrix.ncols()) obsij = [] dim_Tij = [] dim_Lij = [] for fac, exp in tate_factor_Zp(cyc_fac): # the rows of Tij are a basis for Tij # the 'computed' argument avoids echelonizing the kernel basis # which might induce some precision loss on the projection #Tij = fac(frob_matrix).right_kernel_matrix(basis='computed') # computing the right kernel with smith form # howell form or strong echelon could also be good options Tij = padic_right_kernel_matrix(fac(frob_matrix)) if Tij.nrows() != fac.degree() * exp * cyc_exp: raise PrecisionError( "Number of eigenvectors (%d) doesn't match the number of eigenvalues (%d), increasing precision should solve this" % (Tij.nrows(), fac.degree() * exp * cyc_exp)) if over_Qp: dim_Tij.append(Tij.nrows()) obs_map = Tij * P1 rank_obs_ij = obs_map.rank() obsij.append(rank_obs_ij) Lijmatrix = matrix(Tij.base_ring(), Tij.nrows(), 0) for ell in range(fac.degree()): Lijmatrix = Lijmatrix.augment( Tij * frob_power(ell).transpose() * P1) # Lij = right_kernel(K) subspace of Tij that is invariant under Frob and unobstructed Krank = Lijmatrix.rank() dim_Lij.append(Tij.nrows() - Krank) if dim_Lij[-1] % fac.degree() != 0: old_dim = dim_Li[-1] deg = fac.degree() new_dim = dim_Li[-1] = deg * (old_dim // deg) if pedantic: warnings.warn( "rounding dimension of Li from %d to %d for factor = %s" % (old_dim, new_dim, fac)) Ti = Ti.stack(Tij) if over_Qp: dim_Ti.append(dim_Tij) obsi.append(obsij) dim_Li.append(dim_Lij) else: obs_map = Ti * P1 if Ti.nrows() != cyc_fac.degree() * cyc_exp: raise PrecisionError( "Number of eigenvectors (%d) doesn't match the number of eigenvalues (%d), increasing precision should solve this" % (Tij.nrows(), cyc_fac.degree() * cyc_exp)) dim_Ti.append(Ti.nrows()) rank_obs_i = padic_rank(obs_map) obsi.append(Ti.nrows() - rank_obs_i) Limatrix = matrix(Ti.base_ring(), Ti.nrows(), 0) for ell in range(0, cyc_fac.degree()): Limatrix = Limatrix.augment(Ti * frob_power(ell).transpose() * P1) #print(Limatrix.smith_form(exact=False, integral=True, transformation=False).diagonal()) # Li = right_kernel(K) subspace of Tij that is invariant under Frob and unobstructed Krank = padic_rank(Limatrix) dim_Li.append(Ti.nrows() - Krank) if dim_Li[-1] % cyc_fac.degree() != 0: old_dim = dim_Li[-1] deg = cyc_fac.degree() new_dim = dim_Li[-1] = deg * (old_dim // deg) if pedantic: warnings.warn( "rounding dimension of Li from %d to %d for cyc_factor = %s" % (old_dim, new_dim, cyc_fac)) return factor_i, dim_Ti, obsi, dim_Li,
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)