Exemple #1
0
    def spiders(n_legs_in, n_legs_out, dim):
        if len(dim) == 0:
            return Id()

        from discopy.quantum.gates import Bra, CX, H, Ket
        if n_legs_in == 0:
            d1 = Ket(0) >> H
        else:
            d1 = Id(qubit)
            for _ in range(n_legs_in - 1):
                d1 = d1 @ Id(qubit) >> CX >> Id(qubit) @ Bra(0)
        if n_legs_out == 0:
            d2 = H >> Bra(0)
        else:
            d2 = Id(qubit)
            for _ in range(n_legs_out - 1):
                d2 = Id(qubit) @ Ket(0) >> CX >> d2 @ Id(qubit)
        d = d1 >> d2

        i, j, k = n_legs_in, n_legs_out, len(dim)
        permutation = Circuit.permutation
        p1 = permutation([i * (x % k) + (x // k) for x in range(i * k)])
        p2 = permutation([k * (x % j) + (x // j) for x in range(j * k)])

        ds = p1 >> Circuit.tensor(*[d] * len(dim)) >> p2
        return ds
Exemple #2
0
    def measure(self, mixed=False):
        """
        Measures a circuit on the computational basis using :code:`numpy`.

        Parameters
        ----------
        mixed : bool, optional
            Whether to apply :class:`tensor.Functor` or :class:`CQMapFunctor`.

        Returns
        -------
        array : numpy.ndarray
        """
        from discopy.quantum.gates import Bra, Ket
        if mixed or self.is_mixed:
            return self.init_and_discard().eval(mixed=True).array.real
        state = (Ket(*(len(self.dom) * [0])) >> self).eval()
        effects = [
            Bra(*index2bitstring(j, len(self.cod))).eval()
            for j in range(2**len(self.cod))
        ]
        array = np.zeros(len(self.cod) * (2, ) or (1, ))
        for effect in effects:
            array += effect.array * np.absolute((state >> effect).array)**2
        return array
Exemple #3
0
 def init_and_discard(self):
     """ Returns a circuit with empty domain and only bits as codomain. """
     from discopy.quantum.gates import Bits, Ket
     circuit = self
     if circuit.dom:
         init = Id(0).tensor(*(
             Bits(0) if x.name == "bit" else Ket(0) for x in circuit.dom))
         circuit = init >> circuit
     if circuit.cod != bit ** len(circuit.cod):
         discards = Id(0).tensor(*(
             Discard() if x.name == "qubit"
             else Id(bit) for x in circuit.cod))
         circuit = circuit >> discards
     return circuit
Exemple #4
0
def from_tk(tk_circuit):
    """
    Translates from tket to discopy.
    """
    if not isinstance(tk_circuit, tk.Circuit):
        raise TypeError(messages.type_err(tk.Circuit, tk_circuit))
    if not isinstance(tk_circuit, Circuit):
        tk_circuit = Circuit.upgrade(tk_circuit)
    n_bits = tk_circuit.n_bits - len(tk_circuit.post_selection)
    n_qubits = tk_circuit.n_qubits

    def box_from_tk(tk_gate):
        name = tk_gate.op.type.name
        if name == 'Rx':
            return Rx(tk_gate.op.params[0] / 2)
        if name == 'Rz':
            return Rz(tk_gate.op.params[0] / 2)
        if name == 'CRz':
            return CRz(tk_gate.op.params[0] / 2)
        for gate in GATES:
            if name == gate.name:
                return gate
        raise NotImplementedError

    def make_units_adjacent(tk_gate):
        offset = tk_gate.qubits[0].index[0]
        swaps = Id(qubit**n_qubits @ bit**n_bits)
        for i, tk_qubit in enumerate(tk_gate.qubits[1:]):
            source, target = tk_qubit.index[0], offset + i + 1
            if source < target:
                left, right = swaps.cod[:source], swaps.cod[target:]
                swap = Id.swap(swaps.cod[source:source + 1],
                               swaps.cod[source + 1:target])
                if source <= offset:
                    offset -= 1
            elif source > target:
                left, right = swaps.cod[:target], swaps.cod[source + 1:]
                swap = Id.swap(swaps.cod[target:target + 1],
                               swaps.cod[target + 1:source + 1])
            else:  # pragma: no cover
                continue  # units are adjacent already
            swaps = swaps >> Id(left) @ swap @ Id(right)
        return offset, swaps

    circuit = Id(0).tensor(*(n_qubits * [Ket(0)] + n_bits * [Bits(0)]))
    bras = {}
    for tk_gate in tk_circuit.get_commands():
        if tk_gate.op.type.name == "Measure":
            offset = tk_gate.qubits[0].index[0]
            bit_index = tk_gate.bits[0].index[0]
            if bit_index in tk_circuit.post_selection:
                bras[offset] = tk_circuit.post_selection[bit_index]
                continue  # post selection happens at the end
            box = Measure(destructive=False, override_bits=True)
            swaps = Id(circuit.cod[:offset + 1])
            swaps = swaps @ Id.swap(
                circuit.cod[offset + 1:n_qubits + bit_index],
                circuit.cod[n_qubits:][bit_index: bit_index + 1])\
                @ Id(circuit.cod[n_qubits + bit_index + 1:])
        else:
            box = box_from_tk(tk_gate)
            offset, swaps = make_units_adjacent(tk_gate)
        left, right = swaps.cod[:offset], swaps.cod[offset + len(box.dom):]
        circuit = circuit >> swaps >> Id(left) @ box @ Id(right) >> swaps[::-1]
    circuit = circuit >> Id(0).tensor(*(Bra(bras[i]) if i in bras else Discard(
    ) if x.name == 'qubit' else Id(bit) for i, x in enumerate(circuit.cod)))
    if tk_circuit.scalar != 1:
        circuit = circuit @ MixedScalar(tk_circuit.scalar)
    return circuit >> tk_circuit.post_processing
Exemple #5
0
 def remove_ket1(box):
     if not isinstance(box, Ket):
         return box
     x_gates = Id(0).tensor(*(X if x else Id(1) for x in box.bitstring))
     return Ket(*(len(box.bitstring) * (0, ))) >> x_gates