Beispiel #1
0
def test_Word():
    with raises(TypeError):
        Word(0, Ty('n'))
    with raises(TypeError):
        Word('Alice', 1)
    with raises(TypeError):
        Word('Alice', Ty('n'), dom=1)
Beispiel #2
0
 def tensor(self, *others):
     if len(others) != 1:
         return monoidal.Diagram.tensor(self, *others)
     other, = others
     f = rigid.Box('f', Ty('c00', 'q00', 'q00'), Ty('c10', 'q10', 'q10'))
     g = rigid.Box('g', Ty('c01', 'q01', 'q01'), Ty('c11', 'q11', 'q11'))
     above = Diagram.id(f.dom[:1] @ g.dom[:1] @ f.dom[1:2])\
         @ Diagram.swap(g.dom[1:2], f.dom[2:]) @ Diagram.id(g.dom[2:])\
         >> Diagram.id(f.dom[:1]) @ Diagram.swap(g.dom[:1], f.dom[1:])\
         @ Diagram.id(g.dom[1:])
     below =\
         Diagram.id(f.cod[:1]) @ Diagram.swap(f.cod[1:], g.cod[:1])\
         @ Diagram.id(g.cod[1:])\
         >> Diagram.id(f.cod[:1] @ g.cod[:1] @ f.cod[1:2])\
         @ Diagram.swap(f.cod[2:], g.cod[1:2]) @ Diagram.id(g.cod[2:])
     diagram2tensor = tensor.Functor(ob={
         Ty("{}{}{}".format(a, b, c)):
         z.__getattribute__(y).__getattribute__(x)
         for a, x in zip(['c', 'q'], ['classical', 'quantum'])
         for b, y in zip([0, 1], ['dom', 'cod'])
         for c, z in zip([0, 1], [self, other])
     },
                                     ar={
                                         f: self.utensor.array,
                                         g: other.utensor.array
                                     })
     return CQMap(self.dom @ other.dom,
                  self.cod @ other.cod,
                  utensor=diagram2tensor(above >> f @ g >> below))
Beispiel #3
0
def test_Functor_call():
    x, y = Ty('x'), Ty('y')
    f, g = rigid.Box('f', x @ x, y), rigid.Box('g', y, Ty())
    ob = {x: 2, y: 3}
    ar = {f: list(range(2 * 2 * 3)), g: list(range(3))}
    F = Functor(ob, ar)
    assert list(F(f >> g).array.flatten()) == [5.0, 14.0, 23.0, 32.0]
    assert list(F(g.transpose(left=True)).array.flatten()) == [0.0, 1.0, 2.0]
    with raises(TypeError):
        F("Alice")
    assert Functor(ob={x: Ty(2, 3)}, ar=None)(x) == Dim(2, 3)
Beispiel #4
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
Beispiel #5
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
Beispiel #6
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'))
Beispiel #7
0
def test_Tensor_tensor():
    assert Tensor.tensor(Tensor.id(Dim(2))) == Tensor.id(Dim(2))

    assert Tensor.id(Dim(2)) @ Tensor.id(Dim(3)) == Tensor.id(Dim(2, 3))

    v = Tensor(Dim(1), Dim(2), [1, 0])
    assert v @ v == Tensor(dom=Dim(1), cod=Dim(2, 2), array=[1, 0, 0, 0])
    assert v @ v.dagger() == v << v.dagger()

    x, y = Ty('x'), Ty('y')
    f, g = rigid.Box('f', x, x), rigid.Box('g', y, y)
    ob, ar = {x: 2, y: 3}, {f: [1, 0, 0, 1], g: list(range(9))}
    F = Functor(ob, ar)
    assert F(f) @ F(g) == F(f @ g)
Beispiel #8
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)
Beispiel #9
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)
Beispiel #10
0
 def __init__(self, name, cod, dom=Ty(), data=None, _dagger=False):
     if not isinstance(name, str):
         raise TypeError(messages.type_err(str, name))
     if not isinstance(dom, Ty):
         raise TypeError(messages.type_err(Ty, dom))
     if not isinstance(cod, Ty):
         raise TypeError(messages.type_err(Ty, cod))
     super().__init__(name, dom, cod, data, _dagger)
