Пример #1
0
def test_normal_form():
    n = Ty('n')
    w1, w2 = Word('a', n), Word('b', n)
    diagram = w1 @ w2 >>\
        Id(n) @ Cap(n, n.r) @ Id(n) >> Id(n @ n) @ Cup(n.r, n)
    expected_result = w1 @ w2
    assert expected_result == normal_form(diagram)

    with raises(ValueError) as err:
        normal_form(w2 >> w1 @ Id(n))
Пример #2
0
 def generate(self,
              start,
              max_sentences,
              max_depth,
              max_iter=100,
              remove_duplicates=False,
              not_twice=[]):
     """
     Generate sentences from a context-free grammar.
     Assumes the only terminal symbol is Ty().
     Parameters
     ----------
     start : type
         root of the generated trees.
     max_sentences : int
         maximum number of sentences to generate.
     max_depth : int
         maximum depth of the trees.
     max_iter : int
         maximum number of iterations, set to 100 by default.
     remove_duplicates : bool
         if set to True only distinct syntax trees will be generated.
     not_twice : list
         list of productions that you don't want appearing twice
         in a sentence, set to the empty list by default
     """
     prods, cache = list(self.productions), set()
     n, i = 0, 0
     while (not max_sentences or n < max_sentences) and i < max_iter:
         i += 1
         sentence = Id(start)
         depth = 0
         while depth < max_depth:
             recall = depth
             if sentence.dom == Ty():
                 if remove_duplicates and sentence in cache:
                     break
                 yield sentence
                 if remove_duplicates:
                     cache.add(sentence)
                 n += 1
                 break
             tag = sentence.dom[0]
             random.shuffle(prods)
             for prod in prods:
                 if prod in not_twice and prod in sentence.boxes:
                     continue
                 if Ty(tag) == prod.cod:
                     sentence = sentence << prod @ Id(sentence.dom[1:])
                     depth += 1
                     break
             if recall == depth:  # in this case, no production was found
                 break
Пример #3
0
def normal_form(diagram, normalizer=None, **params):
    """
    Applies normal form to a pregroup diagram of the form
    `word @ ... @ word >> cups` by normalising the words and the sentences
    seperately before combining them, so it can be drawn using `grammar.draw`.
    """
    words, is_pregroup = Id(Ty()), True
    for _, box, right in diagram.layers:
        if isinstance(box, Word):
            if right:  # word boxes should be tensored left to right.
                is_pregroup = False
                break
            words = words @ box
        else:
            break

    wires = diagram[len(words):]
    is_pregroup = is_pregroup and all(
        isinstance(box, Cup) or isinstance(box, Swap) or isinstance(box, Cap)
        for box in wires.boxes)
    if not is_pregroup:
        raise ValueError(messages.expected_pregroup())

    norm = lambda d: monoidal.Diagram.normal_form(
        d, normalizer=normalizer or Diagram.normalize, **params)

    return norm(words) >> norm(wires)
Пример #4
0
    def generate(self,
                 start,
                 max_sentences,
                 max_depth,
                 max_iter=100,
                 remove_duplicates=False):
        """
        Generate sentences from a context-free grammar.
        Assumes the only terminal symbol is Ty().

        Parameters
        ----------
        start : type
            root of the generated trees.
        max_sentences : int
            maximum number of sentences to generate.
        max_depth : int
            maximum depth of the trees.
        max_iter : int
            maximum number of iterations, set to 100 by default.
        remove_duplicates : bool
            if set to True only distinct syntax trees will be generated.
        """
        prods, cache = list(self.productions), set()
        n, iter = 0, 0
        while n < max_sentences and (iter < max_iter):
            iter += 1
            sentence = Id(start)
            depth = 0
            while depth < max_depth:
                if sentence.dom == Ty():
                    if remove_duplicates and sentence in cache:
                        break
                    yield sentence
                    if remove_duplicates:
                        cache.add(sentence)
                    n += 1
                    break
                tag = sentence.dom[0]
                random.shuffle(prods)
                for prod in prods:
                    if Ty(tag) == prod.cod:
                        sentence = sentence << prod @ Id(sentence.dom[1:])
                        depth += 1
                        break
