def expand(self): weight = self.parameters[0] # Interpret first and last wire as r and p r = self.wires[0] p = self.wires[-1] # Sequence of the wires entering the CNOTs between wires 'r' and 'p' set_cnot_wires = [ self.wires[l:l + 2] for l in range(len(self.wires) - 1) ] with qml.tape.QuantumTape() as tape: # ------------------------------------------------------------------ # Apply the first layer # U_1, U_2 acting on wires 'r' and 'p' RX(-np.pi / 2, wires=r) Hadamard(wires=p) # Applying CNOTs between wires 'r' and 'p' for cnot_wires in set_cnot_wires: CNOT(wires=cnot_wires) # Z rotation acting on wire 'p' RZ(weight / 2, wires=p) # Applying CNOTs in reverse order for cnot_wires in reversed(set_cnot_wires): CNOT(wires=cnot_wires) # U_1^+, U_2^+ acting on wires 'r' and 'p' RX(np.pi / 2, wires=r) Hadamard(wires=p) # ------------------------------------------------------------------ # Apply the second layer # U_1, U_2 acting on wires 'r' and 'p' Hadamard(wires=r) RX(-np.pi / 2, wires=p) # Applying CNOTs between wires 'r' and 'p' for cnot_wires in set_cnot_wires: CNOT(wires=cnot_wires) # Z rotation acting on wire 'p' RZ(-weight / 2, wires=p) # Applying CNOTs in reverse order for cnot_wires in reversed(set_cnot_wires): CNOT(wires=cnot_wires) # U_1^+, U_2^+ acting on wires 'r' and 'p' Hadamard(wires=r) RX(np.pi / 2, wires=p) return tape
def test_hadamard_state(self, nr_wires, tol): """Tests that applying Hadamard gates on all qubits produces an equal superposition over all basis states""" dev = qml.device("default.mixed", wires=nr_wires) ops = [Hadamard(i) for i in range(nr_wires)] dev.apply(ops) assert np.allclose(dev.state, hadamard_state(nr_wires), atol=tol, rtol=0)
def test_bell_state(self, tol): """Tests that we correctly prepare a Bell state by applying a Hadamard then a CNOT""" dev = qml.device("default.mixed", wires=2) ops = [Hadamard(0), CNOT(wires=[0, 1])] dev.apply(ops) bell = np.zeros((4, 4)) bell[0, 0] = bell[0, 3] = bell[3, 0] = bell[3, 3] = 1 / 2 assert np.allclose(bell, dev.state, atol=tol, rtol=0)
def _layer2(weight, s, r, q, p, set_cnot_wires): r"""Implement the second layer of the circuit to exponentiate the double-excitation operator entering the UCCSD ansatz. .. math:: \hat{U}_{pqrs}^{(2)}(\theta) = \mathrm{exp} \Big\{ \frac{i\theta}{8} \bigotimes_{b=s+1}^{r-1} \hat{Z}_b \bigotimes_{a=q+1}^{p-1} \hat{Z}_a (\hat{Y}_s \hat{X}_r \hat{Y}_q \hat{Y}_p) \Big\} Args: weight (float): angle :math:`\theta` entering the Z rotation acting on wire ``p`` s (int): qubit index ``s`` r (int): qubit index ``r`` q (int): qubit index ``q`` p (int): qubit index ``p`` set_cnot_wires (sequence[int]): two-element sequence with the indices of the qubits the CNOT gates act on """ # U_1, U_2, U_3, U_4 acting on wires 's', 'r', 'q' and 'p' RX(-np.pi / 2, wires=s) Hadamard(wires=r) RX(-np.pi / 2, wires=q) RX(-np.pi / 2, wires=p) # Applying CNOTs between wires 's' and 'p' for cnot_wires in set_cnot_wires: CNOT(wires=cnot_wires) # Z rotation acting on wire 'p' RZ(weight / 8, wires=p) # Applying CNOTs in reverse order for cnot_wires in reversed(set_cnot_wires): CNOT(wires=cnot_wires) # U_1^+, U_2^+, U_3^+, U_4^+ acting on wires 's', 'r', 'q' and 'p' RX(np.pi / 2, wires=s) Hadamard(wires=r) RX(np.pi / 2, wires=q) RX(np.pi / 2, wires=p)
def test_undo_rotations(self, nr_wires, tol): """Tests that rotations are correctly applied by adding their inverse as initial operations""" dev = qml.device("default.mixed", wires=nr_wires) ops = [Hadamard(i) for i in range(nr_wires)] rots = ops dev.apply(ops, rots) basis = np.reshape(basis_state(0, nr_wires), [2] * (2 * nr_wires)) # dev.state = pre-rotated state, dev._state = state after rotations assert np.allclose(dev.state, hadamard_state(nr_wires), atol=tol, rtol=0) assert np.allclose(dev._state, basis, atol=tol, rtol=0)
def _layer8(weight, s, r, q, p, set_cnot_wires): r"""Implement the eighth layer of the circuit to exponentiate the double-excitation operator entering the UCCSD ansatz. .. math:: \hat{U}_{pqrs}^{(8)}(\theta) = \mathrm{exp} \Big\{ -\frac{i\theta}{8} \bigotimes_{b=s+1}^{r-1} \hat{Z}_b \bigotimes_{a=q+1}^{p-1} \hat{Z}_a (\hat{Y}_s \hat{Y}_r \hat{X}_q \hat{Y}_p) \Big\} Args: weight (float): angle :math:`\theta` entering the Z rotation acting on wire ``p`` s (int): qubit index ``s`` r (int): qubit index ``r`` q (int): qubit index ``q`` p (int): qubit index ``p`` set_cnot_wires (list[Wires]): list of CNOT wires """ # U_1, U_2, U_3, U_4 acting on wires 's', 'r', 'q' and 'p' RX(-np.pi / 2, wires=s) RX(-np.pi / 2, wires=r) Hadamard(wires=q) RX(-np.pi / 2, wires=p) # Applying CNOTs for cnot_wires in set_cnot_wires: CNOT(wires=cnot_wires) # Z rotation acting on wire 'p' RZ(-weight / 8, wires=p) # Applying CNOTs in reverse order for cnot_wires in reversed(set_cnot_wires): CNOT(wires=cnot_wires) # U_1^+, U_2^+, U_3^+, U_4^+ acting on wires 's', 'r', 'q' and 'p' RX(np.pi / 2, wires=s) RX(np.pi / 2, wires=r) Hadamard(wires=q) RX(np.pi / 2, wires=p)
def expand(self): ctrl_str = "0" * (len(self.wires) - 1) with qml.tape.QuantumTape() as tape: for wire in self.wires[:-1]: Hadamard(wire) PauliZ(self.wires[-1]) MultiControlledX( control_values=ctrl_str, control_wires=self.wires[:-1], wires=self.wires[-1], work_wires=self.work_wires, ) PauliZ(self.wires[-1]) for wire in self.wires[:-1]: Hadamard(wire) return tape
def qaoa_feature_encoding_hamiltonian(features, n_features, wires): """Implements the encoding Hamiltonian of the QAOA embedding. Args: features (array): array of features to encode n_features (int): number of features to encode """ for idx, w in enumerate(wires): # Either feed in feature if idx < n_features: RX(features[idx], wires=w) # or a Hadamard else: Hadamard(wires=w)
def expand(self): unitary = self.parameters[0] unitary_powers = [unitary] for _ in range(len(self.estimation_wires) - 1): new_power = unitary_powers[-1] @ unitary_powers[-1] unitary_powers.append(new_power) with qml.tape.QuantumTape() as tape: for wire in self.estimation_wires: Hadamard(wire) ControlledQubitUnitary( unitary_powers.pop(), control_wires=wire, wires=self.target_wires ) qml.templates.QFT(wires=self.estimation_wires).inv() return tape
def SingleExcitationUnitary(weight, wires=None): r"""Circuit to exponentiate the tensor product of Pauli matrices representing the single-excitation operator entering the Unitary Coupled-Cluster Singles and Doubles (UCCSD) ansatz. UCCSD is a VQE ansatz commonly used to run quantum chemistry simulations. The CC single-excitation operator is given by .. math:: \hat{U}_{pr}(\theta) = \mathrm{exp} \{ \theta_{pr} (\hat{c}_p^\dagger \hat{c}_r -\mathrm{H.c.}) \}, where :math:`\hat{c}` and :math:`\hat{c}^\dagger` are the fermionic annihilation and creation operators and the indices :math:`r` and :math:`p` run over the occupied and unoccupied molecular orbitals, respectively. Using the `Jordan-Wigner transformation <https://arxiv.org/abs/1208.5986>`_ the fermionic operator defined above can be written in terms of Pauli matrices (for more details see `arXiv:1805.04340 <https://arxiv.org/abs/1805.04340>`_). .. math:: \hat{U}_{pr}(\theta) = \mathrm{exp} \Big\{ \frac{i\theta}{2} \bigotimes_{a=r+1}^{p-1}\hat{Z}_a (\hat{Y}_r \hat{X}_p) \Big\} \mathrm{exp} \Big\{ -\frac{i\theta}{2} \bigotimes_{a=r+1}^{p-1} \hat{Z}_a (\hat{X}_r \hat{Y}_p) \Big\}. The quantum circuit to exponentiate the tensor product of Pauli matrices entering the latter equation is shown below (see `arXiv:1805.04340 <https://arxiv.org/abs/1805.04340>`_): | .. figure:: ../../_static/templates/subroutines/single_excitation_unitary.png :align: center :width: 60% :target: javascript:void(0); | As explained in `Seely et al. (2012) <https://arxiv.org/abs/1208.5986>`_, the exponential of a tensor product of Pauli-Z operators can be decomposed in terms of :math:`2(n-1)` CNOT gates and a single-qubit Z-rotation referred to as :math:`U_\theta` in the figure above. If there are :math:`X` or :math:`Y` Pauli matrices in the product, the Hadamard (:math:`H`) or :math:`R_x` gate has to be applied to change to the :math:`X` or :math:`Y` basis, respectively. The latter operations are denoted as :math:`U_1` and :math:`U_2` in the figure above. See the Usage Details section for more information. Args: weight (float): angle :math:`\theta` entering the Z rotation acting on wire ``p`` wires (Iterable or Wires): Wires that the template acts on. The wires represent the subset of orbitals in the interval ``[r, p]``. Must be of minimum length 2. The first wire is interpreted as ``r`` and the last wire as ``p``. Wires in between are acted on with CNOT gates to compute the parity of the set of qubits. Raises: ValueError: if inputs do not have the correct format .. UsageDetails:: Notice that: #. :math:`\hat{U}_{pr}(\theta)` involves two exponentiations where :math:`\hat{U}_1`, :math:`\hat{U}_2`, and :math:`\hat{U}_\theta` are defined as follows, .. math:: [U_1, U_2, U_{\theta}] = \Bigg\{\bigg[R_x(-\pi/2), H, R_z(\theta/2)\bigg], \bigg[H, R_x(-\frac{\pi}{2}), R_z(-\theta/2) \bigg] \Bigg\} #. For a given pair ``[r, p]``, ten single-qubit and ``4*(len(wires)-1)`` CNOT operations are applied. Notice also that CNOT gates act only on qubits ``wires[1]`` to ``wires[-2]``. The operations performed across these qubits are shown in dashed lines in the figure above. An example of how to use this template is shown below: .. code-block:: python import pennylane as qml from pennylane.templates import SingleExcitationUnitary dev = qml.device('default.qubit', wires=3) @qml.qnode(dev) def circuit(weight, wires=None): SingleExcitationUnitary(weight, wires=wires) return qml.expval(qml.PauliZ(0)) weight = 0.56 print(circuit(weight, wires=[0, 1, 2])) """ ############## # Input checks wires = Wires(wires) if len(wires) < 2: raise ValueError("expected at least two wires; got {}".format( len(wires))) expected_shape = () check_shape( weight, expected_shape, msg="'weight' must be of shape {}; got {}".format( expected_shape, get_shape(weight)), ) ############### # Interpret first and last wire as r and p r = wires[0] p = wires[-1] # Sequence of the wires entering the CNOTs between wires 'r' and 'p' set_cnot_wires = [wires.subset([l, l + 1]) for l in range(len(wires) - 1)] # ------------------------------------------------------------------ # Apply the first layer # U_1, U_2 acting on wires 'r' and 'p' RX(-np.pi / 2, wires=r) Hadamard(wires=p) # Applying CNOTs between wires 'r' and 'p' for cnot_wires in set_cnot_wires: CNOT(wires=cnot_wires) # Z rotation acting on wire 'p' RZ(weight / 2, wires=p) # Applying CNOTs in reverse order for cnot_wires in reversed(set_cnot_wires): CNOT(wires=cnot_wires) # U_1^+, U_2^+ acting on wires 'r' and 'p' RX(np.pi / 2, wires=r) Hadamard(wires=p) # ------------------------------------------------------------------ # Apply the second layer # U_1, U_2 acting on wires 'r' and 'p' Hadamard(wires=r) RX(-np.pi / 2, wires=p) # Applying CNOTs between wires 'r' and 'p' for cnot_wires in set_cnot_wires: CNOT(wires=cnot_wires) # Z rotation acting on wire 'p' RZ(-weight / 2, wires=p) # Applying CNOTs in reverse order for cnot_wires in reversed(set_cnot_wires): CNOT(wires=cnot_wires) # U_1^+, U_2^+ acting on wires 'r' and 'p' Hadamard(wires=r) RX(np.pi / 2, wires=p)