def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. Let ``i_b`` be the ``b``-th bit of ``i`` and ``o_b`` the ``b``-th bit of ``o``. Then ``v = A[i,o]`` encodes the bias of the equation ``sum( i_b * x_i ) = sum( o_b * y_i )`` if ``x_i`` and ``y_i`` represent the input and output variables of the S-box. See [He2002]_ for an introduction to linear cryptanalysis. EXAMPLES:: sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.linear_approximation_matrix() [ 4 0 0 0 0 0 0 0] [ 0 0 0 0 2 2 2 -2] [ 0 0 -2 -2 -2 2 0 0] [ 0 0 -2 2 0 0 -2 -2] [ 0 2 0 2 -2 0 2 0] [ 0 -2 0 2 0 2 0 2] [ 0 -2 -2 0 0 -2 2 0] [ 0 -2 2 0 -2 0 0 -2] According to this matrix the first bit of the input is equal to the third bit of the output 6 out of 8 times:: sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2]) False True True True False True True True """ m = self.m n = self.n nrows = 1<<m ncols = 1<<n B = BooleanFunction(self.m) L = [] for j in range(ncols): for i in range(nrows): B[i] = ZZ(self(i)&j).popcount() L.append(B.walsh_hadamard_transform()) A = Matrix(ZZ, ncols, nrows, L) A = -A.transpose()/2 A.set_immutable() return A
def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. Let ``i_b`` be the ``b``-th bit of ``i`` and ``o_b`` the ``b``-th bit of ``o``. Then ``v = A[i,o]`` encodes the bias of the equation ``sum( i_b * x_i ) = sum( o_b * y_i )`` if ``x_i`` and ``y_i`` represent the input and output variables of the S-box. See [He2002]_ for an introduction to linear cryptanalysis. EXAMPLES:: sage: from sage.crypto.sbox import SBox sage: S = SBox(7,6,0,4,2,5,1,3) sage: S.linear_approximation_matrix() [ 4 0 0 0 0 0 0 0] [ 0 0 0 0 2 2 2 -2] [ 0 0 -2 -2 -2 2 0 0] [ 0 0 -2 2 0 0 -2 -2] [ 0 2 0 2 -2 0 2 0] [ 0 -2 0 2 0 2 0 2] [ 0 -2 -2 0 0 -2 2 0] [ 0 -2 2 0 -2 0 0 -2] According to this matrix the first bit of the input is equal to the third bit of the output 6 out of 8 times:: sage: for i in srange(8): print(S.to_bits(i)[0] == S.to_bits(S(i))[2]) False True True True False True True True """ m = self.m n = self.n nrows = 1<<m ncols = 1<<n B = BooleanFunction(self.m) L = [] for j in range(ncols): for i in range(nrows): B[i] = ZZ(self(i)&j).popcount() L.append(B.walsh_hadamard_transform()) A = Matrix(ZZ, ncols, nrows, L) A = -A.transpose()/2 A.set_immutable() return A
class HarmonicCocycles(AmbientHeckeModule): Element=HarmonicCocycleElement r""" This object represents a space of Gamma invariant harmonic cocycles valued in a cofficient module. INPUT: - ``X`` - A BTQuotient object - ``k`` - integer - The weight. - ``prec`` - integer (Default: None). If specified, the precision for the coefficient module - ``basis_matrix`` - integer (Default: None) - ``base_field`` - (Default: None) EXAMPLES: :: AUTHORS: - Cameron Franc (2012-02-20) - Marc Masdeu """ def __init__(self,X,k,prec=None,basis_matrix=None,base_field=None): self._k=k self._X=X self._E=self._X.get_edge_list() self._V=self._X.get_vertex_list() if prec is None: self._prec=None if base_field is None: try: self._R= X.get_splitting_field() except AttributeError: raise ValueError, "It looks like you are not using Magma as backend...and still we don't know how to compute splittings in that case!" else: pol=X.get_splitting_field().defining_polynomial().factor()[0][0] self._R=base_field.extension(pol,pol.variable_name()).absolute_field(name='r') self._U=OCVn(self._k-2,self._R) else: self._prec=prec if base_field is None: self._R=Qp(self._X._p,prec=prec) else: self._R=base_field self._U=OCVn(self._k-2,self._R,self._k-1) self.__rank = self._X.dimension_harmonic_cocycles(self._k) if basis_matrix is not None: self.__matrix=basis_matrix self.__matrix.set_immutable() assert self.__rank == self.__matrix.nrows() AmbientHeckeModule.__init__(self, self._R, self.__rank, self._X.prime()*self._X.Nplus()*self._X.Nminus(), weight=self._k) self._populate_coercion_lists_() def base_extend(self,base_ring): r""" This function extends the base ring of the coefficient module. INPUT: - ``base_ring`` - a ring that has a coerce map from the current base ring EXAMPLES: :: """ if not base_ring.has_coerce_map_from(self.base_ring()): raise ValueError, "No coercion defined" else: return self.change_ring(base_ring) def change_ring(self, new_base_ring): r""" This function changes the base ring of the coefficient module. INPUT: - ``new_base_ring'' - a ring that has a coerce map from the current base ring EXAMPLES: :: """ if not new_base_ring.has_coerce_map_from(self.base_ring()): raise ValueError, "No coercion defined" else: return self.__class__(self._X,self._k,prec=self._prec,basis_matrix=self.basis_matrix().change_ring(base_ring),base_field=new_base_ring) def rank(self): r""" The rank (dimension) of ``self``. EXAMPLES: :: """ return self.__rank def submodule(self,v,check=False): r""" Return the submodule of ``self`` spanned by ``v``. EXAMPLES: """ return HarmonicCocyclesSubmodule(self,v,dual=None,check=check) def is_simple(self): r""" Whether ``self`` is irreducible. EXAMPLES: :: """ return self.rank()==1 def _repr_(self): r""" This returns the representation of self as a string. """ return 'Space of harmonic cocycles of weight %s on %s'%(self._k,self._X) def _latex_(self): r""" A LaTeX representation of ``self``. EXAMPLES: :: """ s='\\text{Space of harmonic cocycles of weight }'+latex(self._k)+'\\text{ on }'+latex(self._X) return s def _an_element_(self): r""" """ return self.basis()[0] def _coerce_map_from_(self, S): r""" Can coerce from other HarmonicCocycles or from pAutomorphicForms """ if isinstance(S,(HarmonicCocycles,pAutomorphicForms)): if(S._k!=self._k): return False if(S._X!=self._X): return False return True return False def __cmp__(self,other): r""" """ try: res=(self.base_ring()==other.base_ring() and self._X==other._X and self._k==other._k) return res except: return False def _element_constructor_(self,x): r""" """ #Code how to coherce x into the space #Admissible values of x? if isinstance(x,HarmonicCocycleElement): return HarmonicCocycleElement(self,x) elif isinstance(x,pAutomorphicForm): tmp=[self._U.element_class(_parent._U,x._F[ii]).l_act_by(self._E[ii].rep) for ii in range(self._nE)] return HarmonicCocycleElement(self,tmp,from_values=True) else: return HarmonicCocycleElement(self,x) def free_module(self): r""" This function returns the underlying free module EXAMPLES: :: """ try: return self.__free_module except AttributeError: pass V = self.base_ring()**self.dimension() self.__free_module = V return V def character(self): r""" Only implemented the trivial character so far. EXAMPLES: """ return lambda x:x def embed_quaternion(self,g): r""" Embed the quaternion element ``g`` into the matrix algebra. EXAMPLES: :: """ return self._X.embed_quaternion(g,exact = self._R.is_exact(), prec = self._prec) def basis_matrix(self): r""" Returns a basis of ``self`` in matrix form. If the coefficient module `M` is of finite rank then the space of Gamma invariant `M` valued harmonic cocycles can be represented as a subspace of the finite rank space of all functions from the finitely many edges in the corresponding BTQuotient into `M`. This function computes this representation of the space of cocycles. OUTPUT: A basis matrix describing the cocycles in the spaced of all `M` valued Gamma invariant functions on the tree. EXAMPLES: :: sage: X = BTQuotient(3,19) sage: C = HarmonicCocycles(X,4,prec = 5) sage: B = C.basis() Traceback (most recent call last): ... RuntimeError: The computed dimension does not agree with the expectation. Consider increasing precision! We try increasing the precision: :: sage: C = HarmonicCocycles(X,4,prec = 20) sage: B = C.basis() sage: len(B) == X.dimension_harmonic_cocycles(4) True AUTHORS: - Cameron Franc (2012-02-20) - Marc Masdeu (2012-02-20) """ try: return self.__matrix except AttributeError: pass nV=len(self._V) nE=len(self._E) stab_conds=[] S=self._X.get_edge_stabs() p=self._X._p d=self._k-1 for e in self._E: try: g=filter(lambda g:g[2],S[e.label])[0] C=self._U.l_matrix_representation(self.embed_quaternion(g[0])) C-=self._U.l_matrix_representation(Matrix(QQ,2,2,p**g[1])) stab_conds.append([e.label,C]) except IndexError: pass n_stab_conds=len(stab_conds) self._M=Matrix(self._R,(nV+n_stab_conds)*d,nE*d,0,sparse=True) for v in self._V: for e in filter(lambda e:e.parity==0,v.leaving_edges): C=sum([self._U.l_matrix_representation(self.embed_quaternion(x[0])) for x in e.links],Matrix(self._R,d,d,0)) self._M.set_block(v.label*d,e.label*d,C) for e in filter(lambda e:e.parity==0,v.entering_edges): C=sum([self._U.l_matrix_representation(self.embed_quaternion(x[0])) for x in e.opposite.links],Matrix(self._R,d,d,0)) self._M.set_block(v.label*d,e.opposite.label*d,C) for kk in range(n_stab_conds): v=stab_conds[kk] self._M.set_block((nV+kk)*d,v[0]*d,v[1]) x1=self._M.right_kernel().matrix() if x1.nrows() != self.rank(): raise RuntimeError, 'The computed dimension does not agree with the expectation. Consider increasing precision!' K=[c for c in x1.rows()] if not self._R.is_exact(): for ii in range(len(K)): s=min([t.valuation() for t in K[ii]]) for jj in range(len(K[ii])): K[ii][jj]=(p**(-s))*K[ii][jj] self.__matrix=Matrix(self._R,len(K),nE*d,K) self.__matrix.set_immutable() return self.__matrix def __apply_atkin_lehner(self,q,f): r""" This function applies an Atkin-Lehner involution to a harmonic cocycle INPUT: - ``q`` - an integer dividing the full level p*Nminus*Nplus - ``f`` - a harmonic cocycle OUTPUT: The harmonic cocycle obtained by hitting f with the Atkin-Lehner at q EXAMPLES: :: """ R=self._R Data=self._X._get_atkin_lehner_data(q) p=self._X._p tmp=[self._U.element_class(self._U,zero_matrix(self._R,self._k-1,1),quick=True) for jj in range(len(self._E))] d1=Data[1] mga=self.embed_quaternion(Data[0]) for jj in range(len(self._E)): t=d1[jj] tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma(self.embed_quaternion)) return HarmonicCocycleElement(self,tmp,from_values=True) def __apply_hecke_operator(self,l,f): r""" This function applies a Hecke operator to a harmonic cocycle. INPUT: - ``l`` - an integer - ``f`` - a harmonic cocycle OUTPUT: A harmonic cocycle which is the result of applying the lth Hecke operator to f EXAMPLES: :: """ R=self._R HeckeData,alpha=self._X._get_hecke_data(l) if(self.level()%l==0): factor=QQ(l**(Integer((self._k-2)/2))/(l+1)) else: factor=QQ(l**(Integer((self._k-2)/2))) p=self._X._p alphamat=self.embed_quaternion(alpha) tmp=[self._U.element_class(self._U,zero_matrix(self._R,self._k-1,1),quick=True) for jj in range(len(self._E))] for ii in range(len(HeckeData)): d1=HeckeData[ii][1] mga=self.embed_quaternion(HeckeData[ii][0])*alphamat for jj in range(len(self._E)): t=d1[jj] tmp[jj]+=(t.sign()*f._F[t.label]).l_act_by(p**(-t.power)*mga*t.igamma(self.embed_quaternion)) return HarmonicCocycleElement(self,[factor*x for x in tmp],from_values=True) def _compute_atkin_lehner_matrix(self,d): r""" When the underlying coefficient module is finite, this function computes the matrix of an Atkin-Lehner involution in the basis provided by the function basis_matrix INPUT: - ``d`` - an integer dividing p*Nminus*Nplus OUTPUT: The matrix of the AL-involution at d in the basis given by self.basis_matrix EXAMPLES: :: """ res=self.__compute_operator_matrix(lambda f:self.__apply_atkin_lehner(d,f)) return res def _compute_hecke_matrix_prime(self,l): r""" When the underlying coefficient module is finite, this function computes the matrix of a (prime) Hecke operator in the basis provided by the function basis_matrix INPUT: - ``l`` - an integer prime OUTPUT: The matrix of T_l acting on the cocycles in the basis given by self.basis_matrix EXAMPLES: :: """ res=self.__compute_operator_matrix(lambda f:self.__apply_hecke_operator(l,f)) return res def __compute_operator_matrix(self,T): r""" Compute the matrix of the operator ``T``. EXAMPLES: :: """ R=self._R A=self.basis_matrix().transpose() basis=self.basis() B=zero_matrix(R,len(self._E)*(self._k-1),self.dimension()) for rr in range(len(basis)): g=T(basis[rr]) B.set_block(0,rr,Matrix(R,len(self._E)*(self._k-1),1,[g._F[e]._val[ii,0] for e in range(len(self._E)) for ii in range(self._k-1) ])) try: res=(A.solve_right(B)).transpose() res.set_immutable() return res except ValueError: print A print B raise ValueError
def cartan_invariants_matrix(self): r""" Return the Cartan invariants matrix of the algebra. OUTPUT: a matrix of non negative integers Let `A` be this finite dimensional algebra and `(S_i)_{i\in I}` be representatives of the right simple modules of `A`. Note that their adjoints `S_i^*` are representatives of the left simple modules. Let `(P^L_i)_{i\in I}` and `(P^R_i)_{i\in I}` be respectively representatives of the corresponding indecomposable projective left and right modules of `A`. In particular, we assume that the indexing is consistent so that `S_i^*=\operatorname{top} P^L_i` and `S_i=\operatorname{top} P^R_i`. The *Cartan invariant matrix* `(C_{i,j})_{i,j\in I}` is a matrix of non negative integers that encodes much of the representation theory of `A`; namely: - `C_{i,j}` counts how many times `S_i^*\otimes S_j` appears as composition factor of `A` seen as a bimodule over itself; - `C_{i,j}=\dim Hom_A(P^R_j, P^R_i)`; - `C_{i,j}` counts how many times `S_j` appears as composition factor of `P^R_i`; - `C_{i,j}=\dim Hom_A(P^L_i, P^L_j)`; - `C_{i,j}` counts how many times `S_i^*` appears as composition factor of `P^L_j`. In the commutative case, the Cartan invariant matrix is diagonal. In the context of solving systems of multivariate polynomial equations of dimension zero, `A` is the quotient of the polynomial ring by the ideal generated by the equations, the simple modules correspond to the roots, and the numbers `C_{i,i}` give the multiplicities of those roots. .. NOTE:: For simplicity, the current implementation assumes that the index set `I` is of the form `\{0,\dots,n-1\}`. Better indexations will be possible in the future. ALGORITHM: The Cartan invariant matrix of `A` is computed from the dimension of the summands of its peirce decomposition. .. SEEALSO:: - :meth:`peirce_decomposition` - :meth:`isotypic_projective_modules` EXAMPLES: For a semisimple algebra, in particular for group algebras in chararacteristic zero, the Cartan invariants matrix is the identity:: sage: A3 = SymmetricGroup(3).algebra(QQ) sage: A3.cartan_invariants_matrix() [1 0 0] [0 1 0] [0 0 1] For the path algebra of a quiver, the Cartan invariants matrix counts the number of paths between two vertices:: sage: A = Algebras(QQ).FiniteDimensional().WithBasis().example() sage: A.cartan_invariants_matrix() [1 2] [0 1] In the commutative case, the Cartan invariant matrix is diagonal:: sage: Z12 = Monoids().Finite().example(); Z12 An example of a finite multiplicative monoid: the integers modulo 12 sage: A = Z12.algebra(QQ) sage: A.cartan_invariants_matrix() [1 0 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0 0] [0 0 2 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0] [0 0 0 0 2 0 0 0 0] [0 0 0 0 0 1 0 0 0] [0 0 0 0 0 0 1 0 0] [0 0 0 0 0 0 0 2 0] [0 0 0 0 0 0 0 0 1] With the algebra of the `0`-Hecke monoid:: sage: from sage.monoids.hecke_monoid import HeckeMonoid sage: A = HeckeMonoid(SymmetricGroup(4)).algebra(QQ) sage: A.cartan_invariants_matrix() [1 0 0 0 0 0 0 0] [0 2 1 0 1 1 0 0] [0 1 1 0 1 0 0 0] [0 0 0 1 0 1 1 0] [0 1 1 0 1 0 0 0] [0 1 0 1 0 2 1 0] [0 0 0 1 0 1 1 0] [0 0 0 0 0 0 0 1] """ from sage.rings.integer_ring import ZZ A_quo = self.semisimple_quotient() idempotents_quo = A_quo.central_orthogonal_idempotents() # Dimension of simple modules dim_simples = [A_quo.principal_ideal(e).dimension().sqrt() for e in idempotents_quo] # Orthogonal idempotents idempotents = self.orthogonal_idempotents_central_mod_radical() def C(i,j): summand = self.peirce_summand(idempotents[i], idempotents[j]) return summand.dimension() / (dim_simples[i]*dim_simples[j]) m = Matrix(ZZ, len(idempotents), C) m.set_immutable() return m
def cartan_invariants_matrix(self): r""" Return the Cartan invariants matrix of the algebra. OUTPUT: a matrix of non negative integers Let `A` be this finite dimensional algebra and `(S_i)_{i\in I}` be representatives of the right simple modules of `A`. Note that their adjoints `S_i^*` are representatives of the left simple modules. Let `(P^L_i)_{i\in I}` and `(P^R_i)_{i\in I}` be respectively representatives of the corresponding indecomposable projective left and right modules of `A`. In particular, we assume that the indexing is consistent so that `S_i^*=\operatorname{top} P^L_i` and `S_i=\operatorname{top} P^R_i`. The *Cartan invariant matrix* `(C_{i,j})_{i,j\in I}` is a matrix of non negative integers that encodes much of the representation theory of `A`; namely: - `C_{i,j}` counts how many times `S_i^*\otimes S_j` appears as composition factor of `A` seen as a bimodule over itself; - `C_{i,j}=\dim Hom_A(P^R_j, P^R_i)`; - `C_{i,j}` counts how many times `S_j` appears as composition factor of `P^R_i`; - `C_{i,j}=\dim Hom_A(P^L_i, P^L_j)`; - `C_{i,j}` counts how many times `S_i^*` appears as composition factor of `P^L_j`. In the commutative case, the Cartan invariant matrix is diagonal. In the context of solving systems of multivariate polynomial equations of dimension zero, `A` is the quotient of the polynomial ring by the ideal generated by the equations, the simple modules correspond to the roots, and the numbers `C_{i,i}` give the multiplicities of those roots. .. NOTE:: For simplicity, the current implementation assumes that the index set `I` is of the form `\{0,\dots,n-1\}`. Better indexations will be possible in the future. ALGORITHM: The Cartan invariant matrix of `A` is computed from the dimension of the summands of its peirce decomposition. .. SEEALSO:: - :meth:`peirce_decomposition` - :meth:`isotypic_projective_modules` EXAMPLES: For a semisimple algebra, in particular for group algebras in chararacteristic zero, the Cartan invariants matrix is the identity:: sage: A3 = SymmetricGroup(3).algebra(QQ) sage: A3.cartan_invariants_matrix() [1 0 0] [0 1 0] [0 0 1] For the path algebra of a quiver, the Cartan invariants matrix counts the number of paths between two vertices:: sage: A = Algebras(QQ).FiniteDimensional().WithBasis().example() sage: A.cartan_invariants_matrix() [1 2] [0 1] In the commutative case, the Cartan invariant matrix is diagonal:: sage: Z12 = Monoids().Finite().example(); Z12 An example of a finite multiplicative monoid: the integers modulo 12 sage: A = Z12.algebra(QQ) sage: A.cartan_invariants_matrix() [1 0 0 0 0 0 0 0 0] [0 1 0 0 0 0 0 0 0] [0 0 2 0 0 0 0 0 0] [0 0 0 1 0 0 0 0 0] [0 0 0 0 2 0 0 0 0] [0 0 0 0 0 1 0 0 0] [0 0 0 0 0 0 1 0 0] [0 0 0 0 0 0 0 2 0] [0 0 0 0 0 0 0 0 1] With the algebra of the `0`-Hecke monoid:: sage: from sage.monoids.hecke_monoid import HeckeMonoid sage: A = HeckeMonoid(SymmetricGroup(4)).algebra(QQ) sage: A.cartan_invariants_matrix() [1 0 0 0 0 0 0 0] [0 2 1 0 1 1 0 0] [0 1 1 0 1 0 0 0] [0 0 0 1 0 1 1 0] [0 1 1 0 1 0 0 0] [0 1 0 1 0 2 1 0] [0 0 0 1 0 1 1 0] [0 0 0 0 0 0 0 1] """ from sage.rings.integer_ring import ZZ A_quo = self.semisimple_quotient() idempotents_quo = A_quo.central_orthogonal_idempotents() # Dimension of simple modules dim_simples = [sqrt(A_quo.principal_ideal(e).dimension()) for e in idempotents_quo] # Orthogonal idempotents idempotents = self.orthogonal_idempotents_central_mod_radical() def C(i, j): summand = self.peirce_summand(idempotents[i], idempotents[j]) return summand.dimension() / (dim_simples[i] * dim_simples[j]) m = Matrix(ZZ, len(idempotents), C) m.set_immutable() return m