コード例 #1
0
    def __call__(self, *values):
        """
        Call method implemented using PythonFunctors.

        >>> assert SWAP(1, 2) == (2, 1)
        >>> assert (COPY @ COPY >> Id(1) @ SWAP @ Id(1))(1, 2) == (1, 2, 1, 2)
        """
        ob = Quiver(lambda t: PRO(len(t)))
        ar = Quiver(lambda f: Function(len(f.dom), len(f.cod), f.function))
        return PythonFunctor(ob, ar)(self)(*values)
コード例 #2
0
    def flatten(self):
        """
        Takes a diagram of diagrams and returns a diagram.

        >>> x, y = Ty('x'), Ty('y')
        >>> f0, f1 = Box('f0', x, y), Box('f1', y, x)
        >>> g = Box('g', x @ y, y)
        >>> d = (Id(y) @ f0 @ Id(x) >> f0.dagger() @ Id(y) @ f0 >>\\
        ...      g @ f1 >> f1 @ Id(x)).normal_form()
        >>> assert d.foliation().flatten().normal_form() == d
        >>> assert d.foliation().dagger().flatten()\\
        ...     == d.foliation().flatten().dagger()
        """
        return Functor(Quiver(lambda x: x), Quiver(lambda f: f))(self)
コード例 #3
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)
コード例 #4
0
    def normalize(self, _dagger=False):
        """
        Multiplies all the scalars in the diagram.
        Moves the kets to the top of the diagram, adding swaps if necessary.
        Fuses them into preparation layers.
        Moves the bras to the bottom of the diagram,
        Fuses them into meaurement layers.

        >>> circuit = sqrt(2) @ Ket(1, 0) >> CX >> Id(1) @ Ket(0) @ Id(1)
        >>> for step in circuit.normalize():
        ...     print(', '.join(map(str, step.boxes)))
        Ket(1, 0), CX, Ket(0), 1.414
        Ket(1), Ket(0), CX, Ket(0), 1.414
        Ket(1), Ket(0), CX, Ket(0), 1.414
        Ket(0), Ket(1), Ket(0), CX, SWAP, 1.414
        Ket(0), Ket(1), Ket(0), 1.414, CX, SWAP
        Ket(0, 1), Ket(0), 1.414, CX, SWAP
        Ket(0, 1, 0), 1.414, CX, SWAP
        """
        def remove_scalars(diagram):
            for i, box in enumerate(diagram.boxes):
                if box.dom == box.cod == PRO():
                    return diagram[:i] >> diagram[i + 1:], box.array[0]
            return diagram, None

        def find_ket(diagram):
            boxes, offsets = diagram.boxes, diagram.offsets
            for i in range(len(diagram) - 1):
                if isinstance(boxes[i], Ket) and isinstance(boxes[i + 1], Ket)\
                        and offsets[i + 1] == offsets[i] + len(boxes[i].cod):
                    return i
            return None

        def fuse_kets(diagram, i):
            boxes, offsets = diagram.boxes, diagram.offsets
            ket = Ket(*(boxes[i].bitstring + boxes[i + 1].bitstring))
            return Circuit(len(diagram.dom), len(diagram.cod),
                           boxes[:i] + [ket] + boxes[i + 2:],
                           offsets[:i + 1] + offsets[i + 2:])

        def unfuse(ket):
            if not isinstance(ket, Ket):
                return ket
            result = Id(0)
            for bit in ket.bitstring:
                result = result @ Ket(bit)
            return result

        diagram = self
        # step 0: multiply scalars to the right of the diagram
        if not _dagger:
            scalar = 1
            while True:
                diagram, number = remove_scalars(diagram)
                if number is None:
                    break
                scalar = scalar * number
                yield diagram @ Gate('{0:.3f}'.format(scalar), 0, [scalar])
            diagram = diagram @ Gate('{0:.3f}'.format(scalar), 0, [scalar])

        # step 1: unfuse all kets
        before = diagram
        diagram = CircuitFunctor(ob=Quiver(len), ar=Quiver(unfuse))(diagram)
        if diagram != before:
            yield diagram

        # step 2: move kets to the bottom of the diagram by foliating
        ket_count = sum(
            [1 if isinstance(box, Ket) else 0 for box in diagram.boxes])
        gen = diagram.foliate()
        for _ in range(ket_count):
            diagram = next(gen)
            yield diagram

        # step 4: fuse kets
        while True:
            fusable = find_ket(diagram)
            if fusable is None:
                break
            diagram = fuse_kets(diagram, fusable)
            yield diagram

        # step 5: repeat for bras using dagger
        if not _dagger:
            for _diagram in diagram.dagger().normalize(_dagger=True):
                yield _diagram.dagger()
