Beispiel #1
0
    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)
Beispiel #2
0
    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)
Beispiel #3
0
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)
Beispiel #4
0
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)
Beispiel #5
0
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
Beispiel #6
0
    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
Beispiel #7
0
    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
Beispiel #8
0
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))
Beispiel #9
0
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))
Beispiel #10
0
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)
Beispiel #11
0
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]
Beispiel #12
0
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]