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)
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))
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)
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
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
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'))
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)
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)
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)
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)
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
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)
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))
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
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()
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, ))
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)
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)
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)
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)
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)
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)
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]
def test_Functor(): assert repr(Functor({Ty('x'): 1}, {})) ==\ "tensor.Functor(ob={Ty('x'): 1}, ar={})"
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)
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)
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)
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)
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())