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]])
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)
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
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)
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
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
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
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
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