def matId(n): Id = [] n2 = n.quo_rem(2)[0] for j in range(n2): MSn = MatrixSpace(F, n2-j, n2-j) Id.append(MSn.identity_matrix()) return Id
def matA(n): A = [] n2 = n.quo_rem(2)[0] for j in range(n2+2): MS0 = MatrixSpace(F, j, j) I = MS0.identity_matrix() O = MS0(j*j*[1]) A.append(I+O) return A
def RandomLinearCode(n,k,F): r""" The method used is to first construct a `k \times n` matrix using Sage's random_element method for the MatrixSpace class. The construction is probabilistic but should only fail extremely rarely. INPUT: Integers n,k, with `n>k`, and a finite field F OUTPUT: Returns a "random" linear code with length n, dimension k over field F. EXAMPLES:: sage: C = codes.RandomLinearCode(30,15,GF(2)) sage: C Linear code of length 30, dimension 15 over Finite Field of size 2 sage: C = codes.RandomLinearCode(10,5,GF(4,'a')) sage: C Linear code of length 10, dimension 5 over Finite Field in a of size 2^2 AUTHORS: - David Joyner (2007-05) """ MS = MatrixSpace(F,k,n) for i in range(50): G = MS.random_element() if G.rank() == k: V = span(G.rows(), F) return LinearCodeFromVectorSpace(V) # may not be in standard form MS1 = MatrixSpace(F,k,k) MS2 = MatrixSpace(F,k,n-k) Ik = MS1.identity_matrix() A = MS2.random_element() G = Ik.augment(A) return LinearCode(G) # in standard form
def __call__(self, A, name=''): r""" Create an element of the homspace ``self`` from `A`. INPUT: - ``A`` -- one of the following: - an element of a Hecke algebra - a Hecke module morphism - a matrix - a list of elements of the codomain specifying the images of the basis elements of the domain. EXAMPLES:: sage: M = ModularForms(Gamma0(7), 4) sage: H = M.Hom(M) sage: H(M.hecke_operator(7)) Hecke module morphism T_7 defined by the matrix [ -7 0 0] [ 0 1 240] [ 0 0 343] Domain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... Codomain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... sage: H(H(M.hecke_operator(7))) Hecke module morphism T_7 defined by the matrix [ -7 0 0] [ 0 1 240] [ 0 0 343] Domain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... Codomain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... sage: H(matrix(QQ, 3, srange(9))) Hecke module morphism defined by the matrix [0 1 2] [3 4 5] [6 7 8] Domain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... Codomain: Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(7) ... TESTS: Make sure that the element is created correctly when the codomain is not the full module (related to :trac:`21497`):: sage: M = ModularSymbols(Gamma0(3),weight=22,sign=1) sage: S = M.cuspidal_subspace() sage: H = S.Hom(S) sage: H(S.gens()) Hecke module morphism defined by the matrix [1 0 0 0 0 0] [0 1 0 0 0 0] [0 0 1 0 0 0] [0 0 0 1 0 0] [0 0 0 0 1 0] [0 0 0 0 0 1] Domain: Modular Symbols subspace of dimension 6 of Modular Symbols space ... Codomain: Modular Symbols subspace of dimension 6 of Modular Symbols space ... sage: H.zero() in H True sage: H.one() in H True """ try: if A.parent() == self: A._set_parent(self) return A A = A.hecke_module_morphism() if A.parent() == self: A._set_parent(self) return A else: raise TypeError("unable to coerce A to self") except AttributeError: pass if A in self.base_ring(): dim_dom = self.domain().rank() dim_codom = self.codomain().rank() MS = MatrixSpace(self.base_ring(), dim_dom, dim_codom) if self.domain() == self.codomain(): A = A * MS.identity_matrix() elif A == 0: A = MS.zero() else: raise ValueError('scalars do not coerce to this homspace') elif isinstance(A, (list, tuple)): A = matrix([self.codomain().coordinate_vector(f) for f in A]) return HeckeModuleMorphism_matrix(self, A, name)
class AlternatingSignMatrices(Parent, UniqueRepresentation): r""" Class of all `n \times n` alternating sign matrices. An alternating sign matrix of size `n` is an `n \times n` matrix of `0`s, `1`s and `-1`s such that the sum of each row and column is `1` and the non-zero entries in each row and column alternate in sign. Alternating sign matrices of size `n` are in bijection with :class:`monotone triangles <MonotoneTriangles>` with `n` rows. INPUT: - `n` -- an integer, the size of the matrices. - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the generation of the matrices uses monotone triangles, else it will use the earlier and now obsolete contre-tableaux implementation; must be ``True`` to generate a lattice (with the ``lattice`` method) EXAMPLES: This will create an instance to manipulate the alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: A Alternating sign matrices of size 3 sage: A.cardinality() 7 Notably, this implementation allows to make a lattice of it:: sage: L = A.lattice() sage: L Finite lattice containing 7 elements sage: L.category() Category of facade finite lattice posets """ def __init__(self, n, use_monotone_triangles=True): r""" Initialize ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: TestSuite(A).run() sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) False """ self._n = n self._matrix_space = MatrixSpace(ZZ, n) self._umt = use_monotone_triangles Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): r""" Return a string representation of ``self``. TESTS:: sage: A = AlternatingSignMatrices(4); A Alternating sign matrices of size 4 """ return "Alternating sign matrices of size %s" % self._n def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A._repr_option('element_ascii_art') True """ return self._matrix_space._repr_option(key) def __contains__(self, asm): """ Check if ``asm`` is in ``self``. TESTS:: sage: A = AlternatingSignMatrices(3) sage: [[0,1,0],[1,0,0],[0,0,1]] in A True sage: [[0,1,0],[1,-1,1],[0,1,0]] in A True sage: [[0, 1],[1,0]] in A False sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A False sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A False """ if isinstance(asm, AlternatingSignMatrix): return asm._matrix.nrows() == self._n try: asm = self._matrix_space(asm) except (TypeError, ValueError): return False for row in asm: pos = False for val in row: if val > 0: if pos: return False else: pos = True elif val < 0: if pos: pos = False else: return False if not pos: return False if any(sum(row) != 1 for row in asm.columns()): return False return True def _element_constructor_(self, asm): """ Construct an element of ``self``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt [1 0 0] [0 1 0] [0 0 1] sage: elt.parent() is A True sage: A([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] """ if isinstance(asm, AlternatingSignMatrix): if asm.parent() is self: return asm raise ValueError("Cannot convert between alternating sign matrices of different sizes") if asm in MonotoneTriangles(self._n): return self.from_monotone_triangle(asm) return self.element_class(self, self._matrix_space(asm)) Element = AlternatingSignMatrix def _an_element_(self): """ Return an element of ``self``. """ return self.element_class(self, self._matrix_space.identity_matrix()) def from_monotone_triangle(self, triangle): r""" Return an alternating sign matrix from a monotone triangle. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) [0 0 1] [0 1 0] [1 0 0] """ n = len(triangle) if n != self._n: raise ValueError("Incorrect size") asm = [] prev = [0]*n for line in reversed(triangle): v = [1 if j+1 in reversed(line) else 0 for j in range(n)] row = [a-b for (a, b) in zip(v, prev)] asm.append(row) prev = v return self.element_class(self, self._matrix_space(asm)) def size(self): r""" Return the size of the matrices in ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: A.size() 4 """ return self._n def cardinality(self): r""" Return the cardinality of ``self``. The number of `n \times n` alternating sign matrices is equal to .. MATH:: \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} EXAMPLES:: sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] """ return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) for k in range(self._n)] )) def matrix_space(self): """ Return the underlying matrix space. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.matrix_space() Full MatrixSpace of 3 by 3 dense matrices over Integer Ring """ return self._matrix_space def __iter__(self): r""" Iterator on the alternating sign matrices of size `n`. If defined using ``use_monotone_triangles``, this iterator will use the iteration on the monotone triangles. Else, it will use the iteration on contre-tableaux. TESTS:: sage: A = AlternatingSignMatrices(4) sage: len(list(A)) 42 """ if self._umt: for t in MonotoneTriangles(self._n): yield self.from_monotone_triangle(t) else: for c in ContreTableaux(self._n): yield from_contre_tableau(c) def _lattice_initializer(self): r""" Return a 2-tuple to use in argument of ``LatticePoset``. For more details about the cover relations, see ``MonotoneTriangles``. Notice that the returned matrices are made immutable to ensure their hashability required by ``LatticePoset``. EXAMPLES: Proof of the lattice property for alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: P = Poset(A._lattice_initializer()) sage: P.is_lattice() True """ assert(self._umt) (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() bij = dict((t, self.from_monotone_triangle(t)) for t in mts) asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] return (asms, rels) def cover_relations(self): r""" Iterate on the cover relations between the alternating sign matrices. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: for (a,b) in A.cover_relations(): ... eval('a, b') ... ( [1 0 0] [0 1 0] [0 1 0] [1 0 0] [0 0 1], [0 0 1] ) ( [1 0 0] [1 0 0] [0 1 0] [0 0 1] [0 0 1], [0 1 0] ) ( [0 1 0] [ 0 1 0] [1 0 0] [ 1 -1 1] [0 0 1], [ 0 1 0] ) ( [1 0 0] [ 0 1 0] [0 0 1] [ 1 -1 1] [0 1 0], [ 0 1 0] ) ( [ 0 1 0] [0 0 1] [ 1 -1 1] [1 0 0] [ 0 1 0], [0 1 0] ) ( [ 0 1 0] [0 1 0] [ 1 -1 1] [0 0 1] [ 0 1 0], [1 0 0] ) ( [0 0 1] [0 0 1] [1 0 0] [0 1 0] [0 1 0], [1 0 0] ) ( [0 1 0] [0 0 1] [0 0 1] [0 1 0] [1 0 0], [1 0 0] ) """ (_, rels) = self._lattice_initializer() return (_ for _ in rels) def lattice(self): r""" Return the lattice of the alternating sign matrices of size `n`, created by ``LatticePoset``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: L = A.lattice() sage: L Finite lattice containing 7 elements """ return LatticePoset(self._lattice_initializer(), cover_relations=True)
class MatrixRec(object): r""" A matrix recurrence simultaneously generating the coefficients and partial sums of solutions of an ODE, and possibly derivatives of this solution. Note: Mathematically, the recurrence matrix has the structure of a StepMatrix (depending on parameters). However, this class does not derive from StepMatrix as the data structure is different. """ def __init__(self, diffop, dz, derivatives, nterms_est): deq_Scalars = diffop.base_ring().base_ring() E = dz.parent() if deq_Scalars.characteristic() != 0: raise ValueError("only makes sense for scalar rings of " "characteristic 0") assert deq_Scalars is dz.parent() or deq_Scalars != dz.parent() #### Recurrence operator # Reduce to the case of a number field generated by an algebraic # integer. This is mainly intended to avoid computing gcds (due to # denominators in the representation of number field elements) in the # product tree, but could also be useful to extend the field using Pari # in the future. NF_rec, AlgInts_rec = _number_field_with_integer_gen(deq_Scalars) # ore_algebra currently does not support orders as scalar rings Pols = PolynomialRing(NF_rec, 'n') Rops, Sn = ore_algebra.OreAlgebra(Pols, 'Sn').objgen() # Using the primitive part here would break the computation of # residuals! (Cf. local_solutions.) # recop = diffop.to_S(Rops).primitive_part().numerator() recop = diffop.to_S(Rops) recop = lcm([p.denominator() for p in recop.coefficients()]) * recop # Ensure that ordrec >= orddeq. When the homomorphic image of diffop in # Rops is divisible by Sn, it can happen that the recop (e.g., after # normalization to Sn-valuation 0) has order < orddeq, and our strategy # of using vectors of coefficients of the form [u(n-s'), ..., u(n+r-1)] # with s'=s-r does not work in this case. orddelta = recop.order() - diffop.order() if orddelta < 0: recop = Sn**(-orddelta) * recop #### Choose computation domains if ((isinstance(E, (RealBallField, ComplexBallField)) or E is QQ or utilities.is_QQi(E) or E is RLF or E is CLF) and (deq_Scalars is QQ or utilities.is_QQi(deq_Scalars))): # Special-case QQ and QQ[i] to use arb matrices # (overwrites AlgInts_rec) self.StepMatrix_class = StepMatrix_arb self.binsplit_threshold = 8 # Working precision. We typically want operations on exact balls to # be exact, so that overshooting shouldn't be a problem. # XXX Less clear in the case dz ∈ XBF! # XXX The rough estimate below ignores the height of rec and dz. # prec = nterms_est*(recop.degree()*nterms_est.nbits() # + recop.order().nbits() + 1) prec = 8 + nterms_est * (1 + ZZ(ZZ(recop.order()).nbits()).nbits()) if (E is QQ or isinstance(E, RealBallField)) and deq_Scalars is QQ: AlgInts_rec = AlgInts_pow = RealBallField(prec) else: AlgInts_rec = AlgInts_pow = ComplexBallField(prec) if is_NumberField(E): pow_den = AlgInts_pow(dz.denominator()) else: pow_den = AlgInts_pow.one() else: self.StepMatrix_class = StepMatrix_generic self.binsplit_threshold = 64 if is_NumberField(E): # In fact we should probably do something similar for dz in any # finite-dimensional Q-algebra. (But how?) NF_pow, AlgInts_pow = _number_field_with_integer_gen(E) pow_den = NF_pow(dz).denominator() else: # This includes the case E = ZZ, but dz could live in pretty # much any algebra over deq_Scalars (including matrices, # intervals...). Then the computation of sums_row may take time, # but we still hope to gain something on the computation of the # coefficients and/or limit interval blow-up thanks to the use # of binary splitting. AlgInts_pow = E pow_den = ZZ.one() assert pow_den.parent() is ZZ assert AlgInts_pow is AlgInts_rec or AlgInts_pow != AlgInts_rec #### Recurrence matrix self.recop = recop self.orddeq = diffop.order() self.ordrec = recop.order() self.orddelta = self.ordrec - self.orddeq Pols_rec, n = PolynomialRing(AlgInts_rec, 'n').objgen() self.rec_coeffs = [ -Pols_rec(recop[i])(n - self.orddelta) for i in xrange(self.ordrec) ] self.rec_den = Pols_rec(recop.leading_coefficient())(n - self.orddelta) # Guard against various problems related to number field embeddings and # uniqueness assert Pols_rec.base_ring() is AlgInts_rec assert self.rec_den.base_ring() is AlgInts_rec assert self.rec_den( self.rec_den.base_ring().zero()).parent() is AlgInts_rec # Also store a version of the recurrence operator of the form # b[0](n) + b[1](n) S^(-1) + ··· + b[s](n) S^(-s). # This is convenient to share code with other implementations, or at # least make the implementations easier to compare. # XXX: understand what to do about variable names! self.bwrec = [ recop[self.ordrec - k](Rops.base_ring().gen() - self.ordrec) for k in xrange(self.ordrec + 1) ] #### Power of dz. Note that this part does not depend on n. # If we extend the removal of denominators above to algebras other than # number fields, it would probably make more sense to move this into # the caller. --> support dz in non-com ring (mat)? power series work # only over com rings Series_pow = PolynomialRing(AlgInts_pow, 'delta') self.pow_num = Series_pow([pow_den * dz, pow_den]) self.pow_den = pow_den self.derivatives = derivatives #### Partial sums # We need a parent containing both the coefficients of the operator and # the evaluation point. # XXX: Is this the correct way to get one? Should we use # canonical_coercion()? Something else? # XXX: This is not powerful enough to find a number field containing # two given number fields (both given with embeddings into CC) # Work around #14982 (fixed) + weaknesses of the coercion framework for orders #Series_sums = sage.categories.pushout.pushout(AlgInts_rec, Series_pow) try: AlgInts_sums = sage.categories.pushout.pushout( AlgInts_rec, AlgInts_pow) except sage.structure.coerce_exceptions.CoercionException: AlgInts_sums = sage.categories.pushout.pushout(NF_rec, AlgInts_pow) assert AlgInts_sums is AlgInts_rec or AlgInts_sums != AlgInts_rec assert AlgInts_sums is AlgInts_pow or AlgInts_sums != AlgInts_pow Series_sums = PolynomialRing(AlgInts_sums, 'delta') assert Series_sums.base_ring() is AlgInts_sums # for speed self.Series_sums = Series_sums self.series_class_sums = type(Series_sums.gen()) self.Mat_rec = MatrixSpace(AlgInts_rec, self.ordrec, self.ordrec) self.Mat_sums_row = MatrixSpace(Series_sums, 1, self.ordrec) self.Mat_series_sums = self.Mat_rec.change_ring(Series_sums) def __call__(self, n): stepmat = self.StepMatrix_class() stepmat.idx_start = n stepmat.idx_end = n + 1 stepmat.rec_den = self.rec_den(n) stepmat.rec_mat = self.Mat_rec.matrix() for i in xrange(self.ordrec - 1): stepmat.rec_mat[i, i + 1] = stepmat.rec_den for i in xrange(self.ordrec): stepmat.rec_mat[self.ordrec - 1, i] = self.rec_coeffs[i](n) stepmat.pow_num = self.pow_num stepmat.pow_den = self.pow_den # TODO: fix redundancy--the rec_den*pow_den probabably doesn't belong # here # XXX: should we give a truncation order? den = stepmat.rec_den * stepmat.pow_den den = self.series_class_sums(self.Series_sums, [den]) stepmat.sums_row = self.Mat_sums_row.matrix() stepmat.sums_row[0, self.orddelta] = den stepmat.ord = self.derivatives stepmat.BigScalars = self.Series_sums # XXX unused in arb case stepmat.Mat_big_scalars = self.Mat_series_sums return stepmat def one(self, n): stepmat = self.StepMatrix_class() stepmat.idx_start = stepmat.idx_end = n stepmat.rec_mat = self.Mat_rec.identity_matrix() stepmat.rec_den = self.rec_den.base_ring().one() stepmat.pow_num = self.pow_num.parent().one() stepmat.pow_den = self.pow_den.parent().one() stepmat.sums_row = self.Mat_sums_row.matrix() stepmat.ord = self.derivatives stepmat.BigScalars = self.Series_sums # XXX unused in arb case stepmat.Mat_big_scalars = self.Mat_series_sums return stepmat def binsplit(self, low, high): if high - low <= self.binsplit_threshold: mat = self.one(low) for n in xrange(low, high): mat.imulleft(self(n)) else: mid = (low + high) // 2 mat = self.binsplit(low, mid) mat.imulleft(self.binsplit(mid, high)) return mat def __repr__(self): return pprint.pformat(self.__dict__) # XXX: needs testing, especially when rop.valuation() > 0 def normalized_residual(self, maj, prod, n, j): r""" Compute the normalized residual associated with the fundamental solution of index j. TESTS:: sage: from ore_algebra import * sage: DOP, t, D = DifferentialOperators() sage: ode = D + 1/4/(t - 1/2) sage: ode.numerical_transition_matrix([0,1+I,1], 1e-100, algorithm='binsplit') [[0.707...2078...] + [0.707...]*I] """ r, s = self.orddeq, self.ordrec IC = bounds.IC # Compute the "missing" coefficients u(n-s), ..., u(n-s'-1) s'=s-r): # indeed, it is convenient to compute the residuals starting from # u(n-s), ..., u(n-1), while our recurrence matrices produce the partial # sum of index n along with the vector [u(n-s'), ..., u(n+r-1)]. last = [IC.zero()] * r # u(n-s), ..., u(n-s'-1) last.extend([ IC(c) / IC(prod.rec_den) # u(n-s'), ..., u(n+r-1) for c in prod.rec_mat.column(s - r + j) ]) # XXX: check column index rop = self.recop v = rop.valuation() for i in xrange(r - 1, -1, -1): # compute u(n-s+i) last[i] = ~(rop[v](n - s + i)) * sum( rop[k](n - s + i) * last[i + k] # u(n-s+i) for k in xrange(v + 1, s + 1)) # Now compute the residual. WARNING: this residual must correspond to # the operator stored in maj.dop, which typically isn't self.diffop (but # an operator in θx equal to x^k·self.diffop for some k). # XXX: do not recompute this every time! bwrnp = [[[pol(n + i)] for pol in self.bwrec] for i in range(s)] altlast = [[c] for c in reversed(last[:s])] return maj.normalized_residual(n, altlast, bwrnp) def normalized_residuals(self, maj, prod, n): return [ self.normalized_residual(maj, prod, n, j) for j in xrange(self.orddeq) ] def term(self, prod, parent, j): r""" Given a prodrix representing a product B(n-1)···B(0) where B is the recurrence matrix associated to some differential operator P, return the term of index n of the fundamental solution of P of the form y[j](z) = z^j + O(z^r), 0 <= j < r = order(P). """ orddelta = self.orddelta num = parent(prod.rec_mat[orddelta + j, orddelta]) * parent( prod.pow_num[0]) den = parent(prod.rec_den) * parent(prod.pow_den) return num / den def partial_sums(self, prod, ring, rows): r""" Return a matrix of partial sums of the series and its derivatives. """ numer = matrix(ring, rows, self.orddeq, lambda i, j: prod.sums_row[0, self.orddelta + j][i]) denom = ring(prod.rec_den) * ring(prod.pow_den) return numer / denom def error_estimate(self, prod): orddelta = self.orddelta num1 = sum( abs(prod.rec_mat[orddelta + j, orddelta]) for j in range(self.orddeq)) num2 = sum(abs(a) for a in prod.pow_num) den = abs(prod.rec_den) * abs(prod.pow_den) return num1 * num2 / den
class AlternatingSignMatrices(Parent, UniqueRepresentation): r""" Class of all `n \times n` alternating sign matrices. An alternating sign matrix of size `n` is an `n \times n` matrix of `0`'s, `1`'s and `-1`'s such that the sum of each row and column is `1` and the non-zero entries in each row and column alternate in sign. Alternating sign matrices of size `n` are in bijection with :class:`monotone triangles <MonotoneTriangles>` with `n` rows. INPUT: - `n` -- an integer, the size of the matrices. - ``use_monotone_triangle`` -- (Default: ``True``) If ``True``, the generation of the matrices uses monotone triangles, else it will use the earlier and now obsolete contre-tableaux implementation; must be ``True`` to generate a lattice (with the ``lattice`` method) EXAMPLES: This will create an instance to manipulate the alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: A Alternating sign matrices of size 3 sage: A.cardinality() 7 Notably, this implementation allows to make a lattice of it:: sage: L = A.lattice() sage: L Finite lattice containing 7 elements sage: L.category() Category of facade finite lattice posets """ def __init__(self, n, use_monotone_triangles=True): r""" Initialize ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: TestSuite(A).run() sage: A == AlternatingSignMatrices(4, use_monotone_triangles=False) False """ self._n = n self._matrix_space = MatrixSpace(ZZ, n) self._umt = use_monotone_triangles Parent.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self): r""" Return a string representation of ``self``. TESTS:: sage: A = AlternatingSignMatrices(4); A Alternating sign matrices of size 4 """ return "Alternating sign matrices of size %s" % self._n def _repr_option(self, key): """ Metadata about the :meth:`_repr_` output. See :meth:`sage.structure.parent._repr_option` for details. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A._repr_option('element_ascii_art') True """ return self._matrix_space._repr_option(key) def __contains__(self, asm): """ Check if ``asm`` is in ``self``. TESTS:: sage: A = AlternatingSignMatrices(3) sage: [[0,1,0],[1,0,0],[0,0,1]] in A True sage: [[0,1,0],[1,-1,1],[0,1,0]] in A True sage: [[0, 1],[1,0]] in A False sage: [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]] in A False sage: [[-1, 1, 1],[1,-1,1],[1,1,-1]] in A False """ if isinstance(asm, AlternatingSignMatrix): return asm._matrix.nrows() == self._n try: asm = self._matrix_space(asm) except (TypeError, ValueError): return False for row in asm: pos = False for val in row: if val > 0: if pos: return False else: pos = True elif val < 0: if pos: pos = False else: return False if not pos: return False if any(sum(row) != 1 for row in asm.columns()): return False return True def _element_constructor_(self, asm): """ Construct an element of ``self``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: elt = A([[1, 0, 0],[0, 1, 0],[0, 0, 1]]); elt [1 0 0] [0 1 0] [0 0 1] sage: elt.parent() is A True sage: A([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] """ if isinstance(asm, AlternatingSignMatrix): if asm.parent() is self: return asm raise ValueError("Cannot convert between alternating sign matrices of different sizes") if asm in MonotoneTriangles(self._n): return self.from_monotone_triangle(asm) return self.element_class(self, self._matrix_space(asm)) Element = AlternatingSignMatrix def _an_element_(self): """ Return an element of ``self``. """ return self.element_class(self, self._matrix_space.identity_matrix()) def from_monotone_triangle(self, triangle): r""" Return an alternating sign matrix from a monotone triangle. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.from_monotone_triangle([[3, 2, 1], [2, 1], [1]]) [1 0 0] [0 1 0] [0 0 1] sage: A.from_monotone_triangle([[3, 2, 1], [3, 2], [3]]) [0 0 1] [0 1 0] [1 0 0] """ n = len(triangle) if n != self._n: raise ValueError("Incorrect size") asm = [] prev = [0]*n for line in reversed(triangle): v = [1 if j+1 in reversed(line) else 0 for j in range(n)] row = [a-b for (a, b) in zip(v, prev)] asm.append(row) prev = v return self.element_class(self, self._matrix_space(asm)) def size(self): r""" Return the size of the matrices in ``self``. TESTS:: sage: A = AlternatingSignMatrices(4) sage: A.size() 4 """ return self._n def cardinality(self): r""" Return the cardinality of ``self``. The number of `n \times n` alternating sign matrices is equal to .. MATH:: \prod_{k=0}^{n-1} \frac{(3k+1)!}{(n+k)!} = \frac{1! 4! 7! 10! \cdots (3n-2)!}{n! (n+1)! (n+2)! (n+3)! \cdots (2n-1)!} EXAMPLES:: sage: [AlternatingSignMatrices(n).cardinality() for n in range(0, 11)] [1, 1, 2, 7, 42, 429, 7436, 218348, 10850216, 911835460, 129534272700] """ return Integer(prod( [ factorial(3*k+1)/factorial(self._n+k) for k in range(self._n)] )) def matrix_space(self): """ Return the underlying matrix space. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: A.matrix_space() Full MatrixSpace of 3 by 3 dense matrices over Integer Ring """ return self._matrix_space def __iter__(self): r""" Iterator on the alternating sign matrices of size `n`. If defined using ``use_monotone_triangles``, this iterator will use the iteration on the monotone triangles. Else, it will use the iteration on contre-tableaux. TESTS:: sage: A = AlternatingSignMatrices(4) sage: len(list(A)) 42 """ if self._umt: for t in MonotoneTriangles(self._n): yield self.from_monotone_triangle(t) else: for c in ContreTableaux(self._n): yield from_contre_tableau(c) def _lattice_initializer(self): r""" Return a 2-tuple to use in argument of ``LatticePoset``. For more details about the cover relations, see ``MonotoneTriangles``. Notice that the returned matrices are made immutable to ensure their hashability required by ``LatticePoset``. EXAMPLES: Proof of the lattice property for alternating sign matrices of size 3:: sage: A = AlternatingSignMatrices(3) sage: P = Poset(A._lattice_initializer()) sage: P.is_lattice() True """ assert(self._umt) (mts, rels) = MonotoneTriangles(self._n)._lattice_initializer() bij = dict((t, self.from_monotone_triangle(t)) for t in mts) asms, rels = bij.itervalues(), [(bij[a], bij[b]) for (a,b) in rels] return (asms, rels) def cover_relations(self): r""" Iterate on the cover relations between the alternating sign matrices. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: for (a,b) in A.cover_relations(): ... eval('a, b') ... ( [1 0 0] [0 1 0] [0 1 0] [1 0 0] [0 0 1], [0 0 1] ) ( [1 0 0] [1 0 0] [0 1 0] [0 0 1] [0 0 1], [0 1 0] ) ( [0 1 0] [ 0 1 0] [1 0 0] [ 1 -1 1] [0 0 1], [ 0 1 0] ) ( [1 0 0] [ 0 1 0] [0 0 1] [ 1 -1 1] [0 1 0], [ 0 1 0] ) ( [ 0 1 0] [0 0 1] [ 1 -1 1] [1 0 0] [ 0 1 0], [0 1 0] ) ( [ 0 1 0] [0 1 0] [ 1 -1 1] [0 0 1] [ 0 1 0], [1 0 0] ) ( [0 0 1] [0 0 1] [1 0 0] [0 1 0] [0 1 0], [1 0 0] ) ( [0 1 0] [0 0 1] [0 0 1] [0 1 0] [1 0 0], [1 0 0] ) """ (_, rels) = self._lattice_initializer() return (_ for _ in rels) def lattice(self): r""" Return the lattice of the alternating sign matrices of size `n`, created by ``LatticePoset``. EXAMPLES:: sage: A = AlternatingSignMatrices(3) sage: L = A.lattice() sage: L Finite lattice containing 7 elements """ return LatticePoset(self._lattice_initializer(), cover_relations=True)