Ejemplo n.º 1
0
    def test_all_wires_method(self):
        """Tests the ``all_wires()`` method."""

        wires1 = Wires([1, 2, 3])
        wires2 = Wires([1, 4, 5, 2])
        wires3 = Wires([6, 5])

        new_wires = Wires.all_wires([wires1, wires2, wires3])
        assert new_wires.wire_tuple == (1, 2, 3, 4, 5, 6)

        with pytest.raises(WireError, match="Expected a Wires object"):
            Wires.all_wires([[3, 4], [8, 5]])
Ejemplo n.º 2
0
    def obtain_binary_repr(self, n_qubits=None, wire_map=None):
        """Converts the list of Pauli words to a binary matrix.

        Keyword args:
            n_qubits (int): number of qubits to specify dimension of binary vector representation
            wire_map (dict): dictionary containing all wire labels used in the Pauli word as keys,
                and unique integer labels as their values

        Returns:
            array[bool]: a column matrix of the Pauli words in binary vector representation

        """

        if wire_map is None:
            self._wire_map = {
                wire: c
                for c, wire in enumerate(
                    Wires.all_wires([obs.wires
                                     for obs in self.observables]).tolist())
            }

        else:
            self._wire_map = wire_map

        self._n_qubits = n_qubits

        return convert_observables_to_binary_matrix(self.observables, n_qubits,
                                                    self._wire_map)
Ejemplo n.º 3
0
def convert_observables_to_binary_matrix(observables,
                                         n_qubits=None,
                                         wire_map=None):
    """Converts a list of Pauli words to the binary vector representation and yields a row matrix
        of the binary vectors.

    The dimension of the binary vectors will be implied from the highest wire being acted on
    non-trivially by the Pauli words in observables.

    **Usage example:**

    >>> convert_observables_to_binary_matrix([PauliX(0) @ PauliY(2), PauliZ(0) @ PauliZ(1) @ PauliZ(2)])
    array([[1., 1., 0., 0., 1., 0.],
           [0., 0., 0., 1., 1., 1.]])


    Args:
        observables (list[Union[Identity, PauliX, PauliY, PauliZ, Tensor]]): the list of Pauli
            words

    Keyword args:
        n_qubits (int): number of qubits to specify dimension of binary vector representation
        wire_map (dict): dictionary containing all wire labels used in the Pauli words as keys, and
            unique integer labels as their values


    Returns:
        array[array[bool]]: a matrix whose rows are Pauli words in binary vector representation

    """

    m_cols = len(observables)

    if wire_map is None:
        all_wires = Wires.all_wires(
            [pauli_word.wires for pauli_word in observables])
        wire_map = {i: c for c, i in enumerate(all_wires)}

    n_qubits_min = max(wire_map.values()) + 1
    if n_qubits is None:
        n_qubits = n_qubits_min
    elif n_qubits < n_qubits_min:
        raise ValueError(
            "n_qubits must support the highest mapped wire index {},"
            " instead got n_qubits={}.".format(n_qubits_min, n_qubits))

    binary_mat = np.zeros((m_cols, 2 * n_qubits))

    for i in range(m_cols):
        binary_mat[i, :] = pauli_to_binary(observables[i],
                                           n_qubits=n_qubits,
                                           wire_map=wire_map)

    return binary_mat
Ejemplo n.º 4
0
    def active_wires(operators):
        """Returns the wires acted on by a set of operators.

        Args:
            operators (list[~.Operation]): operators for which
                we are gathering the active wires

        Returns:
            Wires: wires activated by the specified operators
        """
        list_of_wires = [op.wires for op in operators]

        return Wires.all_wires(list_of_wires)
Ejemplo n.º 5
0
def _wire_map_from_pauli_pair(pauli_word_1, pauli_word_2):
    """Generate a wire map from the union of wires of two Paulis.

    Args:
        pauli_word_1 (.Operation): A Pauli word.
        pauli_word_2 (.Operation): A second Pauli word.

    Returns:
        dict[Union[str, int], int]): dictionary containing all wire labels used
        in the Pauli word as keys, and unique integer labels as their values.
    """
    wire_labels = Wires.all_wires([pauli_word_1.wires,
                                   pauli_word_2.wires]).labels
    wire_map = {label: i for i, label in enumerate(wire_labels)}
    return wire_map
