def test_relabel(self): r""" Try the function canonical labels for a random even modular subgroup. EXAMPLES:: sage: from sage.modular.arithgroup.tests import Test sage: Test().test_relabel() # random """ if prandom.uniform(0, 1) < self.odd_probability: G = random_odd_arithgroup(self.index) else: G = random_even_arithgroup(self.index) G.relabel() s2 = G._S2 s3 = G._S3 l = G._L r = G._R # 0 should be stabilized by the mapping # used for renumbering so we start at 1 p = list(range(1, self.index)) for _ in range(10): prandom.shuffle(p) # we add 0 to the mapping pp = [0] + p ss2 = [None] * self.index ss3 = [None] * self.index ll = [None] * self.index rr = [None] * self.index for i in range(self.index): ss2[pp[i]] = pp[s2[i]] ss3[pp[i]] = pp[s3[i]] ll[pp[i]] = pp[l[i]] rr[pp[i]] = pp[r[i]] if G.is_even(): GG = EvenArithmeticSubgroup_Permutation(ss2, ss3, ll, rr) else: GG = OddArithmeticSubgroup_Permutation(ss2, ss3, ll, rr) GG.relabel() for elt in ['_S2', '_S3', '_L', '_R']: if getattr(G, elt) != getattr(GG, elt): print("s2 = %s" % str(s2)) print("s3 = %s" % str(s3)) print("ss2 = %s" % str(ss2)) print("ss3 = %s" % str(ss3)) print("pp = %s" % str(pp)) raise AssertionError("%s does not coincide" % elt)
def test_relabel(self): r""" Try the function canonic labels for a random even modular subgroup. EXAMPLES:: sage: from sage.modular.arithgroup.tests import Test sage: Test().test_relabel() # random """ if prandom.uniform(0,1) < self.odd_probability: G = random_odd_arithgroup(self.index) else: G = random_even_arithgroup(self.index) G.relabel() s2 = G._S2 s3 = G._S3 l = G._L r = G._R # 0 should be stabilized by the mapping # used for renumbering so we start at 1 p = range(1,self.index) for _ in xrange(10): prandom.shuffle(p) # we add 0 to the mapping pp = [0] + p ss2 = [None]*self.index ss3 = [None]*self.index ll = [None]*self.index rr = [None]*self.index for i in xrange(self.index): ss2[pp[i]] = pp[s2[i]] ss3[pp[i]] = pp[s3[i]] ll[pp[i]] = pp[l[i]] rr[pp[i]] = pp[r[i]] if G.is_even(): GG = EvenArithmeticSubgroup_Permutation(ss2,ss3,ll,rr) else: GG = OddArithmeticSubgroup_Permutation(ss2,ss3,ll,rr) GG.relabel() for elt in ['_S2','_S3','_L','_R']: if getattr(G,elt) != getattr(GG,elt): print "s2 = %s" %str(s2) print "s3 = %s" %str(s3) print "ss2 = %s" %str(ss2) print "ss3 = %s" %str(ss3) print "pp = %s" %str(pp) raise AssertionError("%s does not coincide" %elt)
def random_even_arithgroup(index,nu2_max=None,nu3_max=None): r""" Return a random even arithmetic subgroup EXAMPLES:: sage: import sage.modular.arithgroup.tests as tests sage: G = tests.random_even_arithgroup(30); G # random Arithmetic subgroup of index 30 sage: G.is_even() True """ from sage.groups.perm_gps.permgroup import PermutationGroup test = False if nu2_max is None: nu2_max = index//5 elif nu2_max == 0: assert index%2 == 0 if nu3_max is None: nu3_max = index//7 elif nu3_max == 0: assert index%3 == 0 while not test: nu2 = prandom.randint(0,nu2_max) nu2 = index%2 + nu2*2 nu3 = prandom.randint(0,nu3_max) nu3 = index%3 + nu3*3 l = range(1,index+1) prandom.shuffle(l) S2 = [] for i in xrange(nu2): S2.append((l[i],)) for i in xrange(nu2,index,2): S2.append((l[i],l[i+1])) prandom.shuffle(l) S3 = [] for i in xrange(nu3): S3.append((l[i],)) for i in xrange(nu3,index,3): S3.append((l[i],l[i+1],l[i+2])) G = PermutationGroup([S2,S3]) test = G.is_transitive() return ArithmeticSubgroup_Permutation(S2=S2,S3=S3)
def random_even_arithgroup(index, nu2_max=None, nu3_max=None): r""" Return a random even arithmetic subgroup EXAMPLES:: sage: import sage.modular.arithgroup.tests as tests sage: G = tests.random_even_arithgroup(30); G # random Arithmetic subgroup of index 30 sage: G.is_even() True """ from sage.groups.perm_gps.permgroup import PermutationGroup test = False if nu2_max is None: nu2_max = index // 5 elif nu2_max == 0: assert index % 2 == 0 if nu3_max is None: nu3_max = index // 7 elif nu3_max == 0: assert index % 3 == 0 while not test: nu2 = prandom.randint(0, nu2_max) nu2 = index % 2 + nu2 * 2 nu3 = prandom.randint(0, nu3_max) nu3 = index % 3 + nu3 * 3 l = list(range(1, index + 1)) prandom.shuffle(l) S2 = [] for i in range(nu2): S2.append((l[i], )) for i in range(nu2, index, 2): S2.append((l[i], l[i + 1])) prandom.shuffle(l) S3 = [] for i in range(nu3): S3.append((l[i], )) for i in range(nu3, index, 3): S3.append((l[i], l[i + 1], l[i + 2])) G = PermutationGroup([S2, S3]) test = G.is_transitive() return ArithmeticSubgroup_Permutation(S2=S2, S3=S3)
def random_partial_injection(n): r""" Return a uniformly chosen random partial injection on 0, 1, ..., n-1. INPUT: - ``n`` -- integer OUTPUT: list EXAMPLES:: sage: from slabbe import random_partial_injection sage: random_partial_injection(10) [3, 5, 2, None, 1, None, 0, 8, 7, 6] sage: random_partial_injection(10) [1, 7, 4, 8, 3, 5, 9, None, 6, None] sage: random_partial_injection(10) [5, 6, 8, None, 7, 4, 0, 9, None, None] TODO:: Adapt the code once this is merged: https://trac.sagemath.org/ticket/24416 AUTHORS: - Sébastien Labbé and Vincent Delecroix, Nov 30, 2017, Sage Thursdays at LaBRI """ L = number_of_partial_injection(n) s = sum(L).n() L = [a/s for a in L] # because of the bug #24416 X = GeneralDiscreteDistribution(L) k = X.get_random_element() codomain = sample(range(n), k) missing = [None]*(n-k) codomain.extend(missing) shuffle(codomain) return codomain
def _get_random_cylinder_diagram(self): r""" Return a random cylinder diagram with right parameters """ test = False while test: n = random.randint(self.min_num_seps,self.max_num_seps) S = SymmetricGroup(2*n) bot = S.random_element() b = [[i-1 for i in c] for c in bot.cycle_tuples(singletons=True)] p = Partitions(2*n,length=len(b)).random_element() top = S([i+1 for i in canonical_perm(p)]) t = [[i-1 for i in c] for c in top.cycle_tuples(singletons=True)] prandom.shuffle(t) c = CylinderDiagram(zip(b,t)) test = c.is_connected() return c
def _get_random_cylinder_diagram(self): r""" Return a random cylinder diagram with right parameters """ test = False while test: n = random.randint(self.min_num_seps, self.max_num_seps) S = SymmetricGroup(2 * n) bot = S.random_element() b = [[i - 1 for i in c] for c in bot.cycle_tuples(singletons=True)] p = Partitions(2 * n, length=len(b)).random_element() top = S([i + 1 for i in canonical_perm(p)]) t = [[i - 1 for i in c] for c in top.cycle_tuples(singletons=True)] prandom.shuffle(t) c = CylinderDiagram(zip(b, t)) test = c.is_connected() return c
def parallel_iter(f, inputs): """ Reference parallel iterator implementation. INPUT: - ``f`` -- a Python function that can be pickled using the pickle_function command. - ``inputs`` -- a list of pickleable pairs (args, kwds), where args is a tuple and kwds is a dictionary. OUTPUT: - iterator over 2-tuples ``(inputs[i], f(inputs[i]))``, where the order may be completely random EXAMPLES:: sage: def f(N,M=10): return N*M sage: inputs = [((2,3),{}), (tuple([]), {'M':5,'N':3}), ((2,),{})] sage: set_random_seed(0) sage: for a, val in sage.parallel.reference.parallel_iter(f, inputs): ....: print((a, val)) (((2,), {}), 20) (((), {'M': 5, 'N': 3}), 15) (((2, 3), {}), 6) sage: for a, val in sage.parallel.reference.parallel_iter(f, inputs): ....: print((a, val)) (((), {'M': 5, 'N': 3}), 15) (((2,), {}), 20) (((2, 3), {}), 6) """ v = list(inputs) shuffle(v) for args, kwds in v: yield ((args, kwds), f(*args, **kwds))
def parallel_iter(f, inputs): """ Reference parallel iterator implementation. INPUT: - ``f`` -- a Python function that can be pickled using the pickle_function command. - ``inputs`` -- a list of pickleable pairs (args, kwds), where args is a tuple and kwds is a dictionary. OUTPUT: - iterator over 2-tuples ``(inputs[i], f(inputs[i]))``, where the order may be completely random EXAMPLES:: sage: def f(N,M=10): return N*M sage: inputs = [((2,3),{}), (tuple([]), {'N':3,'M':5}), ((2,),{})] sage: set_random_seed(0) sage: for a, val in sage.parallel.reference.parallel_iter(f, inputs): ....: print((a, val)) (((2,), {}), 20) (((), {'M': 5, 'N': 3}), 15) (((2, 3), {}), 6) sage: for a, val in sage.parallel.reference.parallel_iter(f, inputs): ....: print((a, val)) (((), {'M': 5, 'N': 3}), 15) (((2,), {}), 20) (((2, 3), {}), 6) """ v = list(inputs) shuffle(v) for args, kwds in v: yield ((args, kwds), f(*args, **kwds))
def test_finite_poset(P): """ Test several functions on a given finite poset. The function contains tests of different kinds, for example - Numerical properties jump number, dimension etc. can't be a bigger in a subposet with one element less. - "Dual tests", for example the dual of meet-semilattice must be a join-semilattice. - Random tries: for example if the dimension of a poset is `k`, then it can't be the intersection of `k-1` random linear extensions. EXAMPLES:: sage: from sage.tests.finite_poset import test_finite_poset sage: P = posets.RandomPoset(10, 0.15) sage: test_finite_poset(P) is None # Long time True """ from sage.combinat.posets.posets import Poset from sage.combinat.subset import Subsets from sage.misc.prandom import shuffle from sage.misc.misc import attrcall e = P.random_element() P_one_less = P.subposet([x for x in P if x != e]) # Cardinality if len(P) != P.cardinality(): raise ValueError("error 1 in cardinality") if P.cardinality() - 1 != P_one_less.cardinality(): raise ValueError("error 5 in cardinality") # Height h1 = P.height() h2, chain = P.height(certificate=True) if h1 != h2: raise ValueError("error 1 in height") if h1 != len(chain): raise ValueError("error 2 in height") if not P.is_chain_of_poset(chain): raise ValueError("error 3 in height") if len(P.random_maximal_chain()) > h1: raise ValueError("error 4 in height") if h1 - P_one_less.height() not in [0, 1]: raise ValueError("error 5 in height") # Width w1 = P.width() w2, antichain = P.width(certificate=True) if w1 != w2: raise ValueError("error 1 in width") if w1 != len(antichain): raise ValueError("error 2 in width") if not P.is_antichain_of_poset(antichain): raise ValueError("error 3 in width") if len(P.random_maximal_antichain()) > w1: raise ValueError("error 4 in width") if w1 - P_one_less.width() not in [0, 1]: raise ValueError("error 5 in width") # Dimension dim1 = P.dimension() dim2, linexts = P.dimension(certificate=True) if dim1 != dim2: raise ValueError("error 1 in dimension") if dim1 != len(linexts): raise ValueError("error 2 in dimension") P_ = Poset((P.list(), lambda a, b: all( linext.index(a) < linext.index(b) for linext in linexts))) if P_ != Poset(P.hasse_diagram()): raise ValueError("error 3 in dimension") x = [P.random_linear_extension() for _ in range(dim1 - 1)] P_ = Poset( (P.list(), lambda a, b: all(linext.index(a) < linext.index(b) for linext in x))) if P_ == Poset(P.hasse_diagram()): raise ValueError("error 4 in dimension") if dim1 - P_one_less.dimension() < 0: raise ValueError("error 5 in dimension") # Jump number j1 = P.jump_number() j2, linext = P.jump_number(certificate=True) if j1 != j2: raise ValueError("error 1 in jump number") if P.linear_extension(linext).jump_count() != j1: raise ValueError("error 2 in jump number") if not P.is_linear_extension(linext): raise ValueError("error 3 in jump number") if P.linear_extension(P.random_linear_extension()).jump_count() < j1: raise ValueError("error 4 in jump number") if j1 - P_one_less.jump_number() not in [0, 1]: raise ValueError("error 5 in jump number") P_dual = P.dual() selfdual_properties = [ 'chain', 'bounded', 'connected', 'graded', 'ranked', 'series_parallel', 'slender', 'lattice' ] for prop in selfdual_properties: f = attrcall('is_' + prop) if f(P) != f(P_dual): raise ValueError("error in self-dual property %s" % prop) if P.is_graded(): if P.is_bounded(): if P.is_eulerian() != P_dual.is_eulerian(): raise ValueError("error in self-dual property eulerian") if P.is_eulerian(): P_ = P.star_product(P) if not P_.is_eulerian(): raise ("error in star product / eulerian") chain1 = P.random_maximal_chain() if len(chain1) != h1: raise ValueError("error in is_graded") if not P.is_ranked(): raise ValueError("error in is_ranked / is_graded") if P.is_meet_semilattice() != P_dual.is_join_semilattice(): raise ValueError("error in meet/join semilattice") if set(P.minimal_elements()) != set(P_dual.maximal_elements()): raise ValueError("error in min/max elements") if P.top() != P_dual.bottom(): raise ValueError("error in top/bottom element") parts = P.connected_components() P_ = Poset() for part in parts: P_ = P_.disjoint_union(part) if not P.is_isomorphic(P_): raise ValueError("error in connected components / disjoint union") parts = P.ordinal_summands() P_ = Poset() for part in parts: P_ = P_.ordinal_sum(part) if not P.is_isomorphic(P_): raise ValueError("error in ordinal summands / ordinal sum") P_ = P.with_bounds().without_bounds() if not P.is_isomorphic(P_): raise ValueError("error in with bounds / without bounds") P_ = P.completion_by_cuts().irreducibles_poset() if not P.has_isomorphic_subposet(P_): raise ValueError("error in completion by cuts / irreducibles poset") P_ = P.subposet(Subsets(P).random_element()) if not P_.is_induced_subposet(P): raise ValueError("error in subposet / is induced subposet") if not P.is_linear_extension(P.random_linear_extension()): raise ValueError("error in is linear extension") x = list(P) shuffle(x) if not P.is_linear_extension(P.sorted(x)): raise ValueError("error in sorted") dil = P.dilworth_decomposition() chain = dil[randint(0, len(dil) - 1)] if not P.is_chain_of_poset(chain): raise ValueError("error in Dilworth decomposition") lev = P.level_sets() level = lev[randint(0, len(lev) - 1)] if not P.is_antichain_of_poset(level): raise ValueError("error in level sets") # certificate=True must return a pair bool_with_cert = [ 'eulerian', 'greedy', 'join_semilattice', 'jump_critical', 'meet_semilattice', 'slender' ] for p in bool_with_cert: try: # some properties are not always defined for all posets res1 = attrcall('is_' + p)(P) except ValueError: continue res2 = attrcall('is_' + p, certificate=True)(P) if type(res2) != type((1, 2)) or len(res2) != 2: raise ValueError( "certificate-option does not return a pair in %s" % p) if res1 != res2[0]: raise ValueError("certificate-option changes result in %s" % p)
def _auxiliary_random_word(n): r""" Return a random word used to generate random triangulations. INPUT: n -- an integer OUTPUT: A binary sequence `w` of length `4n-2` with `n-1` ones, such that any proper prefix `u` of `w` satisfies `3|u|_1 - |u|_0 > -2` (where `|u|_1` and `|u|_0` are respectively the number of 1s and 0s in `u`). Those words are the expected input of :func:`_contour_and_graph_from_word`. ALGORITHM: A random word with these numbers of `0` and `1` is chosen. This word is then rotated in order to give an admissible code for a tree (as explained in Proposition 4.2, [PS2006]_). There are exactly two such rotations, one of which is chosen at random. Let us consider a word `w` satisfying the expected conditions. By drawing a step (1,3) for each 1 and a step (1,-1) for each 0 in `w`, one gets a path starting at height 0, ending at height -2 and staying above (or on) the horizontal line of height -1 except at the end point. By cutting the word at the first position of height -1, let us write `w=uv`. One can then see that `v` can only touch the line of height -1 at its initial point and just before its end point (these two points may be the same). Now consider a word `w'` obtained from `w` by any rotation. Because `vu` is another word satisfying the expected conditions, one can assume that `w'` is obtained from `w` by starting at some point in `u`. The algorithm must then recognize the end of `u` and the end of `v` inside `w'`. The end of `v` is the unique point of minimal height `h`. The end of `u` is the first point reaching the height `h+1`. EXAMPLES:: sage: from sage.graphs.generators.random import _auxiliary_random_word sage: _auxiliary_random_word(4) # random [1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0] sage: def check(w): ....: steps = {1: 3, 0: -1} ....: return all(sum(steps[u] for u in w[:i]) >= -1 for i in range(len(w))) sage: for n in range(1, 10): ....: w = _auxiliary_random_word(n) ....: assert len(w) == 4 * n - 2 ....: assert w.count(0) == 3 * n - 1 ....: assert check(w) """ from sage.misc.prandom import shuffle w = [0] * (3 * n - 1) + [1] * (n - 1) shuffle(w) # Finding the two admissible shifts. # The 'if height' is true at least once. # If it is true just once, then the word is admissible # and cuts = [0, first position of -1] (ok) # Otherwise, cuts will always contain # [first position of hmin, first position of hmin - 1] (ok) cuts = [0, 0] height = 0 height_min = 0 for i in range(4 * n - 3): if w[i] == 1: height += 3 else: height -= 1 if height < height_min: height_min = height cuts = cuts[1], i + 1 # random choice of one of the two possible cuts idx = cuts[randint(0, 1)] return w[idx:] + w[:idx]