コード例 #5
0
ファイル: tk_interface.py プロジェクト: codeaudit/discopy
def to_tk(self):
    def remove_ket1(box):
        if not isinstance(box, Ket):
            return box
        x_gates = Circuit.id(0)
        for bit in box.bitstring:
            x_gates = x_gates @ (X if bit else Circuit.id(1))
        return Ket(*(len(box.bitstring) * (0, ))) >> x_gates

    def swap(tk_circ, i, j):
        old = Qubit('q', i)
        tmp = Qubit('tmp', 0)
        new = Qubit('q', j)
        tk_circ.rename_units({old: tmp})
        tk_circ.rename_units({new: old})
        tk_circ.rename_units({tmp: new})

    def prepare_qubit(tk_circ, left, box, right):
        if len(right) > 0:
            renaming = dict()
            for i in range(len(left), tk_circ.n_qubits):
                old = Qubit('q', i)
                new = Qubit('q', i + len(box.cod))
                renaming.update({old: new})
            tk_circ.rename_units(renaming)
        tk_circ.add_blank_wires(len(box.cod))

    def add_gate(tk_circ, box, off):
        qubits = [off + j for j in range(len(box.dom))]
        if isinstance(box, (Rx, Rz)):
            tk_circ.__getattribute__(box.name[:2])(2 * box.phase, *qubits)
        elif isinstance(box, CRz):
            tk_circ.__getattribute__(box.name[:3])(2 * box.phase, *qubits)
        else:
            tk_circ.__getattribute__(box.name)(*qubits)

    def measure_qubit(tk_circ, left, box, right):
        if len(right) > 0:
            renaming = dict()
            for i, _ in enumerate(box.dom):
                old = Qubit('q', len(left) + i)
                tmp = Qubit('tmp', i)
                renaming.update({old: tmp})
            for i, _ in enumerate(right):
                old = Qubit('q', len(left @ box.dom) + i)
                new = Qubit('q', len(left) + i)
                renaming.update({old: new})
            tk_circ.rename_units(renaming)
            renaming = dict()
            for j, _ in enumerate(box.dom):
                tmp = Qubit('tmp', j)
                new = Qubit('q', len(left @ right) + j)
                renaming.update({tmp: new})
            tk_circ.rename_units(renaming)
        return {
            len(left @ right) + j: box.bitstring[j]
            for j, _ in enumerate(box.dom)
        }

    circuit = CircuitFunctor(ob=Quiver(len), ar=Quiver(remove_ket1))(self)
    if circuit.dom != PRO(0):
        circuit = Ket(*(len(circuit.dom) * (0, ))) >> circuit
    tk_circ = TketCircuit()
    for left, box, right in circuit.layers:
        if isinstance(box, Ket):
            prepare_qubit(tk_circ, left, box, right)
        elif isinstance(box, Bra):
            tk_circ.post_selection.update(
                measure_qubit(tk_circ, left, box, right))
        elif box == SWAP:
            swap(tk_circ, len(left), len(left) + 1)
        elif box.dom == box.cod == PRO(0):
            tk_circ.scalar *= box.array[0]
        else:
            add_gate(tk_circ, box, len(left))
    return tk_circ