Ejemplo n.º 6
0
    def _ev_exact(self, obs_nodes, obs_wires):
        r"""Expectation value of observables on specified wires using an exact representation.

        Args:
           obs_nodes (Sequence[tn.Node]): the observables as TensorNetwork Nodes
           obs_wires (Sequence[Wires]): measured wires for each observable

        Returns:
           complex: expectation value :math:`\expect{A} = \bra{\psi}A\ket{\psi}`
        """
        self._contract_premeasurement_network()
        ket = self._contracted_state_node
        bra = tn.conj(ket, name="Bra")

        all_device_wires = Wires(range(self.num_wires))
        meas_device_wires = []
        # For wires which are measured, add edges between
        # the ket node, the observable nodes, and the bra node
        for obs_node, wires in zip(obs_nodes, obs_wires):

            # translate to consecutive wire labels used by device
            device_wires = self.map_wires(wires)

            meas_device_wires.append(device_wires)
            for idx, l in enumerate(device_wires.labels):
                # Use convention that the indices of a tensor are ordered like
                # [output_idx1, output_idx2, ..., input_idx1, input_idx2, ...]
                output_idx = idx
                input_idx = len(device_wires) + idx
                tn.connect(obs_node[input_idx], ket[l])  # A|psi>
                tn.connect(bra[l], obs_node[output_idx])  # <psi|A

        meas_device_wires = Wires.all_wires(meas_device_wires)

        # unmeasured wires are contracted directly between bra and ket
        unmeasured_device_wires = Wires.unique_wires(
            [all_device_wires, meas_device_wires])
        for w in unmeasured_device_wires.labels:
            tn.connect(bra[w], ket[w])

        # At this stage, all nodes are connected, and the contraction yields a
        # scalar value.
        ket_and_observable_node = ket
        for obs_node in obs_nodes:
            ket_and_observable_node = tn.contract_between(
                obs_node, ket_and_observable_node)
        return tn.contract_between(bra, ket_and_observable_node).tensor
Ejemplo n.º 7
0
    def extract_active_wires(self, raw_operation_grid, raw_observable_grid):
        """Get the subset of wires on the device that are used in the circuit.

        Args:
            raw_operation_grid (Iterable[~.Operator]): The raw grid of operations
            raw_observable_grid (Iterable[~.Operator]): The raw  grid of observables

        Return:
            Wires: active wires on the device
        """
        # pylint: disable=protected-access
        all_operators = list(qml.utils._flatten(raw_operation_grid)) + list(
            qml.utils._flatten(raw_observable_grid)
        )
        all_wires_with_duplicates = [op.wires for op in all_operators if op is not None]
        # make Wires object containing all used wires
        all_wires = Wires.all_wires(all_wires_with_duplicates)
        # shared wires will observe the ordering of the device's wires
        shared_wires = Wires.shared_wires([self.wires, all_wires])
        return shared_wires
Ejemplo n.º 8
0
def diagonalize_qwc_pauli_words(qwc_grouping):
    """Diagonalizes a list of mutually qubit-wise commutative Pauli words.

    Args:
        qwc_grouping (list[Observable]): a list of observables containing mutually
            qubit-wise commutative Pauli words

    Returns:
        tuple:

            * list[Operation]: an instance of the qwc_rotation template which
              diagonalizes the qubit-wise commuting grouping
            * list[Observable]: list of Pauli string observables diagonal in
              the computational basis

    Raises:
        ValueError: if any 2 elements in the input QWC grouping are not qubit-wise commutative

    **Example**

    >>> qwc_group = [qml.PauliX(0) @ qml.PauliZ(1),
                     qml.PauliX(0) @ qml.PauliY(3),
                     qml.PauliZ(1) @ qml.PauliY(3)]
    >>> diagonalize_qwc_pauli_words(qwc_group)
    ([RY(-1.5707963267948966, wires=[0]), RX(1.5707963267948966, wires=[3])],
     [Tensor(PauliZ(wires=[0]), PauliZ(wires=[1])),
     Tensor(PauliZ(wires=[0]), PauliZ(wires=[3])),
     Tensor(PauliZ(wires=[1]), PauliZ(wires=[3]))])
    """
    m_paulis = len(qwc_grouping)
    all_wires = Wires.all_wires(
        [pauli_word.wires for pauli_word in qwc_grouping])
    wire_map = {label: ind for ind, label in enumerate(all_wires)}
    for i in range(m_paulis):
        for j in range(i + 1, m_paulis):
            if not is_qwc(
                    pauli_to_binary(qwc_grouping[i], wire_map=wire_map),
                    pauli_to_binary(qwc_grouping[j], wire_map=wire_map),
            ):
                raise ValueError(
                    "{} and {} are not qubit-wise commuting.".format(
                        qwc_grouping[i], qwc_grouping[j]))

    pauli_operators = []
    diag_terms = []

    paulis_with_identity = (qml.PauliX, qml.PauliY, qml.PauliZ, qml.Identity)
    for term in qwc_grouping:
        diag_terms.append(diagonalize_pauli_word(term))
        if isinstance(term, Tensor):
            for sigma in term.obs:
                if sigma.name != "Identity":
                    if not any([
                            are_identical_pauli_words(sigma, existing_pauli)
                            for existing_pauli in pauli_operators
                    ]):
                        pauli_operators.append(sigma)
        elif isinstance(term, paulis_with_identity):
            sigma = term
            if sigma.name != "Identity":
                if not any([
                        are_identical_pauli_words(sigma, existing_pauli)
                        for existing_pauli in pauli_operators
                ]):
                    pauli_operators.append(sigma)

    unitary = qwc_rotation(pauli_operators)

    return unitary, diag_terms
