def is_valid(self): """ Check if the arithmetic matroid axioms are satisfied. """ if not super(ArithmeticMatroidMixin, self).is_valid(): # check validity of the underlying matroid return False E = self.groundset() # check axioms for arithmetic matroids for X in powerset(E): X = frozenset(X) for v in E: if v not in X: if self._is_dependent_from(v, X): # check axiom 1 if self.multiplicity(X) % self.multiplicity(X.union([v])) != 0: # print >> sys.stderr, "Axiom 1 fails on", X, v return False else: # check axiom 2 if self.multiplicity(X.union([v])) % self.multiplicity(X) != 0: # print >> sys.stderr, "Axiom 2 fails on", X, v return False for Y in powerset(E): Y = frozenset(Y) for X in powerset(Y): X = frozenset(X) T = [] F = [] for y in Y: if y not in X: if self._is_dependent_from(y, X): T.append(y) else: F.append(y) if len(F) + self.rank(X) == self.rank(Y): # (X, Y) is a molecule T = frozenset(T) F = frozenset(F) # check axiom 3 if self.multiplicity(X) * self.multiplicity(Y) != self.multiplicity(X.union(F)) * self.multiplicity(X.union(T)): return False # check axiom P if (-1)**(len(T)) * sum((-1)**(len(Y)-len(Z)-len(X)) * self.multiplicity(X.union(Z)) for Z in powerset(Y.difference(X))) < 0: return False return True
def is_gcd(self): """ Check if the matroid satisfies the gcd property, defined in [DM13, Section 3]. """ return all( self.multiplicity(X) == reduce(gcd, ( self.multiplicity(I) for I in powerset(X) if self.rank(X) == self.rank(I) and self.is_independent(I)), 0) for X in powerset(self.groundset()))
def nonisomorphic_cubes_Z2(n, avoid_complete=False): """ Returns a generator for all n-dimensional Cube-like graphs (Cayley graphs over Z_2^n) with their generators. With avoid_complete=True avoids the complete graph. Iterates over tuples (generatorSet, G). """ vs = VectorSpace(GF(2), n) basegens = vs.gens() optgens = [v for v in vs if sum(map(int, v)) >= 2] total = 2**len(optgens) seen_graphs = set() c = 0 for g in powerset(optgens): c += 1 gens = tuple(list(basegens) + g) if c % (total / 100 + 1) == 0: log.debug("Generating (%d of %d)" % (c, total)) if avoid_complete: if len(g) >= len(optgens): continue G = CayleyGraph(vs, gens) canon = tuple(Graph(G).canonical_label().edges()) if canon in seen_graphs: continue log.debug("Unique graph (%d of %d) gens=%s" % (c, total, gens)) seen_graphs.add(canon) yield (gens, G)
def check_representation(self, A, ordered_groundset=None, check_bases=False): """ Check if the given matrix is a representation for the matroid. If check_bases==True, check that the multiplicity is correct only on the bases. """ r = self.full_rank() n = len(self.groundset()) if ordered_groundset is not None: # use the groundset in the given order E = ordered_groundset assert frozenset(E) == self.groundset() assert len(E) == len(self.groundset()) else: # to sort the groundset E = list(sorted(self.groundset())) if A.ncols() != n: return False for S in powerset(range(n)): T = frozenset(E[i] for i in S) # corresponding subset of E if A[:,S].rank() != self.rank(T): # print >> sys.stderr, "Not representable, rank of %r is incorrect" % T return False if check_bases and len(T) != r and self.rank(T) < r: # skip multiplicity check continue if reduce(operator.mul, [d for d in A[:,S].elementary_divisors() if d != 0], 1) != self.multiplicity(T): # print >> sys.stderr, "Not representable, multiplicity of %r is incorrect" % T return False return True
def nonisomorphic_cubes_Z2(n, avoid_complete=False): """ Returns a generator for all n-dimensional Cube-like graphs (Cayley graphs over Z_2^n) with their generators. With avoid_complete=True avoids the complete graph. Iterates over tuples (generatorSet, G). """ vs = VectorSpace(GF(2), n) basegens = vs.gens() optgens = [v for v in vs if sum(map(int,v)) >= 2] total = 2**len(optgens) seen_graphs = set() c = 0 for g in powerset(optgens): c += 1 gens = tuple(list(basegens) + g) if c % (total / 100 + 1) == 0: log.debug("Generating (%d of %d)" % (c, total)) if avoid_complete: if len(g) >= len(optgens): continue G = CayleyGraph(vs, gens) canon = tuple(Graph(G).canonical_label().edges()) if canon in seen_graphs: continue log.debug("Unique graph (%d of %d) gens=%s" % (c, total, gens)) seen_graphs.add(canon) yield (gens, G)
def principal_submatrices(self, proper=False): """ Return a list of all principal submatrices of ``self``. INPUT: - ``proper`` -- if ``True``, return only proper submatrices EXAMPLES:: sage: M = CartanMatrix(['A',2]) sage: M.principal_submatrices() [ [ 2 -1] [], [2], [2], [-1 2] ] sage: M.principal_submatrices(proper=True) [[], [2], [2]] """ iset = list(range(self.ncols())) ret = [] for l in powerset(iset): if not proper or (proper and l != iset): ret.append(self.matrix_from_rows_and_columns(l, l)) return ret
def principal_submatrices(self, proper=False): """ Return a list of all principal submatrices of ``self``. INPUT: - ``proper`` -- if ``True``, return only proper submatrices EXAMPLES:: sage: M = CartanMatrix(['A',2]) sage: M.principal_submatrices() [ [ 2 -1] [], [2], [2], [-1 2] ] sage: M.principal_submatrices(proper=True) [[], [2], [2]] """ iset = range(self.ncols()) ret = [] for l in powerset(iset): if not proper or (proper and l != iset): ret.append(self.matrix_from_rows_and_columns(l, l)) return ret
def is_strong_gcd(self): """ Check if the matroid satisfies the gcd property, defined in [PP19, Section 3]. """ return all( self.multiplicity(X) == reduce(gcd, ( self.multiplicity(B) for B in self.bases() if len(B.intersection(X)) == self.rank(X)), 0) for X in powerset(self.groundset()))
def _is_isomorphism(self, other, morphism): """ Version of is_isomorphism() that does no type checking. (see Matroid.is_isomorphism) """ for X in powerset(self.groundset()): Y = frozenset(morphism[e] for e in X) if self.rank(X) != other.rank(Y) or self.multiplicity(X) != other.multiplicity(Y): return False return True
def __iter__(self): r""" Iterate over ``self``. EXAMPLES:: sage: from sage.combinat.blob_algebra import BlobDiagrams sage: BD3 = BlobDiagrams(3) sage: sorted(BD3) [({}, {{-3, -2}, {-1, 1}, {2, 3}}), ({}, {{-3, -2}, {-1, 3}, {1, 2}}), ({}, {{-3, 1}, {-2, -1}, {2, 3}}), ({}, {{-3, 3}, {-2, -1}, {1, 2}}), ({}, {{-3, 3}, {-2, 2}, {-1, 1}}), ({{-3, 1}}, {{-2, -1}, {2, 3}}), ({{-3, 3}}, {{-2, -1}, {1, 2}}), ({{-2, -1}}, {{-3, 1}, {2, 3}}), ({{-2, -1}}, {{-3, 3}, {1, 2}}), ({{-1, 1}}, {{-3, -2}, {2, 3}}), ({{-1, 1}}, {{-3, 3}, {-2, 2}}), ({{-1, 3}}, {{-3, -2}, {1, 2}}), ({{1, 2}}, {{-3, -2}, {-1, 3}}), ({{1, 2}}, {{-3, 3}, {-2, -1}}), ({{-3, 1}, {-2, -1}}, {{2, 3}}), ({{-3, 3}, {-2, -1}}, {{1, 2}}), ({{-3, 3}, {1, 2}}, {{-2, -1}}), ({{-2, -1}, {1, 2}}, {{-3, 3}}), ({{-1, 3}, {1, 2}}, {{-3, -2}}), ({{-3, 3}, {-2, -1}, {1, 2}}, {})] """ for D in DyckWords(self._n): markable = set() unmarked = [] unpaired = [] # Determine the pairing and which pairings are markable for i, d in enumerate(D): if i >= self._n: i = -2 * self._n + i else: i += 1 if d == 1: unpaired.append(i) else: # d == 0 m = unpaired.pop() if not unpaired: markable.add((m, i)) else: unmarked.append((m, i)) for X in powerset(markable): yield self.element_class( self, X, unmarked + list(markable.difference(X)))
def arithmetic_tutte_polynomial(self, x=None, y=None): """ Return the arithmetic Tutte polynomial of the matroid. """ E = self.groundset() r = self.full_rank() a = x b = y R = ZZ['x, y'] x, y = R._first_ngens(2) T = R(0) for X in powerset(self.groundset()): T += self.multiplicity(X) * (x-1) ** (r - self.rank(X)) * (y-1) ** (len(X) - self.rank(X)) if a is not None and b is not None: T = T(a, b) return T
def reduced_charges(M): """ Given a snappy manifold M, we find all reduced charges so that: (1) no loop in the triangulation passes an odd number of pi's. (2) no tetrahedron has three pi's and """ out = sol_and_kernel(M) x, A = out nt = M.num_tetrahedra() dim = 3 * nt V = VectorSpace(ZZ2, dim) AA = V.subspace(A) # the reduced kernel xx = V(x) # the reduced solution charges = [xx + sum(B) for B in powerset(AA.basis())] charges = [c for c in charges if not has_pi_triple(c) ] # reject if there are three pi's in any tet. return charges
def unramified_outside(self, S, d=None, var='a'): """ Return all fields in the database of degree d unramified outside S. If d is omitted, return fields of any degree up to 6. The fields are ordered by degree and discriminant. INPUT: - ``S`` - list or set of primes, or a single prime - ``d`` - None (default, in which case all fields of degree <= 6 are returned) or a positive integer giving the degree of the number fields returned. - ``var`` - the name used for the generator of the number fields (default 'a'). EXAMPLES:: sage: J = JonesDatabase() # optional - jones_database sage: J.unramified_outside([101,109]) # optional - jones_database [Number Field in a with defining polynomial x - 1, Number Field in a with defining polynomial x^2 - 101, Number Field in a with defining polynomial x^2 - 109, Number Field in a with defining polynomial x^3 - x^2 - 36*x + 4, Number Field in a with defining polynomial x^4 - x^3 + 13*x^2 - 19*x + 361, Number Field in a with defining polynomial x^4 - x^3 + 14*x^2 + 34*x + 393, Number Field in a with defining polynomial x^5 + 2*x^4 + 7*x^3 + 4*x^2 + 11*x - 6, Number Field in a with defining polynomial x^5 + x^4 - 6*x^3 - x^2 + 18*x + 4, Number Field in a with defining polynomial x^5 - x^4 - 40*x^3 - 93*x^2 - 21*x + 17] """ try: S = list(S) except TypeError: S = [S] Z = [] for X in powerset(S): Z += self.ramified_at(X, d=d, var=var) Z = [(k.degree(), k.discriminant().abs(), k.discriminant() > 0, k) for k in Z] Z.sort() return [z[-1] for z in Z]
def unramified_outside(self, S, d=None, var='a'): """ Return all fields in the database of degree d unramified outside S. If d is omitted, return fields of any degree up to 6. The fields are ordered by degree and discriminant. INPUT: - ``S`` - list or set of primes, or a single prime - ``d`` - None (default, in which case all fields of degree <= 6 are returned) or a positive integer giving the degree of the number fields returned. - ``var`` - the name used for the generator of the number fields (default 'a'). EXAMPLES:: sage: J = JonesDatabase() # optional - database_jones_numfield sage: J.unramified_outside([101,109]) # optional - database_jones_numfield [Number Field in a with defining polynomial x - 1, Number Field in a with defining polynomial x^2 - 101, Number Field in a with defining polynomial x^2 - 109, Number Field in a with defining polynomial x^3 - x^2 - 36*x + 4, Number Field in a with defining polynomial x^4 - x^3 + 13*x^2 - 19*x + 361, Number Field in a with defining polynomial x^4 - x^3 + 14*x^2 + 34*x + 393, Number Field in a with defining polynomial x^5 + 2*x^4 + 7*x^3 + 4*x^2 + 11*x - 6, Number Field in a with defining polynomial x^5 + x^4 - 6*x^3 - x^2 + 18*x + 4, Number Field in a with defining polynomial x^5 - x^4 - 40*x^3 - 93*x^2 - 21*x + 17] """ try: S = list(S) except TypeError: S = [S] Z = [] for X in powerset(S): Z += self.ramified_at(X, d=d, var=var) Z = [(k.degree(), k.discriminant().abs(), k.discriminant() > 0, k) for k in Z] Z.sort() return [z[-1] for z in Z]
def shapley_value(self): r""" Return the Shapley value for ``self``. The Shapley value is the "fair" payoff vector and is computed by the following formula: .. MATH:: \phi_i(G) = \sum_{S \subseteq \Omega} \sum_{p \in S} \frac{1}{|S|\binom{N}{|S|}} \bigl( v(S) - v(S \setminus \{p\}) \bigr). EXAMPLES: A typical example of computing the Shapley value:: sage: integer_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) sage: integer_game.player_list (1, 2, 3) sage: integer_game.shapley_value() {1: 2, 2: 5, 3: 35} A longer example of the Shapley value:: sage: long_function = {(): 0, ....: (1,): 0, ....: (2,): 0, ....: (3,): 0, ....: (4,): 0, ....: (1, 2): 0, ....: (1, 3): 0, ....: (1, 4): 0, ....: (2, 3): 0, ....: (2, 4): 0, ....: (3, 4): 0, ....: (1, 2, 3): 0, ....: (1, 2, 4): 45, ....: (1, 3, 4): 40, ....: (2, 3, 4): 0, ....: (1, 2, 3, 4): 65} sage: long_game = CooperativeGame(long_function) sage: long_game.shapley_value() {1: 70/3, 2: 10, 3: 25/3, 4: 70/3} """ payoff_vector = {} n = Integer(len(self.player_list)) for player in self.player_list: weighted_contribution = 0 for coalition in powerset(self.player_list): if coalition: # If non-empty k = Integer(len(coalition)) weight = 1 / (n.binomial(k) * k) t = tuple(p for p in coalition if p != player) weighted_contribution += weight * ( self.ch_f[tuple(coalition)] - self.ch_f[t]) payoff_vector[player] = weighted_contribution return payoff_vector
def _coeffs_from_height(self, height_tuple): """ Returns a list of tuples of a-invariants of all curves described by height_tuple. INPUT: - ``height_tuple`` -- A tuple of the form (H, C, I) such that H: The smallest height >= N C: A list of coefficients for curves of this height I: A list of indices indicating which of the above coefficients achieve this height. The remaining values in C indicate the max absolute value those coefficients are allowed to obtain without altering the height. For example, the tuple (4, [1, 2], [1]) for the short Weierstrass case denotes set of curves with height 4; these are all of the form Y^2 = X^3 + A*X + B, where B=2 and A ranges between -1 and 1. OUTPUT: - A list of 2-tuples, each consisting of the given height, followed by a tuple of a-invariants of a curve of that height. EXAMPLES: sage: from sage.schemes.elliptic_curves.curve_enumerator import * sage: C = CurveEnumerator(family="short_weierstrass") sage: B = C.next_height(4); B (4, [1, 2], [1]) sage: L = C._coeffs_from_height(B) sage: for ell in L: print ell ...: (4, [0, 0, 0, -1, -2]) (4, [0, 0, 0, -1, 2]) (4, [0, 0, 0, 0, -2]) (4, [0, 0, 0, 0, 2]) (4, [0, 0, 0, 1, -2]) (4, [0, 0, 0, 1, 2]) """ height = height_tuple[0] coeffs = height_tuple[1] index = height_tuple[2] # Produce list of all coefficient tuples with given height L = [] for S in list(powerset(index))[1:]: B = [] for j in range(len(coeffs)): if j in S: B.append([-coeffs[j], coeffs[j]]) elif j in index: B.append(srange(-coeffs[j] + 1, coeffs[j])) else: B.append(srange(-coeffs[j], coeffs[j] + 1)) C = CartesianProduct(*B).list() for c in C: L.append(c) # Convert coefficient tuples to a-invariants L2 = [] for c in L: C = self._coeffs_to_a_invariants(c) if not self._is_singular(C): L2.append((height, C)) return L2
def __init__(self, characteristic_function): r""" Initializes a co-operative game and checks the inputs. TESTS: An attempt to construct a game from an integer:: sage: int_game = CooperativeGame(4) Traceback (most recent call last): ... TypeError: characteristic function must be a dictionary This test checks that an incorrectly entered singularly tuple will be changed into a tuple. In this case ``(1)`` becomes ``(1,)``:: sage: tuple_function = {(): 0, ....: (1): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: tuple_game = CooperativeGame(tuple_function) This test checks that if a key is not a tuple an error is raised:: sage: error_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: 12: 12, ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: error_game = CooperativeGame(error_function) Traceback (most recent call last): ... TypeError: key must be a tuple A test to ensure that the characteristic function is the power set of the grand coalition (ie all possible sub-coalitions):: sage: incorrect_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2, 3,): 42} sage: incorrect_game = CooperativeGame(incorrect_function) Traceback (most recent call last): ... ValueError: characteristic function must be the power set """ if not isinstance(characteristic_function, dict): raise TypeError("characteristic function must be a dictionary") self.ch_f = characteristic_function for key in list(self.ch_f): if len(str(key)) == 1 and not isinstance(key, tuple): self.ch_f[(key, )] = self.ch_f.pop(key) elif not isinstance(key, tuple): raise TypeError("key must be a tuple") for key in list(self.ch_f): sortedkey = tuple(sorted(key)) self.ch_f[sortedkey] = self.ch_f.pop(key) self.player_list = max(characteristic_function, key=len) for coalition in powerset(self.player_list): if tuple(sorted(coalition)) not in self.ch_f: raise ValueError( "characteristic function must be the power set") self.number_players = len(self.player_list)
def _coeffs_from_height(self,height_tuple): """ Returns a list of tuples of a-invariants of all curves described by height_tuple. INPUT: - ``height_tuple`` -- A tuple of the form (H, C, I) such that H: The smallest height >= N C: A list of coefficients for curves of this height I: A list of indices indicating which of the above coefficients achieve this height. The remaining values in C indicate the max absolute value those coefficients are allowed to obtain without altering the height. For example, the tuple (4, [1, 2], [1]) for the short Weierstrass case denotes set of curves with height 4; these are all of the form Y^2 = X^3 + A*X + B, where B=2 and A ranges between -1 and 1. OUTPUT: - A list of 2-tuples, each consisting of the given height, followed by a tuple of a-invariants of a curve of that height. EXAMPLES: sage: from sage.schemes.elliptic_curves.curve_enumerator import * sage: C = CurveEnumerator(family="short_weierstrass") sage: B = C.next_height(4); B (4, [1, 2], [1]) sage: L = C._coeffs_from_height(B) sage: for ell in L: print ell ...: (4, [0, 0, 0, -1, -2]) (4, [0, 0, 0, -1, 2]) (4, [0, 0, 0, 0, -2]) (4, [0, 0, 0, 0, 2]) (4, [0, 0, 0, 1, -2]) (4, [0, 0, 0, 1, 2]) """ height = height_tuple[0] coeffs = height_tuple[1] index = height_tuple[2] # Produce list of all coefficient tuples with given height L = [] for S in list(powerset(index))[1:]: B = [] for j in range(len(coeffs)): if j in S: B.append([-coeffs[j],coeffs[j]]) elif j in index: B.append(srange(-coeffs[j]+1,coeffs[j])) else: B.append(srange(-coeffs[j],coeffs[j]+1)) C = CartesianProduct(*B).list() for c in C: L.append(c) # Convert coefficient tuples to a-invariants L2 = [] for c in L: C = self._coeffs_to_a_invariants(c) if not self._is_singular(C): L2.append((height,C)) return L2
def shapley_value(self): r""" Return the Shapley value for ``self``. The Shapley value is the "fair" payoff vector and is computed by the following formula: .. MATH:: \phi_i(G) = \sum_{S \subseteq \Omega} \sum_{p \in S} \frac{1}{|S|\binom{N}{|S|}} \bigl( v(S) - v(S \setminus \{p\}) \bigr). EXAMPLES: A typical example of computing the Shapley value:: sage: integer_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: integer_game = CooperativeGame(integer_function) sage: integer_game.player_list (1, 2, 3) sage: integer_game.shapley_value() {1: 2, 2: 5, 3: 35} A longer example of the Shapley value:: sage: long_function = {(): 0, ....: (1,): 0, ....: (2,): 0, ....: (3,): 0, ....: (4,): 0, ....: (1, 2): 0, ....: (1, 3): 0, ....: (1, 4): 0, ....: (2, 3): 0, ....: (2, 4): 0, ....: (3, 4): 0, ....: (1, 2, 3): 0, ....: (1, 2, 4): 45, ....: (1, 3, 4): 40, ....: (2, 3, 4): 0, ....: (1, 2, 3, 4): 65} sage: long_game = CooperativeGame(long_function) sage: long_game.shapley_value() {1: 70/3, 2: 10, 3: 25/3, 4: 70/3} """ payoff_vector = {} n = Integer(len(self.player_list)) for player in self.player_list: weighted_contribution = 0 for coalition in powerset(self.player_list): if coalition: # If non-empty k = Integer(len(coalition)) weight = 1 / (n.binomial(k) * k) t = tuple(p for p in coalition if p != player) weighted_contribution += weight * (self.ch_f[tuple(coalition)] - self.ch_f[t]) payoff_vector[player] = weighted_contribution return payoff_vector
def __init__(self, characteristic_function): r""" Initializes a co-operative game and checks the inputs. TESTS: An attempt to construct a game from an integer:: sage: int_game = CooperativeGame(4) Traceback (most recent call last): ... TypeError: characteristic function must be a dictionary This test checks that an incorrectly entered singularly tuple will be changed into a tuple. In this case ``(1)`` becomes ``(1,)``:: sage: tuple_function = {(): 0, ....: (1): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2,): 12, ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: tuple_game = CooperativeGame(tuple_function) This test checks that if a key is not a tuple an error is raised:: sage: error_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: 12: 12, ....: (1, 3,): 42, ....: (2, 3,): 42, ....: (1, 2, 3,): 42} sage: error_game = CooperativeGame(error_function) Traceback (most recent call last): ... TypeError: key must be a tuple A test to ensure that the characteristic function is the power set of the grand coalition (ie all possible sub-coalitions):: sage: incorrect_function = {(): 0, ....: (1,): 6, ....: (2,): 12, ....: (3,): 42, ....: (1, 2, 3,): 42} sage: incorrect_game = CooperativeGame(incorrect_function) Traceback (most recent call last): ... ValueError: characteristic function must be the power set """ if type(characteristic_function) is not dict: raise TypeError("characteristic function must be a dictionary") self.ch_f = characteristic_function for key in self.ch_f: if len(str(key)) == 1 and type(key) is not tuple: self.ch_f[(key,)] = self.ch_f.pop(key) elif type(key) is not tuple: raise TypeError("key must be a tuple") for key in self.ch_f: sortedkey = tuple(sorted(list(key))) self.ch_f[sortedkey] = self.ch_f.pop(key) self.player_list = max(characteristic_function.keys(), key=lambda key: len(key)) for coalition in powerset(self.player_list): if tuple(sorted(list(coalition))) not in sorted(self.ch_f.keys()): raise ValueError("characteristic function must be the power set") self.number_players = len(self.player_list)
def poset_of_layers(self): """ Compute the poset of layers of the associated toric arrangement, using Lenz's algorithm [Len17a]. """ # TODO: implement for Q != 0 if self._Q.ncols() > 0: raise NotImplementedError A = self._A.transpose() E = range(A.nrows()) data = {} # compute Smith normal forms of all submatrices for S in powerset(E): D, U, V = A[S,:].smith_form() # D == U*A[S,:]*V diagonal = [D[i,i] if i < D.ncols() else 0 for i in xrange(len(S))] data[tuple(S)] = (diagonal, U) # generate al possible elements of the poset of layers elements = {tuple(S): list(vector(ZZ, x) for x in itertools.product(*(range(max(data[tuple(S)][0][i], 1)) for i in xrange(len(S))))) for S in powerset(E)} for l in elements.itervalues(): for v in l: v.set_immutable() possible_layers = list((S, x) for (S, l) in elements.iteritems() for x in l) uf = DisjointSet(possible_layers) cover_relations = [] for (S, l) in elements.iteritems(): diagonal_S, U_S = data[S] rk_S = A[S,:].rank() for s in S: i = S.index(s) # index where the element s appears in S T = tuple(t for t in S if t != s) diagonal_T, U_T = data[T] rk_T = A[T,:].rank() for x in l: h = (S, x) y = U_S**(-1) * x z = U_T * vector(ZZ, y[:i].list() + y[i+1:].list()) w = vector(ZZ, (a % diagonal_T[j] if diagonal_T[j] > 0 else 0 for j, a in enumerate(z))) w.set_immutable() ph = (T, w) if rk_S == rk_T: uf.union(h, ph) else: cover_relations.append((ph, h)) # find representatives for layers (we keep the representative (S,x) with maximal S) root_to_representative_dict = {} for root, subset in uf.root_to_elements_dict().iteritems(): S, x = max(subset, key=lambda (S, x): len(S)) S_labeled = tuple(self._E[i] for i in S) root_to_representative_dict[root] = (S_labeled, x) # get layers and cover relations layers = root_to_representative_dict.values() cover_relations = set( (root_to_representative_dict[uf.find(a)], root_to_representative_dict[uf.find(b)]) for (a,b) in cover_relations) return Poset(data=(layers, cover_relations), cover_relations=True)