def _compute_densities(self): densities = [0 for x in range(len(self.admissible_perms))] for densperm, coeff in self.density_pattern: if self.N < densperm.N: # too big to fit, then skip continue combs = Combinations(range(self.N), densperm.N) numcombs = combs.cardinality() # for each admissible permutation, compute the density of densperm in it for i in range(len(self.admissible_perms)): admissibleperm = self.admissible_perms[i] counter = 0 # number of copies of densperm in admissibleperm for c in combs: admissible_subperm = normalize([admissibleperm.perm[x] for x in c], densperm.N) admissible_subflag = PermFlag(admissible_subperm) if admissible_subflag == densperm: counter += 1 densities[i] += coeff*Integer(counter)/numcombs return densities
def add_assumption(self, lincomb, bound): """Assumption of the form [(perm, coef), ..., (perm, coef)] \geq bound.""" # switch on 'assumptions mode' if self.assumptions is False: self.assumptions = True assumption_densities = [-Rational(bound) for x in range(len(self.admissible_perms))] for densperm, coeff in lincomb: densperm = PermFlag(densperm) if self.N < densperm.N: # if too big to fit, then skip continue combs = Combinations(range(self.N), densperm.N) numcombs = combs.cardinality() # for each admissible permutation, compute the density of densperm in it for i in range(len(self.admissible_perms)): admissibleperm = self.admissible_perms[i] counter = 0 # number of copies of densperm in admissible perm for c in combs: admissible_subperm = normalize([admissibleperm.perm[x] for x in c], densperm.N) admissible_subflag = PermFlag(admissible_subperm) if admissible_subflag == densperm: counter += 1 assumption_densities[i] += coeff*Integer(counter)/numcombs #sys.stdout.write("assumption_densities[i] = %s\n" % str(assumption_densities[i])) self.assumption_densities.append(assumption_densities)
def antisymmetrized_coordinate_sums(dim, n): """ Return formal anti-symmetrized sum of multi-indices INPUT: - ``dim`` -- integer. The dimension (range of each index). - ``n`` -- integer. The total number of indices. OUTPUT: An anti-symmetrized formal sum of multi-indices (tuples of integers) EXAMPLES:: sage: from sage.modules.tensor_operations import antisymmetrized_coordinate_sums sage: antisymmetrized_coordinate_sums(3, 2) ((0, 1) - (1, 0), (0, 2) - (2, 0), (1, 2) - (2, 1)) """ from sage.structure.formal_sum import FormalSum table = [] from sage.groups.perm_gps.permgroup_named import SymmetricGroup S_d = SymmetricGroup(n) from sage.combinat.combination import Combinations for i in Combinations(range(dim), n): i = tuple(i) x = [] for g in S_d: x.append([g.sign(), g(i)]) x = FormalSum(x) table.append(x) return tuple(table)
def parallelotope(self, generators): r""" Return the parallelotope spanned by the generators. INPUT: - ``generators`` -- an iterable of anything convertible to vector (for example, a list of vectors) such that the vectors all have the same dimension. OUTPUT: The parallelotope. This is the multi-dimensional generalization of a parallelogram (2 generators) and a parallelepiped (3 generators). EXAMPLES:: sage: polytopes.parallelotope([ (1,0), (0,1) ]) A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 4 vertices sage: polytopes.parallelotope([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]]) A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 16 vertices """ try: generators = [vector(QQ, v) for v in generators] base_ring = QQ except TypeError: generators = [vector(RDF, v) for v in generators] base_ring = RDF from sage.combinat.combination import Combinations par = [0 * generators[0]] par += [sum(c) for c in Combinations(generators) if c != []] return Polyhedron(vertices=par, base_ring=base_ring)
def ChooseNK(n, k): """ All possible choices of k elements out of range(n) without repetitions. The elements of the output are tuples of Python int (and not Sage Integer). This was deprecated in :trac:`10534` for :func:`Combinations` (or ``itertools.combinations`` for doing iteration). EXAMPLES:: sage: from sage.combinat.choose_nk import ChooseNK sage: c = ChooseNK(4,2) doctest:...: DeprecationWarning: ChooseNk is deprecated and will be removed. Use Combinations instead (or combinations from the itertools module for iteration) See http://trac.sagemath.org/10534 for details. sage: c.first() [0, 1] sage: c.list() [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]] """ from sage.misc.superseded import deprecation deprecation( 10534, "ChooseNk is deprecated and will be removed. Use Combinations instead (or combinations from the itertools module for iteration)" ) from sage.combinat.combination import Combinations return Combinations(n, k)
def is_covering(self): """ Checks that all t-sets are in fact covered. INPUT: putative covering design OUTPUT: True if all t-sets are in at least one block NOTES: This is very slow and wasteful of memory. A faster Cython version will be added soon. EXAMPLES: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.is_covering() True sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 6]],0, 'not a covering') # last block altered sage: C.is_covering() False """ v = self.__v k = self.__k t = self.__t Svt = Combinations(range(v), t) Skt = Combinations(range(k), t) tset = {} # tables of t-sets: False = uncovered, True = covered for i in Svt: tset[tuple(i)] = False # mark all t-sets covered by each block for a in self.__incidence_structure.blocks(): for z in Skt: y = [a[x] for x in z] tset[tuple(y)] = True for i in Svt: if tset[tuple(i)] == False: # uncovered return False return True # everything was covered
def minimal_nonfaces(self): """ Return the minimal non-faces of ``self``. EXAMPLES:: sage: ClusterComplex(['A', 2]).minimal_nonfaces() [[0, 2], [0, 3], [1, 3], [1, 4], [2, 4]] """ from sage.combinat.combination import Combinations return [X for X in Combinations(self.vertices(), self.k() + 1) if not any(set(X).issubset(F) for F in self.facets())]
def royle_x_graph(): r""" Return a strongly regular graph, as described by Royle [Roy2008]_. INPUT: None. OUTPUT: An object of class ``Graph``, representing Royle's X graph [Roy2008]_. EXAMPLES: :: sage: from boolean_cayley_graphs.royle_x_graph import royle_x_graph sage: g = royle_x_graph() sage: g.is_strongly_regular() True sage: g.is_strongly_regular(parameters=True) (64, 35, 18, 20) REFERENCES: Royle [Roy2008]_. """ n = 8 order = 64 vecs = [vector([1] * n)] for a in Combinations(xsrange(1, n), 4): vecs.append(vector([-1 if x in a else 1 for x in xsrange(n)])) for b in Combinations(xsrange(n), 2): vecs.append(vector([-1 if x in b else 1 for x in xsrange(n)])) return Graph([(i, j) for i in xsrange(order) for j in xsrange(i + 1, order) if vecs[i] * vecs[j] == 0])
def _structures(self, structure_class, labels): """ EXAMPLES:: sage: S = species.SubsetSpecies() sage: S.structures([1,2]).list() [{}, {1}, {2}, {1, 2}] sage: S.structures(['a','b']).list() [{}, {'a'}, {'b'}, {'a', 'b'}] """ from sage.combinat.combination import Combinations for c in Combinations(range(1, len(labels) + 1)): yield structure_class(self, labels, c)
def is_covering(self): """ Check all `t`-sets are in fact covered by the blocks of ``self``. .. NOTE:: This is very slow and wasteful of memory. EXAMPLES:: sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.is_covering() True sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], ....: [2, 4, 6]], 0, 'not a covering') # last block altered sage: C.is_covering() False """ v = self.__v k = self.__k t = self.__t Svt = Combinations(range(v), t) Skt = Combinations(range(k), t) tset = {} # tables of t-sets: False = uncovered, True = covered for i in Svt: tset[tuple(i)] = False # mark all t-sets covered by each block for a in self.__incidence_structure.blocks(): for z in Skt: y = (a[x] for x in z) tset[tuple(y)] = True for i in Svt: if not tset[tuple(i)]: # uncovered return False return True # everything was covered
def c3_func(SUK, prec=106): r""" Return the constant `c_3` from Smart's 1995 TCDF paper, [Sma1995]_ INPUT: - ``SUK`` -- a group of `S`-units - ``prec`` -- (default: 106) the precision of the real field OUTPUT: The constant ``c3``, as a real number EXAMPLES:: sage: from sage.rings.number_field.S_unit_solver import c3_func sage: K.<xi> = NumberField(x^3-3) sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3))) sage: c3_func(SUK) # abs tol 1e-29 0.4257859134798034746197327286726 .. NOTE:: The numerator should be as close to 1 as possible, especially as the rank of the `S`-units grows large REFERENCES: - [Sma1995]_ p. 823 """ R = RealField(prec) all_places = list(SUK.primes()) + SUK.number_field().places(prec) Possible_U = Combinations(all_places, SUK.rank()) c1 = R(0) for U in Possible_U: # first, build the matrix C_{i,U} columns_of_C = [] for unit in SUK.fundamental_units(): columns_of_C.append(column_Log(SUK, unit, U, prec)) C = Matrix(SUK.rank(), SUK.rank(), columns_of_C) # Is it invertible? if abs(C.determinant()) > 10**(-10): poss_c1 = C.inverse().apply_map(abs).norm(Infinity) c1 = R(max(poss_c1, c1)) return R(0.9999999) / (c1*SUK.rank())
def fibration_generator(self, dim): """ Generate the lattice polytope fibrations. For the purposes of this function, a lattice polytope fiber is a sub-lattice polytope. Projecting the plane spanned by the subpolytope to a point yields another lattice polytope, the base of the fibration. INPUT: - ``dim`` -- integer. The dimension of the lattice polytope fiber. OUTPUT: A generator yielding the distinct lattice polytope fibers of given dimension. EXAMPLES:: sage: P = Polyhedron(toric_varieties.P4_11169().fan().rays(), base_ring=ZZ) sage: list( P.fibration_generator(2) ) [A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices] """ from sage.combinat.combination import Combinations if not self.is_compact(): raise ValueError('Only polytopes (compact polyhedra) are allowed.') nonzero_points = [p for p in self.integral_points() if not p.is_zero()] origin = [[0] * self.ambient_dim()] fibers = set() parent = self.parent() for points in Combinations(nonzero_points, dim): plane = parent.element_class(parent, [origin, [], points], None) if plane.dim() != dim: continue fiber = self.intersection(plane) if fiber.base_ring() is not ZZ: continue fiber_vertices = tuple( sorted(tuple(v) for v in fiber.vertex_generator())) if fiber_vertices not in fibers: yield fiber fibers.update([fiber_vertices]) plane._delete()
def attacking_boxes(self): """ Returns a list of pairs of boxes in ``self`` that are attacking. EXAMPLES:: sage: a = AugmentedLatticeDiagramFilling([[1,6],[2],[3,4,2],[],[],[5,5]]) sage: a.attacking_boxes()[:5] [((1, 1), (2, 1)), ((1, 1), (3, 1)), ((1, 1), (6, 1)), ((1, 1), (2, 0)), ((1, 1), (3, 0))] """ boxes = self.boxes() res = [] for (i, j), (ii, jj) in Combinations(boxes, 2): if self.are_attacking(i, j, ii, jj): res.append(((i, j), (ii, jj))) return res
def subperm_density(self, perm): """ Return density of perm in self. INPUT: - perm: permutation of type PermFlag EXAMPLE: sage: P = PermFlag("1234765") sage: P.subperm_density(PermFlag("123")) 22/35 """ combs = Combinations(self.N, perm.N) counter = 0 for c in combs: if self._induced_subpattern(c) == perm.perm: counter += 1 return Integer(counter) / combs.cardinality()
def _is_admissible(self, perm): """Check if perm is admissible.""" if not isinstance(perm, PermFlag): raise ValueError("Input must be of a valid PermFlag object.") order = perm.N verdict = True for fp in self.forbidden_perms: perm_contains_fp = False for c in Combinations(order, fp.N): induced_subperm = perm._induced_subpattern(c) if induced_subperm == fp.perm: perm_contains_fp = True break if perm_contains_fp: verdict = False break return verdict
def _init_antisymmetric(self): """ Initialization for the antisymmetric product EXAMPLES:: sage: from sage.modules.tensor_operations import \ ....: VectorCollection, TensorOperation sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) sage: Alt2_R = TensorOperation([R, R], operation='antisymmetric') # indirect doctest sage: sorted(Alt2_R._index_map.iteritems()) [((0, 1), 0), ((0, 2), 1)] """ n = len(self._V) dim = self._V[0].degree() Alt = antisymmetrized_coordinate_sums(dim, n) from sage.combinat.combination import Combinations for i in Combinations(range(self._V[0].n_vectors()), n): ray = self._init_power_operation_vectors(i, Alt) if ray is not None: self._index_map[tuple(i)] = ray self._symmetrize_indices = True
def _compute_t_flag_products(self, ti): """For each type t, generate all quintuples (s, p1, p2, a, b): s: admissible permutation (its index) p1: flag1 index in self.flags[t] p2: flag2 index in self.flags[t] a/b: density of p1*p2 in s 10 Dec 15: for N=6, runs 4min28s """ Nset = range(self.N) num_types = len(self.types) num_admissible_perms = len(self.admissible_perms) range_num_admissible_perms = range(num_admissible_perms) t = self.types[ti] m = int((self.N + t.N)/2) # flag order t_products = list() # will hold products of flags on type t # p(F1,F2,A) = 1/|\Theta|*\sum_{\theta} p(F1,F2,theta,A) denom = binomial(self.N,t.N)*binomial(self.N-t.N,m-t.N) bigTheta = Combinations(self.N,t.N)#.list() num_ti_flags = len(self.flags[ti]) range_num_ti_flags = range(num_ti_flags) for permi in range_num_admissible_perms: perm = self.admissible_perms[permi] # count number of times when F1xF2 == (perm,theta) // over all theta counter = [[0 for fi in range_num_ti_flags] for fj in range_num_ti_flags] for S0 in bigTheta: # S0 := im(theta) S0vals_in_perm = [perm.perm[x] for x in S0] nonimtheta = [x for x in Nset if x not in S0] S1_possibilities = Combinations(nonimtheta, m-t.N) # condition 1 if not PermFlag(normalize(S0vals_in_perm, t.N)) == t: continue # go to next theta # given cond1, how many times does (cond2 && cond3) hold for each fi,fj from ti-flags for S1 in S1_possibilities: S1S0 = S1+S0 S1S0.sort() subperm1 = [perm.perm[x] for x in S1S0] subperm1_t = [subperm1.index(perm.perm[x]) for x in S0] subperm2 = [perm.perm[x] for x in [x for x in Nset if x not in S1]] subperm2_t = [subperm2.index(perm.perm[x]) for x in S0] perm1flag = PermFlag(normalize(subperm1, m), subperm1_t) toperm = normalize(subperm2,m) perm2flag = PermFlag(toperm, subperm2_t) for fi in range_num_ti_flags: for fj in range(fi, num_ti_flags): # only need to do distinct flags flg1 = self.flags[ti][fi] flg2 = self.flags[ti][fj] # condition 2 if not perm1flag == flg1: continue # go to the next S1 # condition 3 if perm2flag == flg2: counter[fi][fj] += 1 # end fj # end fi # end S1 # end S0 (i.e. theta) for fi in range_num_ti_flags: for fj in range_num_ti_flags: if counter[fi][fj] > 0: # save to t_products t_products.append([permi, fi, fj, counter[fi][fj], denom]) sys.stdout.write("%d, " % ti) sys.stdout.flush() return ti,t_products
def is_block_design(self): """ Returns a pair True, pars if the incidence structure is a t-design, for some t, where pars is the list of parameters [t, v, k, lmbda]. The largest possible t is returned, provided t=10. EXAMPLES:: sage: BD = IncidenceStructure(range(7),[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: BD.is_block_design() (True, [2, 7, 3, 1]) sage: BD.block_design_checker(2, 7, 3, 1) True sage: BD = WittDesign(9) # optional - gap_packages (design package) sage: BD.is_block_design() # optional - gap_packages (design package) (True, [2, 9, 3, 1]) sage: BD = WittDesign(12) # optional - gap_packages (design package) sage: BD.is_block_design() # optional - gap_packages (design package) (True, [5, 12, 6, 1]) sage: BD = AffineGeometryDesign(3, 1, GF(2)) sage: BD.is_block_design() (True, [2, 8, 2, 2]) """ from sage.combinat.designs.incidence_structures import coordinatewise_product from sage.combinat.combinat import unordered_tuples from sage.combinat.combination import Combinations A = self.incidence_matrix() v = len(self.points()) b = len(self.blocks()) k = sum(A.columns()[0]) rowsA = A.rows() VS = rowsA[0].parent() r = sum(rowsA[0]) for i in range(b): if not(sum(A.columns()[i]) == k): return False for i in range(v): if not(sum(A.rows()[i]) == r): return False t_found_yet = False lambdas = [] for t in range(2,min(v,11)): #print t L1 = Combinations(range(v),t) L2 = [[rowsA[i] for i in L] for L in L1] #print t,len(L2) lmbda = VS(coordinatewise_product(L2[0])).hamming_weight() lambdas.append(lmbda) pars = [t,v,k,lmbda] #print pars for ell in L2: a = VS(coordinatewise_product(ell)).hamming_weight() if not(a == lmbda) or a==0: if not(t_found_yet): pars = [t-1,v,k,lambdas[t-3]] return False, pars else: #print pars, lambdas pars = [t-1,v,k,lambdas[t-3]] return True, pars t_found_yet = True pars = [t-1,v,k,lambdas[t-3]] return True, pars
def trivial_covering_design(v, k, t): r""" Construct a trivial covering design. INPUT: v -- integer, size of point set k -- integer, cardinality of each block t -- integer, cardinality of sets being covered OUTPUT: (v,k,t) covering design EXAMPLE: sage: C = trivial_covering_design(8,3,1) sage: print C C(8,3,1) = 3 Method: Trivial 0 1 2 0 6 7 3 4 5 sage: C = trivial_covering_design(5,3,2) sage: print C 4 <= C(5,3,2) <= 10 Method: Trivial 0 1 2 0 1 3 0 1 4 0 2 3 0 2 4 0 3 4 1 2 3 1 2 4 1 3 4 2 3 4 NOTES: Cases are: \begin{enumerate} \item $t=0$: This could be empty, but it's a useful convention to have one block (which is empty if $k=0$). \item $t=1$: This contains $\lceil v/k \rceil$ blocks: $[0,...,k-1]$,[k,...,2k-1],...$. The last block wraps around if $k$ does not divide $v$. \item anything else: Just use every $k$-subset of $[0,1,...,v-1]$. \end{enumerate} """ if t == 0: #single block [0,...,k-1] blk = [] for i in range(k): blk.append(i) return CoveringDesign(v, k, t, 1, range(v), [blk], 1, "Trivial") if t == 1: #blocks [0,...,k-1],[k,...,2k-1],... size = Rational((v, k)).ceil() blocks = [] for i in range(size - 1): blk = [] for j in range(i * k, (i + 1) * k): blk.append(j) blocks.append(blk) blk = [] # last block: if k does not divide v, wrap around for j in range((size - 1) * k, v): blk.append(j) for j in range(k - len(blk)): blk.append(j) blk.sort() blocks.append(blk) return CoveringDesign(v, k, t, size, range(v), blocks, size, "Trivial") # default case, all k-subsets return CoveringDesign(v, k, t, binomial(v, k), range(v), Combinations(range(v), k), schonheim(v, k, t), "Trivial")
def ChessboardGraphGenerator(dim_list, rook=True, rook_radius=None, bishop=True, bishop_radius=None, knight=True, knight_x=1, knight_y=2, relabel=False): r""" Returns a Graph built on a `d`-dimensional chessboard with prescribed dimensions and interconnections. This function allows to generate many kinds of graphs corresponding to legal movements on a `d`-dimensional chessboard: Queen Graph, King Graph, Knight Graphs, Bishop Graph, and many generalizations. It also allows to avoid redondant code. INPUT: - ``dim_list`` -- an iterable object (list, set, dict) providing the dimensions `n_1, n_2, \ldots, n_d`, with `n_i \geq 1`, of the chessboard. - ``rook`` -- (default: ``True``) boolean value indicating if the chess piece is able to move as a rook, that is at any distance along a dimension. - ``rook_radius`` -- (default: None) integer value restricting the rook-like movements to distance at most `rook_radius`. - ``bishop`` -- (default: ``True``) boolean value indicating if the chess piece is able to move like a bishop, that is along diagonals. - ``bishop_radius`` -- (default: None) integer value restricting the bishop-like movements to distance at most `bishop_radius`. - ``knight`` -- (default: ``True``) boolean value indicating if the chess piece is able to move like a knight. - ``knight_x`` -- (default: 1) integer indicating the number on steps the chess piece moves in one dimension when moving like a knight. - ``knight_y`` -- (default: 2) integer indicating the number on steps the chess piece moves in the second dimension when moving like a knight. - ``relabel`` -- (default: ``False``) a boolean set to ``True`` if vertices must be relabeled as integers. OUTPUT: - A Graph build on a `d`-dimensional chessboard with prescribed dimensions, and with edges according given parameters. - A string encoding the dimensions. This is mainly useful for providing names to graphs. EXAMPLES: A `(2,2)`-King Graph is isomorphic to the complete graph on 4 vertices:: sage: G, _ = graphs.ChessboardGraphGenerator( [2,2] ) sage: G.is_isomorphic( graphs.CompleteGraph(4) ) True A Rook's Graph in 2 dimensions is isomporphic to the Cartesian product of 2 complete graphs:: sage: G, _ = graphs.ChessboardGraphGenerator( [3,4], rook=True, rook_radius=None, bishop=False, knight=False ) sage: H = ( graphs.CompleteGraph(3) ).cartesian_product( graphs.CompleteGraph(4) ) sage: G.is_isomorphic(H) True TESTS: Giving dimensions less than 2:: sage: graphs.ChessboardGraphGenerator( [0, 2] ) Traceback (most recent call last): ... ValueError: The dimensions must be positive integers larger than 1. Giving non integer dimensions:: sage: graphs.ChessboardGraphGenerator( [4.5, 2] ) Traceback (most recent call last): ... ValueError: The dimensions must be positive integers larger than 1. Giving too few dimensions:: sage: graphs.ChessboardGraphGenerator( [2] ) Traceback (most recent call last): ... ValueError: The chessboard must have at least 2 dimensions. Giving a non-iterable object as first parameter:: sage: graphs.ChessboardGraphGenerator( 2, 3 ) Traceback (most recent call last): ... TypeError: The first parameter must be an iterable object. Giving too small rook radius:: sage: graphs.ChessboardGraphGenerator( [2, 3], rook=True, rook_radius=0 ) Traceback (most recent call last): ... ValueError: The rook_radius must be either None or have an integer value >= 1. Giving wrong values for knights movements:: sage: graphs.ChessboardGraphGenerator( [2, 3], rook=False, bishop=False, knight=True, knight_x=1, knight_y=-1 ) Traceback (most recent call last): ... ValueError: The knight_x and knight_y values must be integers of value >= 1. """ from sage.rings.integer_ring import ZZ # We decode the dimensions of the chessboard try: dim = list(dim_list) nb_dim = len(dim) except TypeError: raise TypeError('The first parameter must be an iterable object.') if nb_dim < 2: raise ValueError('The chessboard must have at least 2 dimensions.') if any(a not in ZZ or a < 1 for a in dim): raise ValueError( 'The dimensions must be positive integers larger than 1.') dimstr = str(tuple(dim)) # We check the radius toward neighbors if rook: if rook_radius is None: rook_radius = max(dim) elif not rook_radius in ZZ or rook_radius < 1: raise ValueError( 'The rook_radius must be either None or have an integer value >= 1.' ) if bishop: if bishop_radius is None: bishop_radius = max(dim) elif not bishop_radius in ZZ or bishop_radius < 1: raise ValueError( 'The bishop_radius must be either None or have an integer value >= 1.' ) if knight and (not knight_x in ZZ or not knight_y in ZZ or knight_x < 1 or knight_y < 1): raise ValueError( 'The knight_x and knight_y values must be integers of value >= 1.') # We build the set of vertices of the d-dimensionnal chessboard from itertools import product V = [list(x) for x in list(product(*[range(_) for _ in dim]))] from sage.combinat.combination import Combinations combin = Combinations(range(nb_dim), 2) from sage.graphs.graph import Graph G = Graph() for u in V: uu = tuple(u) G.add_vertex(uu) if rook: # We add edges to vertices we can reach when moving in one dimension for d in xrange(nb_dim): v = u[:] for k in xrange(v[d] + 1, min(dim[d], v[d] + 1 + rook_radius)): v[d] = k G.add_edge(uu, tuple(v)) if bishop or knight: # We add edges to vertices we can reach when moving in two dimensions for dx, dy in combin: n = dim[dx] m = dim[dy] v = u[:] i = u[dx] j = u[dy] if bishop: # Diagonal for k in xrange(1, min(n - i, m - j, bishop_radius + 1)): v[dx] = i + k v[dy] = j + k G.add_edge(uu, tuple(v)) # Anti-diagonal for k in xrange(min(i, m - j - 1, bishop_radius)): v[dx] = i - k - 1 v[dy] = j + k + 1 G.add_edge(uu, tuple(v)) if knight: # Moving knight_x in one dimension and knight_y in another # dimension if i + knight_y < n: if j + knight_x < m: v[dx] = i + knight_y v[dy] = j + knight_x G.add_edge(uu, tuple(v)) if j - knight_x >= 0: v[dx] = i + knight_y v[dy] = j - knight_x G.add_edge(uu, tuple(v)) if j + knight_y < m: if i + knight_x < n: v[dx] = i + knight_x v[dy] = j + knight_y G.add_edge(uu, tuple(v)) if i - knight_x >= 0: v[dx] = i - knight_x v[dy] = j + knight_y G.add_edge(uu, tuple(v)) if relabel: G.relabel(inplace=True) return G, dimstr
def generalized_noncrossing_partitions(self, m, c=None, positive=False): r""" Return the set of all chains of length ``m`` in the noncrossing partition lattice of ``self``, see :meth:`noncrossing_partition_lattice`. .. NOTE:: ``self`` is assumed to be well-generated. INPUT: - ``c`` -- (default: ``None``) if an element ``c`` in ``self`` is given, it is used as the maximal element in the interval - ``positive`` -- (default: ``False``) if ``True``, only those generalized noncrossing partitions of full support are returned EXAMPLES:: sage: W = ReflectionGroup((1,1,3)) # optional - gap3 sage: sorted([w.reduced_word() for w in chain] # optional - gap3 ....: for chain in W.generalized_noncrossing_partitions(2)) # optional - gap3 [[[], [], [1, 2]], [[], [1], [2]], [[], [1, 2], []], [[], [1, 2, 1], [1]], [[], [2], [1, 2, 1]], [[1], [], [2]], [[1], [2], []], [[1, 2], [], []], [[1, 2, 1], [], [1]], [[1, 2, 1], [1], []], [[2], [], [1, 2, 1]], [[2], [1, 2, 1], []]] sage: sorted([w.reduced_word() for w in chain] # optional - gap3 ....: for chain in W.generalized_noncrossing_partitions(2, positive=True)) # optional - gap3 [[[], [1, 2], []], [[], [1, 2, 1], [1]], [[1], [2], []], [[1, 2], [], []], [[1, 2, 1], [], [1]], [[1, 2, 1], [1], []], [[2], [1, 2, 1], []]] """ from sage.combinat.combination import Combinations NC = self.noncrossing_partition_lattice(c=c) one = self.one() if c is None: c = self.coxeter_element() chains = NC.chains() NCm = set() iter = chains.breadth_first_search_iterator() chain = next(iter) chain = next(iter) while len(chain) <= m: chain.append(c) for i in range(len(chain) - 1, 0, -1): chain[i] = chain[i - 1]**-1 * chain[i] k = m + 1 - len(chain) for positions in Combinations(range(m + 1), k): ncm = [] for l in range(m + 1): if l in positions: ncm.append(one) else: l_prime = l - len( [i for i in positions if i <= l]) ncm.append(chain[l_prime]) if not positive or prod(ncm[:-1]).has_full_support(): NCm.add(tuple(ncm)) try: chain = next(iter) except StopIteration: chain = list(range(m + 1)) return NCm
def trivial_covering_design(v, k, t): r""" Construct a trivial covering design. INPUT: - ``v`` -- integer, size of point set - ``k`` -- integer, cardinality of each block - ``t`` -- integer, cardinality of sets being covered OUTPUT: A trivial `(v, k, t)` covering design EXAMPLES:: sage: C = trivial_covering_design(8, 3, 1) sage: print(C) C(8, 3, 1) = 3 Method: Trivial 0 1 2 0 6 7 3 4 5 sage: C = trivial_covering_design(5, 3, 2) sage: print(C) 4 <= C(5, 3, 2) <= 10 Method: Trivial 0 1 2 0 1 3 0 1 4 0 2 3 0 2 4 0 3 4 1 2 3 1 2 4 1 3 4 2 3 4 NOTES: Cases are: * `t=0`: This could be empty, but it's a useful convention to have one block (which is empty if $k=0$). * `t=1` : This contains `\lceil v/k \rceil` blocks: `[0, ..., k-1], [k, ..., 2k-1], ...`. The last block wraps around if `k` does not divide `v`. * anything else: Just use every `k`-subset of `[0, 1,..., v-1]`. """ if t == 0: # single block [0, ..., k-1] blk = [] for i in range(k): blk.append(i) return CoveringDesign(v, k, t, 1, range(v), [blk], 1, "Trivial") if t == 1: # blocks [0, ..., k-1], [k, ..., 2k-1], ... size = Rational((v, k)).ceil() blocks = [] for i in range(size - 1): blk = [] for j in range(i * k, (i + 1) * k): blk.append(j) blocks.append(blk) blk = [] # last block: if k does not divide v, wrap around for j in range((size - 1) * k, v): blk.append(j) for j in range(k - len(blk)): blk.append(j) blk.sort() blocks.append(blk) return CoveringDesign(v, k, t, size, range(v), blocks, size, "Trivial") # default case, all k-subsets return CoveringDesign(v, k, t, binomial(v, k), range(v), Combinations(range(v), k), schonheim(v, k, t), "Trivial")