Ejemplo n.º 9
0
def _terms_to_qubit_operator(coeffs, ops, wires=None):
    r"""Converts a 2-tuple of complex coefficients and PennyLane operations to
    OpenFermion ``QubitOperator``.

    This function is the inverse of ``_qubit_operator_to_terms``.

    Args:
        coeffs (array[complex]):
            coefficients for each observable, same length as ops
        ops (Iterable[pennylane.operation.Observable]): List of PennyLane observables as
            Tensor products of Pauli observables
        wires (Wires, list, tuple, dict): Custom wire mapping used to convert to qubit operator
            from an observable terms measurable in a PennyLane ansatz.
            For types Wires/list/tuple, each item in the iterable represents a wire label
            corresponding to the qubit number equal to its index.
            For type dict, only consecutive-int-valued dict (for wire-to-qubit conversion) is
            accepted. If None, will map sorted wires from all `ops` to consecutive int.

    Returns:
        QubitOperator: an instance of OpenFermion's ``QubitOperator``.

    **Example**

    >>> coeffs = np.array([0.1, 0.2])
    >>> ops = [
    ...     qml.operation.Tensor(qml.PauliX(wires=['w0'])),
    ...     qml.operation.Tensor(qml.PauliY(wires=['w0']), qml.PauliZ(wires=['w2']))
    ... ]
    >>> _terms_to_qubit_operator(coeffs, ops, wires=Wires(['w0', 'w1', 'w2']))
    0.1 [X0] +
    0.2 [Y0 Z2]
    """
    all_wires = Wires.all_wires([op.wires for op in ops], sort=True)

    if wires is not None:
        qubit_indexed_wires = _process_wires(
            wires,
        )
        if not set(all_wires).issubset(set(qubit_indexed_wires)):
            raise ValueError("Supplied `wires` does not cover all wires defined in `ops`.")
    else:
        qubit_indexed_wires = all_wires

    q_op = QubitOperator()
    for coeff, op in zip(coeffs, ops):

        extra_obsvbs = set(op.name) - {"PauliX", "PauliY", "PauliZ", "Identity"}
        if extra_obsvbs != set():
            raise ValueError(
                "Expected only PennyLane observables PauliX/Y/Z or Identity, "
                + "but also got {}.".format(extra_obsvbs)
            )

        # Pauli axis names, note s[-1] expects only 'Pauli{X,Y,Z}'
        pauli_names = [s[-1] if s != "Identity" else s for s in op.name]

        all_identity = all(obs.name == "Identity" for obs in op.obs)
        if (op.name == ["Identity"] and len(op.wires) == 1) or all_identity:
            term_str = ""
        else:
            term_str = " ".join(
                [
                    "{}{}".format(pauli, qubit_indexed_wires.index(wire))
                    for pauli, wire in zip(pauli_names, op.wires)
                    if pauli != "Identity"
                ]
            )

        # This is how one makes QubitOperator in OpenFermion
        q_op += coeff * QubitOperator(term_str)

    return q_op