Пример #5
0
def draw(diagram, **params):
    """
    Draws a pregroup diagram, i.e. of shape :code:`word @ ... @ word >> cups`.

    Parameters
    ----------
    width : float, optional
        Width of the word triangles, default is :code:`2.0`.
    space : float, optional
        Space between word triangles, default is :code:`0.5`.
    textpad : pair of floats, optional
        Padding between text and wires, default is :code:`(0.1, 0.2)`.
    draw_type_labels : bool, optional
        Whether to draw type labels, default is :code:`True`.
    aspect : string, optional
        Aspect ratio, one of :code:`['equal', 'auto']`.
    margins : tuple, optional
        Margins, default is :code:`(0.05, 0.05)`.
    fontsize : int, optional
        Font size for the words, default is :code:`12`.
    fontsize_types : int, optional
        Font size for the types, default is :code:`12`.
    figsize : tuple, optional
        Figure size.
    path : str, optional
        Where to save the image, if :code:`None` we call :code:`plt.show()`.
    pretty_types : bool, optional
        Whether to draw type labels with superscript, default is :code:`False`.
    triangles : bool, optional
        Whether to draw words as triangular states, default is :code:`False`.

    Raises
    ------
    ValueError
        Whenever the input is not a pregroup diagram.
    """
    from discopy.rigid import Swap
    if not isinstance(diagram, Diagram):
        raise TypeError(messages.type_err(Diagram, diagram))
    words, is_pregroup = Id(Ty()), True
    for _, box, right in diagram.layers:
        if isinstance(box, Word):
            if right:  # word boxes should be tensored left to right.
                is_pregroup = False
                break
            words = words @ box
        else:
            break
    cups = diagram[len(words):].foliation().boxes\
        if len(words) < len(diagram) else []
    is_pregroup = is_pregroup and words and all(
        isinstance(box, Cup) or isinstance(box, Swap) for s in cups
        for box in s.boxes)
    if not is_pregroup:
        raise ValueError(messages.expected_pregroup())
    drawing.pregroup_draw(words, cups, **params)
Пример #6
0
def eager_parse(*words, target=Ty('s')):
    """
    Tries to parse a given list of words in an eager fashion.
    """
    result = fold(lambda x, y: x @ y, words)
    scan = result.cod
    while True:
        fail = True
        for i in range(len(scan) - 1):
            if scan[i:i + 1].r != scan[i + 1:i + 2]:
                continue
            cup = Cup(scan[i:i + 1], scan[i + 1:i + 2])
            result = result >> Id(scan[:i]) @ cup @ Id(scan[i + 2:])
            scan, fail = result.cod, False
            break
        if result.cod == target:
            return result
        if fail:
            raise NotImplementedError
Пример #7
0
def test_pregroup_draw_errors():
    n = Ty('n')
    with raises(TypeError):
        draw(0)
    with raises(ValueError) as err:
        draw(Cap(n, n.l))
    with raises(ValueError) as err:
        draw(Cup(n, n.r))
    with raises(ValueError) as err:
        draw(Word('Alice', n) >> Word('Alice', n) @ Id(n))
    assert str(err.value) is messages.expected_pregroup()
Пример #8
0
def test_CFG():
    s, n, v, vp = Ty('S'), Ty('N'), Ty('V'), Ty('VP')
    R0, R1 = Box('R0', vp @ n, s), Box('R1', n @ v, vp)
    Jane, loves, Bob = Word('Jane', n), Word('loves', v), Word('Bob', n)
    cfg = CFG(R0, R1, Jane, loves, Bob)
    assert Jane in cfg.productions
    assert "CFG(Box('R0', Ty('VP', 'N'), Ty('S'))" in repr(cfg)
    assert not list(CFG().generate(start=s, max_sentences=1, max_depth=1))
    sentence, *_ = cfg.generate(
        start=s, max_sentences=1, max_depth=10, not_twice=[Jane, Bob], seed=42)
    assert sentence\
        == (Jane @ loves @ Bob).normal_form(left=True) >> R1 @ Id(n) >> R0
Пример #9
0
def test_brute_force():
    s, n = Ty('s'), Ty('n')
    Alice = Word('Alice', n)
    loves = Word('loves', n.r @ s @ n.l)
    Bob = Word('Bob', n)
    grammar = Cup(n, n.r) @ Id(s) @ Cup(n.l, n)
    gen = brute_force(Alice, loves, Bob)
    assert next(gen) == Alice @ loves @ Alice >> grammar
    assert next(gen) == Alice @ loves @ Bob >> grammar
    assert next(gen) == Bob @ loves @ Alice >> grammar
    assert next(gen) == Bob @ loves @ Bob >> grammar
    gen = brute_force(Alice, loves, Bob, target=n)
    assert next(gen) == Word('Alice', Ty('n'))
    assert next(gen) == Word('Bob', Ty('n'))
Пример #10
0
def test_eager_parse():
    s, n = Ty('s'), Ty('n')
    Alice = Word('Alice', n)
    loves = Word('loves', n.r @ s @ n.l)
    Bob = Word('Bob', n)
    grammar = Cup(n, n.r) @ Id(s) @ Cup(n.l, n)
    assert eager_parse(Alice, loves, Bob) == grammar << Alice @ loves @ Bob
    who = Word('who', n.r @ n @ s.l @ n)
    assert eager_parse(Bob, who, loves, Alice, target=n).offsets ==\
        [0, 1, 5, 8, 0, 2, 1, 1]
    with raises(NotImplementedError):
        eager_parse(Alice, Bob, loves)
    with raises(NotImplementedError):
        eager_parse(Alice, loves, Bob, who, loves, Alice)
Пример #11
0
def test_from_tree():
    s, n = Ty('s'), Ty('n')
    Alice, Bob = Word('Alice', n), Word('Bob', n)
    loves = Word('loves', n.r @ s @ n.l)
    sentence = Alice @ loves @ Bob >> Cup(n, n.r) @ Id(s) @ Cup(n.l, n)
    sentence == from_tree(sentence.to_tree())