Source code for qiskit.circuit.library.data_preparation.pauli_feature_map

# This code is part of Qiskit.
# (C) Copyright IBM 2017, 2020.
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""The Pauli expansion circuit module."""

from typing import Optional, Callable, List, Union
from functools import reduce
import numpy as np

from qiskit.circuit import QuantumCircuit
from qiskit.circuit import Parameter, ParameterVector
from qiskit.circuit.library.standard_gates import HGate

from ..n_local.n_local import NLocal

[docs]class PauliFeatureMap(NLocal): r"""The Pauli Expansion circuit. The Pauli Expansion circuit is a data encoding circuit that transforms input data :math:`\vec{x} \in \mathbb{R}^n` as .. math:: U_{\Phi(\vec{x})}=\exp\left(i\sum_{S\subseteq [n]} \phi_S(\vec{x})\prod_{i\in S} P_i\right) The circuit contains ``reps`` repetitions of this transformation. The variable :math:`P_i \in \{ I, X, Y, Z \}` denotes the Pauli matrices. The index :math:`S` describes connectivities between different qubits or datapoints: :math:`S \in \{\binom{n}{k}\ combinations,\ k = 1,... n \}`. Per default the data-mapping :math:`\phi_S` is .. math:: \phi_S(\vec{x}) = \begin{cases} x_0 \text{ if } k = 1 \\ \prod_{j \in S} (\pi - x_j) \text{ otherwise } \end{cases} For example, if the Pauli strings are chosen to be :math:`P_0 = Z` and :math:`P_{0,1} = YY` on 2 qubits and with 1 repetition using the default data-mapping, the Pauli evolution feature map is represented by: .. parsed-literal:: ┌───┐┌──────────────┐┌──────────┐ ┌───────────┐ ┤ H ├┤ U1(2.0*x[0]) ├┤ RX(pi/2) ├──■───────────────────────────────────────■──┤ RX(-pi/2) ├ ├───┤├──────────────┤├──────────┤┌─┴─┐┌─────────────────────────────────┐┌─┴─┐├───────────┤ ┤ H ├┤ U1(2.0*x[1]) ├┤ RX(pi/2) ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├┤ RX(-pi/2) ├ └───┘└──────────────┘└──────────┘└───┘└─────────────────────────────────┘└───┘└───────────┘ Please refer to :class:`ZFeatureMap` for the case :math:`k = 1`, :math:`P_0 = Z` and to :class:`ZZFeatureMap` for the case :math:`k = 2`, :math:`P_0 = Z` and :math:`P_{0,1} = ZZ`. Examples: >>> prep = PauliFeatureMap(2, reps=1, paulis=['ZZ']) >>> print(prep) ┌───┐ q_0: ┤ H ├──■───────────────────────────────────────■── ├───┤┌─┴─┐┌─────────────────────────────────┐┌─┴─┐ q_1: ┤ H ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├ └───┘└───┘└─────────────────────────────────┘└───┘ >>> prep = PauliFeatureMap(2, reps=1, paulis=['Z', 'XX']) >>> print(prep) ┌───┐┌──────────────┐┌───┐ ┌───┐ q_0: ┤ H ├┤ U1(2.0*x[0]) ├┤ H ├──■───────────────────────────────────────■──┤ H ├ ├───┤├──────────────┤├───┤┌─┴─┐┌─────────────────────────────────┐┌─┴─┐├───┤ q_1: ┤ H ├┤ U1(2.0*x[1]) ├┤ H ├┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├┤ H ├ └───┘└──────────────┘└───┘└───┘└─────────────────────────────────┘└───┘└───┘ >>> prep = PauliFeatureMap(2, reps=1, paulis=['ZY']) >>> print(prep) ┌───┐┌──────────┐ ┌───────────┐ q_0: ┤ H ├┤ RX(pi/2) ├──■───────────────────────────────────────■──┤ RX(-pi/2) ├ ├───┤└──────────┘┌─┴─┐┌─────────────────────────────────┐┌─┴─┐└───────────┘ q_1: ┤ H ├────────────┤ X ├┤ U1(2.0*(pi - x[0])*(pi - x[1])) ├┤ X ├───────────── └───┘ └───┘└─────────────────────────────────┘└───┘ >>> from qiskit.circuit.library import EfficientSU2 >>> prep = PauliFeatureMap(3, reps=3, paulis=['Z', 'YY', 'ZXZ']) >>> wavefunction = EfficientSU2(3) >>> classifier = prep.compose(wavefunction >>> classifier.num_parameters 27 >>> classifier.count_ops() OrderedDict([('cx', 39), ('rx', 36), ('u1', 21), ('h', 15), ('ry', 12), ('rz', 12)]) References: [1]: Havlicek et al. (2018), Supervised learning with quantum enhanced feature spaces. `arXiv:1804.11326 <https://arxiv.org/abs/1804.11326>`_ """
[docs] def __init__(self, feature_dimension: Optional[int] = None, reps: int = 2, entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', paulis: Optional[List[str]] = None, data_map_func: Optional[Callable[[np.ndarray], float]] = None, parameter_prefix: str = 'x', insert_barriers: bool = False, ) -> None: """Create a new Pauli expansion circuit. Args: feature_dimension: Number of qubits in the circuit. reps: The number of repeated circuits. entanglement: Specifies the entanglement structure. Refer to :class:`~qiskit.circuit.library.NLocal` for detail. paulis: A list of strings for to-be-used paulis. If None are provided, ``['Z', 'ZZ']`` will be used. data_map_func: A mapping function for data x which can be supplied to override the default mapping from :meth:`self_product`. parameter_prefix: The prefix used if default parameters are generated. insert_barriers: If True, barriers are inserted in between the evolution instructions and hadamard layers. """ super().__init__(num_qubits=feature_dimension, reps=reps, rotation_blocks=HGate(), entanglement=entanglement, parameter_prefix=parameter_prefix, insert_barriers=insert_barriers, skip_final_rotation_layer=True) self._data_map_func = data_map_func or self_product self._paulis = paulis or ['Z', 'ZZ']
# pylint: disable=unused-argument def _parameter_generator(self, rep: int, block: int, indices: List[int] ) -> Optional[List[Parameter]]: """If certain blocks should use certain parameters this method can be overriden.""" params = [self.ordered_parameters[i] for i in indices] return params @property def num_parameters_settable(self): """The number of distinct parameters.""" return self.feature_dimension @property def paulis(self) -> List[str]: """The Pauli strings used in the entanglement of the qubits. Returns: The Pauli strings as list. """ return self._paulis @paulis.setter def paulis(self, paulis: List[str]) -> None: """Set the pauli strings. Args: paulis: The new pauli strings. """ self._invalidate() self._paulis = paulis @property def entanglement_blocks(self): return [self.pauli_block(pauli) for pauli in self._paulis] @entanglement_blocks.setter def entanglement_blocks(self, entanglement_blocks): self._entanglement_blocks = entanglement_blocks @property def feature_dimension(self) -> int: """Returns the feature dimension (which is equal to the number of qubits). Returns: The feature dimension of this feature map. """ return self.num_qubits @feature_dimension.setter def feature_dimension(self, feature_dimension: int) -> None: """Set the feature dimension. Args: feature_dimension: The new feature dimension. """ self.num_qubits = feature_dimension def _extract_data_for_rotation(self, pauli, x): where_non_i = np.where(np.asarray(list(pauli[::-1])) != 'I')[0] x = np.asarray(x) return x[where_non_i]
[docs] def pauli_block(self, pauli_string): """Get the Pauli block for the feature map circuit.""" params = ParameterVector('_', length=len(pauli_string)) time = self._data_map_func(np.asarray(params)) return self.pauli_evolution(pauli_string, time)
[docs] def pauli_evolution(self, pauli_string, time): """Get the evolution block for the given pauli string.""" # for some reason this is in reversed order pauli_string = pauli_string[::-1] # trim the pauli string if identities are included trimmed = [] indices = [] for i, pauli in enumerate(pauli_string): if pauli != 'I': trimmed += [pauli] indices += [i] evo = QuantumCircuit(len(pauli_string)) if len(trimmed) == 0: return evo def basis_change(circuit, inverse=False): for i, pauli in enumerate(pauli_string): if pauli == 'X': circuit.h(i) elif pauli == 'Y': circuit.rx(-np.pi / 2 if inverse else np.pi / 2, i) def cx_chain(circuit, inverse=False): num_cx = len(indices) - 1 for i in reversed(range(num_cx)) if inverse else range(num_cx): circuit.cx(indices[i], indices[i + 1]) basis_change(evo) cx_chain(evo) evo.p(2.0 * time, indices[-1]) cx_chain(evo, inverse=True) basis_change(evo, inverse=True) return evo
def self_product(x: np.ndarray) -> float: """ Define a function map from R^n to R. Args: x: data Returns: float: the mapped value """ coeff = x[0] if len(x) == 1 else reduce(lambda m, n: m * n, np.pi - x) return coeff

© Copyright 2020, Qiskit Development Team. Last updated on 2021/02/27.

Built with Sphinx using a theme provided by Read the Docs.