Beispiel #11
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
Beispiel #12
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)
Beispiel #13
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))
Beispiel #14
0
def eager_parse(*words, target=Ty('s')):
    """
    Tries to parse a given list of words in an eager fashion.
    """
    result = Id(Ty()).tensor(*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
Beispiel #15
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()
Beispiel #16
0
def brute_force(*vocab, target=Ty('s')):
    """
    Given a vocabulary, search for grammatical sentences.
    """
    test = [()]
    for words in test:
        for word in vocab:
            try:
                yield eager_parse(*(words + (word, )), target=target)
            except NotImplementedError:
                pass
            test.append(words + (word, ))
Beispiel #17
0
 def __call__(self, diagram):
     """
     >>> x = Ty('x')
     >>> F = CircuitFunctor({x: 1}, {})
     >>> assert isinstance(F(Diagram.id(x)), Circuit)
     """
     if isinstance(diagram, Ty):
         return PRO(len(super().__call__(diagram)))
     if isinstance(diagram, Ob) and not diagram.z:
         return PRO(self.ob[Ty(diagram.name)])
     if isinstance(diagram, Diagram):
         return Circuit._upgrade(super().__call__(diagram))
     return super().__call__(diagram)
Beispiel #18
0
    def __call__(self, diagram):
        if isinstance(diagram, Ty):
            return sum(map(self, diagram.objects), Dim(1))
        if isinstance(diagram, Ob) and not diagram.z:
            result = self.ob[Ty(diagram.name)]
            return result if isinstance(result, Dim) else Dim(result)
        if isinstance(diagram, Ob):
            return super().__call__(diagram)
        if isinstance(diagram, Cup):
            return Tensor.cups(self(diagram.dom[0]), self(diagram.dom[1]))
        if isinstance(diagram, Cap):
            return Tensor.caps(self(diagram.cod[0]), self(diagram.cod[1]))
        if isinstance(diagram, Box) and not isinstance(diagram, Swap):
            if diagram.is_dagger:
                return self(diagram.dagger()).dagger()
            return Tensor(self(diagram.dom), self(diagram.cod),
                          self.ar[diagram])
        if not isinstance(diagram, Diagram):
            raise TypeError(messages.type_err(Diagram, diagram))

        def dim(scan):
            return len(self(scan))
        scan, array = diagram.dom, Id(self(diagram.dom)).array
        for box, off in zip(diagram.boxes, diagram.offsets):
            if isinstance(box, Swap):
                source = range(
                    dim(diagram.dom @ scan[:off]),
                    dim(diagram.dom @ scan[:off] @ box.dom))
                target = [
                    i + dim(box.right)
                    if i < dim(diagram.dom @ scan[:off]) + dim(box.left)
                    else i - dim(box.left) for i in source]
                array = np.moveaxis(array, list(source), list(target))
                scan = scan[:off] + box.cod + scan[off + len(box.dom):]
                continue
            left = dim(scan[:off])
            if array.shape and self(box).array.shape:
                source = list(range(dim(diagram.dom) + left,
                                    dim(diagram.dom) + left + dim(box.dom)))
                target = list(range(dim(box.dom)))
                array = np.tensordot(array, self(box).array, (source, target))
            else:
                array = array * self(box).array
            source = range(len(array.shape) - dim(box.cod), len(array.shape))
            target = range(dim(diagram.dom) + left,
                           dim(diagram.dom) + left + dim(box.cod))
            array = np.moveaxis(array, list(source), list(target))
            scan = scan[:off] + box.cod + scan[off + len(box.dom):]
        return Tensor(self(diagram.dom), self(diagram.cod), array)
Beispiel #19
0
 def tensor(self, *others):
     if len(others) != 1:
         return monoidal.Diagram.tensor(self, *others)
     f = rigid.Box('f', Ty('c00', 'q00', 'q00'), Ty('c10', 'q10', 'q10'))
     g = rigid.Box('g', Ty('c01', 'q01', 'q01'), Ty('c11', 'q11', 'q11'))
     ob = {Ty("{}{}{}".format(a, b, c)):
           z.__getattribute__(y).__getattribute__(x)
           for a, x in zip(['c', 'q'], ['classical', 'quantum'])
           for b, y in zip([0, 1], ['dom', 'cod'])
           for c, z in zip([0, 1], [self, others[0]])}
     ar = {f: self.array, g: others[0].array}
     permute_above = Diagram.id(f.dom[:1] @ g.dom[:1] @ f.dom[1:2])\
         @ Diagram.swap(g.dom[1:2], f.dom[2:]) @ Diagram.id(g.dom[2:])\
         >> Diagram.id(f.dom[:1]) @ Diagram.swap(g.dom[:1], f.dom[1:])\
         @ Diagram.id(g.dom[1:])
     permute_below =\
         Diagram.id(f.cod[:1]) @ Diagram.swap(f.cod[1:], g.cod[:1])\
         @ Diagram.id(g.cod[1:])\
         >> Diagram.id(f.cod[:1] @ g.cod[:1] @ f.cod[1:2])\
         @ Diagram.swap(f.cod[2:], g.cod[1:2]) @ Diagram.id(g.cod[2:])
     F = TensorFunctor(ob, ar)
     array = F(permute_above >> f @ g >> permute_below).array
     dom, cod = self.dom @ others[0].dom, self.cod @ others[0].cod
     return CQMap(dom, cod, array)
Beispiel #20
0
    def eval(self):
        """
        Evaluates the circuit as a discopy Tensor.

        Returns
        -------
        tensor : :class:`discopy.tensor.Tensor`
            with complex amplitudes as entries.

        Examples
        --------
        >>> state, isometry = Ket(1, 1), Id(1) @ Bra(0)
        >>> assert state.eval() >> isometry.eval()\\
        ...     == (state >> isometry).eval()
        """
        return TensorFunctor({Ty(1): 2}, Quiver(lambda g: g.array))(self)
Beispiel #21
0
    def __call__(self, diagram):
        if isinstance(diagram, Ty):
            return sum(map(self, diagram.objects), Dim(1))
        if isinstance(diagram, Ob):
            return Dim(self.ob[Ty(Ob(diagram.name, z=0))])
        if isinstance(diagram, Cup):
            return Tensor.cups(self(diagram.dom[0]), self(diagram.dom[1]))
        if isinstance(diagram, Cap):
            return Tensor.caps(self(diagram.cod[0]), self(diagram.cod[1]))
        if isinstance(diagram, Box):
            if diagram.is_dagger:
                return self(diagram.dagger()).dagger()
            return Tensor(self(diagram.dom), self(diagram.cod),
                          self.ar[diagram])
        if not isinstance(diagram, Diagram):
            raise TypeError(messages.type_err(Diagram, diagram))

        def dim(scan):
            return len(self(scan))

        scan, array = diagram.dom, Id(self(diagram.dom)).array
        for box, off in zip(diagram.boxes, diagram.offsets):
            left = dim(scan[:off])
            if array.shape and self(box).array.shape:
                source = list(
                    range(
                        dim(diagram.dom) + left,
                        dim(diagram.dom) + left + dim(box.dom)))
                target = list(range(dim(box.dom)))
                array = np.tensordot(array, self(box).array, (source, target))
            else:
                array = array * self(box).array
            source = range(len(array.shape) - dim(box.cod), len(array.shape))
            target = range(
                dim(diagram.dom) + left,
                dim(diagram.dom) + left + dim(box.cod))
            array = np.moveaxis(array, list(source), list(target))
            scan = scan[:off] + box.cod + scan[off + len(box.dom):]
        return Tensor(self(diagram.dom), self(diagram.cod), array)
Beispiel #22
0
 def __init__(self, word, ty):
     if not isinstance(word, str):
         raise TypeError(messages.type_err(str, word))
     if not isinstance(ty, Ty):
         raise TypeError(messages.type_err(Ty, ty))
     super().__init__(word, Ty(), ty)
Beispiel #23
0
def test_tree2diagram():
    tree = {
        'type':
        'ba',
        'cat':
        'S[dcl]',
        'children': [{
            'word': 'that',
            'lemma': 'XX',
            'pos': 'XX',
            'entity': 'XX',
            'chunk': 'XX',
            'cat': 'NP'
        }, {
            'type':
            'fa',
            'cat':
            'S[dcl]\\NP',
            'children': [{
                'type':
                'bx',
                'cat':
                '(S[dcl]\\NP)/NP',
                'children': [{
                    'word': "'s",
                    'lemma': 'XX',
                    'pos': 'XX',
                    'entity': 'XX',
                    'chunk': 'XX',
                    'cat': '(S[dcl]\\NP)/NP'
                }, {
                    'word': 'exactly',
                    'lemma': 'XX',
                    'pos': 'XX',
                    'entity': 'XX',
                    'chunk': 'XX',
                    'cat': '(S\\NP)\\(S\\NP)'
                }]
            }, {
                'type':
                'fa',
                'cat':
                'NP',
                'children': [{
                    'word': 'what',
                    'lemma': 'XX',
                    'pos': 'XX',
                    'entity': 'XX',
                    'chunk': 'XX',
                    'cat': 'NP/(S[dcl]/NP)'
                }, {
                    'type':
                    'fc',
                    'cat':
                    'S[dcl]/NP',
                    'children': [{
                        'type':
                        'tr',
                        'cat':
                        'S[X]/(S[X]\\NP)',
                        'children': [{
                            'word': 'i',
                            'lemma': 'XX',
                            'pos': 'XX',
                            'entity': 'XX',
                            'chunk': 'XX',
                            'cat': 'NP'
                        }]
                    }, {
                        'type':
                        'bx',
                        'cat':
                        '(S[dcl]\\NP)/NP',
                        'children': [{
                            'word': 'showed',
                            'lemma': 'XX',
                            'pos': 'XX',
                            'entity': 'XX',
                            'chunk': 'XX',
                            'cat': '(S[dcl]\\NP)/NP'
                        }, {
                            'type':
                            'fa',
                            'cat':
                            '(S\\NP)\\(S\\NP)',
                            'children': [{
                                'word': 'to',
                                'lemma': 'XX',
                                'pos': 'XX',
                                'entity': 'XX',
                                'chunk': 'XX',
                                'cat': '((S\\NP)\\(S\\NP))/NP'
                            }, {
                                'word': 'her',
                                'lemma': 'XX',
                                'pos': 'XX',
                                'entity': 'XX',
                                'chunk': 'XX',
                                'cat': 'NP'
                            }]
                        }]
                    }]
                }]
            }]
        }]
    }
    diagram = tree2diagram(tree)
    from discopy.biclosed import (Ty, Over, Under, Box, FA, BA, Functor,
                                  biclosed2rigid)
    from discopy.grammar.ccg import Word
    assert diagram.boxes == [
        Word('that', Ty('NP')),
        Word("'s", Over(Under(Ty('NP'), Ty('S')), Ty('NP'))),
        Word('exactly',
             Under(Under(Ty('NP'), Ty('S')), Under(Ty('NP'), Ty('S')))),
        Box(
            'bx',
            Ty(Over(Under(Ty('NP'), Ty('S')), Ty('NP')),
               Under(Under(Ty('NP'), Ty('S')), Under(Ty('NP'), Ty('S')))),
            Over(Under(Ty('NP'), Ty('S')), Ty('NP'))),
        Word('what', Over(Ty('NP'), Over(Ty('S'), Ty('NP')))),
        Word('i', Ty('NP')),
        Box('tr', Ty('NP'), Over(Ty('S'), Under(Ty('NP'), Ty('S')))),
        Word('showed', Over(Under(Ty('NP'), Ty('S')), Ty('NP'))),
        Word(
            'to',
            Over(Under(Under(Ty('NP'), Ty('S')), Under(Ty('NP'), Ty('S'))),
                 Ty('NP'))),
        Word('her', Ty('NP')),
        FA(
            Over(Under(Under(Ty('NP'), Ty('S')), Under(Ty('NP'), Ty('S'))),
                 Ty('NP'))),
        Box(
            'bx',
            Ty(Over(Under(Ty('NP'), Ty('S')), Ty('NP')),
               Under(Under(Ty('NP'), Ty('S')), Under(Ty('NP'), Ty('S')))),
            Over(Under(Ty('NP'), Ty('S')), Ty('NP'))),
        Box(
            'FC((S << (NP >> S)), ((NP >> S) << NP))',
            Ty(Over(Ty('S'), Under(Ty('NP'), Ty('S'))),
               Over(Under(Ty('NP'), Ty('S')), Ty('NP'))),
            Over(Ty('S'), Ty('NP'))),
        FA(Over(Ty('NP'), Over(Ty('S'), Ty('NP')))),
        FA(Over(Under(Ty('NP'), Ty('S')), Ty('NP'))),
        BA(Under(Ty('NP'), Ty('S')))
    ]
    assert diagram.offsets == [0, 1, 2, 1, 2, 3, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0]
    from discopy.rigid import Ob, Ty, Box, Cup
    biclosed2rigid(diagram).boxes == [
        Box('that', Ty(), Ty('NP')),
        Box("'s", Ty(), Ty(Ob('NP', z=1), 'S', Ob('NP', z=-1))),
        Box('exactly', Ty(), Ty(Ob('S', z=1), Ob('NP', z=2), Ob('NP', z=1),
                                'S')),
        Box(
            'bx',
            Ty(Ob('NP', z=1), 'S', Ob('NP', z=-1), Ob('S', z=1), Ob('NP', z=2),
               Ob('NP', z=1), 'S'), Ty(Ob('NP', z=1), 'S', Ob('NP', z=-1))),
        Box('what', Ty(), Ty('NP', Ob('NP', z=-2), Ob('S', z=-1))),
        Box('i', Ty(), Ty('NP')),
        Box('tr', Ty('NP'), Ty('S', Ob('S', z=-1), 'NP')),
        Box('showed', Ty(), Ty(Ob('NP', z=1), 'S', Ob('NP', z=-1))),
        Box(
            'to', Ty(),
            Ty(Ob('S', z=1), Ob('NP', z=2), Ob('NP', z=1), 'S', Ob('NP',
                                                                   z=-1))),
        Box('her', Ty(), Ty('NP')),
        Cup(Ty(Ob('NP', z=-1)), Ty('NP')),
        Box(
            'bx',
            Ty(Ob('NP', z=1), 'S', Ob('NP', z=-1), Ob('S', z=1), Ob('NP', z=2),
               Ob('NP', z=1), 'S'), Ty(Ob('NP', z=1), 'S', Ob('NP', z=-1))),
        Cup(Ty('NP'), Ty(Ob('NP', z=1))),
        Cup(Ty(Ob('S', z=-1)), Ty('S')),
        Cup(Ty(Ob('S', z=-1)), Ty('S')),
        Cup(Ty(Ob('NP', z=-2)), Ty(Ob('NP', z=-1))),
        Cup(Ty(Ob('NP', z=-1)), Ty('NP')),
        Cup(Ty('NP'), Ty(Ob('NP', z=1)))
    ]
    biclosed2rigid(diagram).offsets ==\
        [0, 1, 4, 1, 4, 7, 7, 10, 13, 18, 17, 10, 9, 8, 6, 5, 3, 0]
Beispiel #24
0
def test_Functor():
    assert repr(Functor({Ty('x'): 1}, {})) ==\
        "tensor.Functor(ob={Ty('x'): 1}, ar={})"
Beispiel #25
0
def test_Functor_swap():
    x, y = Ty('x'), Ty('y')
    f, g = rigid.Box('f', x, x), rigid.Box('g', y, y)
    F = Functor({x: 2, y: 3}, {f: [1, 2, 3, 4], g: list(range(9))})
    assert F(f @ g >> rigid.Diagram.swap(x, y)) == \
           F(rigid.Diagram.swap(x, y) >> g @ f)
Beispiel #26
0
 def __call__(self, diagram):
     if isinstance(diagram, Ob) and not isinstance(diagram, Ty):
         result = self.ob[Ty(diagram.name)]
         return qubit ** result if isinstance(result, int) else result
     return super().__call__(diagram)
Beispiel #27
0
def test_Functor_sum():
    x, y = Ty('x'), Ty('y')
    f = rigid.Box('f', x, y)
    F = Functor({x: 1, y: 2}, {f: [1, 0]})
    assert F(f + f) == F(f) + F(f)
Beispiel #28
0
    def eval(self, backend=None, mixed=False, **params):
        """
        Parameters
        ----------
        backend : pytket.Backend, optional
            Backend on which to run the circuit, if none then we apply
            :class:`TensorFunctor` or :class:`CQMapFunctor` instead.
        mixed : bool, optional
            Whether to apply :class:`TensorFunctor` or :class:`CQMapFunctor`.
        params : kwargs, optional
            Get passed to Circuit.get_counts.

        Returns
        -------
        tensor : :class:`discopy.tensor.Tensor`
            If :code:`backend is not None` or :code:`mixed=False`.
        cqmap : :class:`CQMap`
            Otherwise.

        Examples
        --------
        We can evaluate a pure circuit (i.e. with :code:`not circuit.is_mixed`)
        as a unitary :class:`discopy.tensor.Tensor` or as a :class:`CQMap`:

        >>> H.eval().round(2)
        Tensor(dom=Dim(2), cod=Dim(2), array=[0.71, 0.71, 0.71, -0.71])
        >>> H.eval(mixed=True).round(1)  # doctest: +ELLIPSIS
        CQMap(dom=Q(Dim(2)), cod=Q(Dim(2)), array=[0.5, ..., 0.5])

        We can evaluate a mixed circuit as a :class:`CQMap`:

        >>> Measure().eval()
        CQMap(dom=Q(Dim(2)), cod=C(Dim(2)), array=[1, 0, 0, 0, 0, 0, 0, 1])
        >>> circuit = Bits(1, 0) @ Ket(0) >> Discard(bit ** 2 @ qubit)
        >>> assert circuit.eval() == CQMap(dom=CQ(), cod=CQ(), array=[1.0])

        We can execute any circuit on a `pytket.Backend`:

        >>> circuit = Ket(0, 0) >> sqrt(2) @ H @ X >> CX >> Measure() @ Bra(0)
        >>> from unittest.mock import Mock
        >>> backend = Mock()
        >>> backend.get_counts.return_value = {(0, 1): 512, (1, 0): 512}
        >>> assert circuit.eval(backend, n_shots=2**10).round()\\
        ...     == Tensor(dom=Dim(1), cod=Dim(2), array=[0., 1.])
        """
        if backend is None and (mixed or self.is_mixed):
            ob = {Ty('bit'): C(Dim(2)), Ty('qubit'): Q(Dim(2))}
            F_ob = CQMapFunctor(ob, {})
            def ar(box):
                if isinstance(box, Swap):
                    return CQMap.swap(F_ob(box.dom[:1]), F_ob(box.dom[1:]))
                if isinstance(box, Discard):
                    return CQMap.discard(F_ob(box.dom))
                if isinstance(box, Measure):
                    measure = CQMap.measure(
                        F_ob(box.dom).quantum, destructive=box.destructive)
                    measure = measure @ CQMap.discard(F_ob(box.dom).classical)\
                        if box.override_bits else measure
                    return measure
                if isinstance(box, (MixedState, Encode)):
                    return ar(box.dagger()).dagger()
                if not box.is_mixed and box.classical:
                    return CQMap(F_ob(box.dom), F_ob(box.cod), box.array)
                if not box.is_mixed:
                    dom, cod = F_ob(box.dom).quantum, F_ob(box.cod).quantum
                    return CQMap.pure(Tensor(dom, cod, box.array))
                raise TypeError(messages.type_err(QuantumGate, box))
            return CQMapFunctor(ob, ar)(self)
        if backend is None:
            return TensorFunctor(lambda x: 2, lambda f: f.array)(self)
        counts = self.get_counts(backend, **params)
        n_bits = len(list(counts.keys()).pop())
        array = np.zeros(n_bits * (2, ) or (1, ))
        for bitstring, count in counts.items():
            array += count * Ket(*bitstring).array
        return Tensor(Dim(1), Dim(*(n_bits * (2, ))), array)
Beispiel #29
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())