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 AngleEmbedding(features, wires, rotation="X"): r""" Encodes :math:`N` features into the rotation angles of :math:`n` qubits, where :math:`N \leq n`. The rotations can be chosen as either :class:`~pennylane.ops.RX`, :class:`~pennylane.ops.RY` or :class:`~pennylane.ops.RZ` gates, as defined by the ``rotation`` parameter: * ``rotation='X'`` uses the features as angles of RX rotations * ``rotation='Y'`` uses the features as angles of RY rotations * ``rotation='Z'`` uses the features as angles of RZ rotations The length of ``features`` has to be smaller or equal to the number of qubits. If there are fewer entries in ``features`` than rotations, the circuit does not apply the remaining rotation gates. Args: features (array): input array of shape ``(N,)``, where N is the number of input features to embed, with :math:`N\leq n` wires (Sequence[int] or int): qubit indices that the template acts on rotation (str): Type of rotations used Raises: ValueError: if inputs do not have the correct format """ ############# # Input checks _check_no_variable(rotation, msg="'rotation' cannot be differentiable") wires = _check_wires(wires) _check_shape( features, (len(wires), ), bound="max", msg="'features' must be of shape {} or smaller; " "got {}.".format((len(wires), ), _get_shape(features)), ) _check_type(rotation, [str], msg="'rotation' must be a string; got {}".format(rotation)) _check_is_in_options( rotation, ["X", "Y", "Z"], msg="did not recognize option {} for 'rotation'.".format(rotation), ) ############### if rotation == "X": for f, w in zip(features, wires): RX(f, wires=w) elif rotation == "Y": for f, w in zip(features, wires): RY(f, wires=w) elif rotation == "Z": for f, w in zip(features, wires): RZ(f, wires=w)
def zz(weight, wires): """Template for decomposition of ZZ coupling. Args: wires (list[int]): qubit indices that the template acts on """ CNOT(wires=wires) RZ(2 * weight, wires=wires[0]) CNOT(wires=wires)
def AngleEmbedding(features, wires, rotation='X'): r""" Encodes :math:`N` features into the rotation angles of :math:`n` qubits, where :math:`N \leq n`. The rotations can be chosen as either :class:`~pennylane.ops.RX`, :class:`~pennylane.ops.RY` or :class:`~pennylane.ops.RZ` gates, as defined by the ``rotation`` parameter: * ``rotation='X'`` uses the features as angles of RX rotations * ``rotation='Y'`` uses the features as angles of RY rotations * ``rotation='Z'`` uses the features as angles of RZ rotations The length of ``features`` has to be smaller or equal to the number of qubits. If there are fewer entries in ``features`` than rotations, the circuit does not apply the remaining rotation gates. This embedding method can also be used to encode a binary sequence into a basis state. For example, to prepare basis state :math:`|0,1,1,0\rangle`, choose ``rotation='X'`` and use the feature vector :math:`[0, \pi/2, \pi/2, 0]`. Alternatively, one can use the :mod:`BasisEmbedding()` template. Args: features (array): Input array of shape ``(N,)``, where N is the number of features to embed. ``N`` must be smaller or equal to the total number of wires. wires (Sequence[int]): sequence of qubit indices that the template acts on Keyword Args: rotation (str): Type of rotations used Raises: ValueError: if ``features`` or ``wires`` is invalid """ if not isinstance(wires, Iterable): raise ValueError( "Wires must be passed as a list of integers; got {}.".format( wires)) if len(features) > len(wires): raise ValueError( "Number of features to embed cannot be larger than number of wires, which is {}; " "got {}.".format(len(wires), len(features))) if rotation == 'X': for f, w in zip(features, wires): RX(f, wires=w) elif rotation == 'Y': for f, w in zip(features, wires): RY(f, wires=w) elif rotation == 'Z': for f, w in zip(features, wires): RZ(f, wires=w) else: raise ValueError( "Rotation has to be `X`, `Y` or `Z`; got {}.".format(rotation))
def strongly_entangling_layer(weights, wires, r, imprimitive): r"""A layer applying rotations on each qubit followed by cascades of 2-qubit entangling gates. Args: weights (array[float]): array of weights of shape ``(len(wires), 3)`` wires (Sequence[int]): sequence of qubit indices that the template acts on r (int): range of the imprimitive gates of this layer, defaults to 1 imprimitive (pennylane.ops.Operation): two-qubit gate to use, defaults to :class:`~pennylane.ops.CNOT` """ for i, wire in enumerate(wires): RZ(weights[i, 0], wires=wire) RY(weights[i, 1], wires=wire) RZ(weights[i, 2], wires=wire) n_wires = len(wires) if n_wires > 1: for i in range(n_wires): imprimitive(wires=[wires[i], wires[(i + r) % n_wires]])
def qaoa_ising_hamiltonian(weights, wires, local_fields, l): """Implements the Ising-like Hamiltonian of the QAOA embedding. Args: weights (array): array of weights wires (Sequence[int] or int): `n` qubit indices that the template acts on local_fields (str): gate implementing the local field l (int): layer index """ # trainable "Ising" ansatz if len(wires) == 1: local_fields(weights[l][0], wires=wires[0]) elif len(wires) == 2: # ZZ coupling CNOT(wires=[wires[0], wires[1]]) RZ(2 * weights[l][0], wires=wires[0]) CNOT(wires=[wires[0], wires[1]]) # local fields for i, _ in enumerate(wires): local_fields(weights[l][i + 1], wires=wires[i]) else: for i, _ in enumerate(wires): if i < len(wires) - 1: # ZZ coupling CNOT(wires=[wires[i], wires[i + 1]]) RZ(2 * weights[l][i], wires=wires[i]) CNOT(wires=[wires[i], wires[i + 1]]) else: # ZZ coupling to enforce periodic boundary condition CNOT(wires=[wires[i], wires[0]]) RZ(2 * weights[l][i], wires=wires[i]) CNOT(wires=[wires[i], wires[0]]) # local fields for i, _ in enumerate(wires): local_fields(weights[l][len(wires) + i], wires=wires[i])
def AngleEmbedding(features, wires, rotation='X'): r""" Encodes :math:`N` features into the rotation angles of :math:`n` qubits, where :math:`N \leq n`. The rotations can be chosen as either :class:`~pennylane.ops.RX`, :class:`~pennylane.ops.RY` or :class:`~pennylane.ops.RZ` gates, as defined by the ``rotation`` parameter: * ``rotation='X'`` uses the features as angles of RX rotations * ``rotation='Y'`` uses the features as angles of RY rotations * ``rotation='Z'`` uses the features as angles of RZ rotations The length of ``features`` has to be smaller or equal to the number of qubits. If there are fewer entries in ``features`` than rotations, the circuit does not apply the remaining rotation gates. Args: features (array): input array of shape ``(N,)``, where N is the number of input features to embed, with :math:`N\leq n` wires (Sequence[int] or int): qubit indices that the template acts on rotation (str): Type of rotations used Raises: ValueError: if inputs do not have the correct format """ ############# # Input checks _check_no_variable([rotation], ['rotation']) wires, n_wires = _check_wires(wires) msg = "AngleEmbedding cannot process more features than number of qubits {};" \ "got {}.".format(n_wires, len(features)) _check_shape(features, (n_wires,), bound='max', msg=msg) _check_type(rotation, [str]) msg = "Rotation strategy {} not recognized.".format(rotation) _check_hyperp_is_in_options(rotation, ['X', 'Y', 'Z'], msg=msg) ############### if rotation == 'X': for f, w in zip(features, wires): RX(f, wires=w) elif rotation == 'Y': for f, w in zip(features, wires): RY(f, wires=w) elif rotation == 'Z': for f, w in zip(features, wires): RZ(f, wires=w)
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 _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 ParticleConservingU2(weights, wires, init_state=None): r"""Implements the heuristic VQE ansatz for Quantum Chemistry simulations using the particle-conserving entangler :math:`U_\mathrm{ent}(\vec{\theta}, \vec{\phi})` proposed in `arXiv:1805.04340 <https://arxiv.org/abs/1805.04340>`_. This template prepares :math:`N`-qubit trial states by applying :math:`D` layers of the entangler block :math:`U_\mathrm{ent}(\vec{\theta}, \vec{\phi})` to the Hartree-Fock state .. math:: \vert \Psi(\vec{\theta}, \vec{\phi}) \rangle = \hat{U}^{(D)}_\mathrm{ent}(\vec{\theta}_D, \vec{\phi}_D) \dots \hat{U}^{(2)}_\mathrm{ent}(\vec{\theta}_2, \vec{\phi}_2) \hat{U}^{(1)}_\mathrm{ent}(\vec{\theta}_1, \vec{\phi}_1) \vert \mathrm{HF}\rangle, where :math:`\hat{U}^{(i)}_\mathrm{ent}(\vec{\theta}_i, \vec{\phi}_i) = \hat{R}_\mathrm{z}(\vec{\theta}_i) \hat{U}_\mathrm{2,\mathrm{ex}}(\vec{\phi}_i)`. The circuit implementing the entangler blocks is shown in the figure below: | .. figure:: ../../_static/templates/layers/particle_conserving_u2.png :align: center :width: 60% :target: javascript:void(0); | Each layer contains :math:`N` rotation gates :math:`R_\mathrm{z}(\vec{\theta})` and :math:`N-1` particle-conserving exchange gates :math:`U_{2,\mathrm{ex}}(\phi)` that act on pairs of nearest-neighbors qubits. The repeated units across several qubits are shown in dotted boxes. The unitary matrix representing :math:`U_{2,\mathrm{ex}}(\phi)` (`arXiv:1805.04340 <https://arxiv.org/abs/1805.04340>`_) is decomposed into its elementary gates and implemented in the :func:`~.u2_ex_gate` function using PennyLane quantum operations. | .. figure:: ../../_static/templates/layers/u2_decomposition.png :align: center :width: 60% :target: javascript:void(0); | Args: weights (tensor_like): Weight tensor of shape ``(D, M)`` where ``D`` is the number of layers and ``M`` = ``2N-1`` is the total number of rotation ``(N)`` and exchange ``(N-1)`` gates per layer. wires (Iterable or Wires): Wires that the template acts on. Accepts an iterable of numbers or strings, or a Wires object. init_state (tensor_like): shape ``(len(wires),)`` tensor representing the Hartree-Fock state used to initialize the wires. Raises: ValueError: if inputs do not have the correct format .. UsageDetails:: #. The number of wires has to be equal to the number of spin orbitals included in the active space. #. The number of trainable parameters scales with the number of layers :math:`D` as :math:`D(2N-1)`. An example of how to use this template is shown below: .. code-block:: python import pennylane as qml from pennylane.templates import ParticleConservingU2 from functools import partial # Build the electronic Hamiltonian from a local .xyz file h, qubits = qml.qchem.molecular_hamiltonian("h2", "h2.xyz") # Define the HF state ref_state = qml.qchem.hf_state(2, qubits) # Define the device dev = qml.device('default.qubit', wires=qubits) # Define the ansatz ansatz = partial(ParticleConservingU2, init_state=ref_state) # Define the cost function cost_fn = qml.ExpvalCost(ansatz, h, dev) # Compute the expectation value of 'h' for a given set of parameters layers = 1 params = qml.init.particle_conserving_u2_normal(layers, qubits) print(cost_fn(params)) """ wires = Wires(wires) repeat, nm_wires, init_state = _preprocess(weights, wires, init_state) qml.BasisState(init_state, wires=wires) for l in range(repeat): for j, _ in enumerate(wires): RZ(weights[l, j], wires=wires[j]) for i, wires_ in enumerate(nm_wires): u2_ex_gate(weights[l, len(wires) + i], wires=wires_)
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)