From 24717922ed34f89c062882c15cb9457c61101ab9 Mon Sep 17 00:00:00 2001 From: Declan Millar Date: Sat, 10 Jun 2023 13:26:38 +0100 Subject: [PATCH] Add `TrainableFidelityStatevectorKernel` (#639) * add trainable fidelity statevector kernel * fix copyright * refactor check on trainable parameters * fix copyright * consolidate trainable kernels tests * avoid string flags in tests * fix copyright * test trainer with statevector kernel * add release note * fix style * format release note * lint * style * Update release note based on Steve's comments * add imports to release note snippet * fix typo --------- Co-authored-by: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com> --- qiskit_machine_learning/kernels/__init__.py | 3 + .../kernels/fidelity_statevector_kernel.py | 3 + .../trainable_fidelity_quantum_kernel.py | 25 +---- .../trainable_fidelity_statevector_kernel.py | 102 ++++++++++++++++++ .../kernels/trainable_kernel.py | 27 ++++- ...y-statevector-kernel-6d9c927d16c1cb35.yaml | 44 ++++++++ .../test_fidelity_qkernel_trainer.py | 49 ++++++--- .../test_fidelity_statevector_kernel.py | 5 - .../test_trainable_fidelity_qkernel.py | 91 ++++++++-------- 9 files changed, 260 insertions(+), 89 deletions(-) create mode 100644 qiskit_machine_learning/kernels/trainable_fidelity_statevector_kernel.py create mode 100644 releasenotes/notes/add-trainable-fidelity-statevector-kernel-6d9c927d16c1cb35.yaml diff --git a/qiskit_machine_learning/kernels/__init__.py b/qiskit_machine_learning/kernels/__init__.py index e99dc1be8..0b721fa55 100644 --- a/qiskit_machine_learning/kernels/__init__.py +++ b/qiskit_machine_learning/kernels/__init__.py @@ -48,6 +48,7 @@ FidelityStatevectorKernel TrainableKernel TrainableFidelityQuantumKernel + TrainableFidelityStatevectorKernel Submodules ========== @@ -64,6 +65,7 @@ from .fidelity_statevector_kernel import FidelityStatevectorKernel from .trainable_kernel import TrainableKernel from .trainable_fidelity_quantum_kernel import TrainableFidelityQuantumKernel +from .trainable_fidelity_statevector_kernel import TrainableFidelityStatevectorKernel __all__ = [ "QuantumKernel", @@ -72,4 +74,5 @@ "FidelityStatevectorKernel", "TrainableKernel", "TrainableFidelityQuantumKernel", + "TrainableFidelityStatevectorKernel", ] diff --git a/qiskit_machine_learning/kernels/fidelity_statevector_kernel.py b/qiskit_machine_learning/kernels/fidelity_statevector_kernel.py index e0b1b1fa1..f6d93b1f1 100644 --- a/qiskit_machine_learning/kernels/fidelity_statevector_kernel.py +++ b/qiskit_machine_learning/kernels/fidelity_statevector_kernel.py @@ -117,6 +117,9 @@ def evaluate( elif not np.array_equal(x_vec, y_vec): is_symmetric = False + return self._evaluate(x_vec, y_vec, is_symmetric) + + def _evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray, is_symmetric: bool): kernel_shape = (x_vec.shape[0], y_vec.shape[0]) x_svs = [self._get_statevector(tuple(x)) for x in x_vec] diff --git a/qiskit_machine_learning/kernels/trainable_fidelity_quantum_kernel.py b/qiskit_machine_learning/kernels/trainable_fidelity_quantum_kernel.py index 6b843e46c..25bc67752 100644 --- a/qiskit_machine_learning/kernels/trainable_fidelity_quantum_kernel.py +++ b/qiskit_machine_learning/kernels/trainable_fidelity_quantum_kernel.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # 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 @@ -23,7 +23,6 @@ from .fidelity_quantum_kernel import FidelityQuantumKernel, KernelIndices from .trainable_kernel import TrainableKernel -from ..exceptions import QiskitMachineLearningError class TrainableFidelityQuantumKernel(TrainableKernel, FidelityQuantumKernel): @@ -101,28 +100,6 @@ def __init__( ] self._parameter_dict = {parameter: None for parameter in feature_map.parameters} - def evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray | None = None) -> np.ndarray: - for param in self._training_parameters: - if self._parameter_dict[param] is None: - raise QiskitMachineLearningError( - f"Trainable parameter {param} has not been bound. Make sure to bind all" - "trainable parameters to numerical values using `.assign_training_parameters()`" - "before calling `.evaluate()`." - ) - return super().evaluate(x_vec, y_vec) - - def _parameter_array(self, x_vec: np.ndarray) -> np.ndarray: - """ - Combines the feature values and the trainable parameters into one array. - """ - full_array = np.zeros((x_vec.shape[0], self._num_features + self._num_training_parameters)) - for i, x in enumerate(x_vec): - self._parameter_dict.update( - {feature_param: x[j] for j, feature_param in enumerate(self._feature_parameters)} - ) - full_array[i, :] = list(self._parameter_dict.values()) - return full_array - def _get_parameterization( self, x_vec: np.ndarray, y_vec: np.ndarray ) -> tuple[np.ndarray, np.ndarray, KernelIndices]: diff --git a/qiskit_machine_learning/kernels/trainable_fidelity_statevector_kernel.py b/qiskit_machine_learning/kernels/trainable_fidelity_statevector_kernel.py new file mode 100644 index 000000000..9d8e82a7f --- /dev/null +++ b/qiskit_machine_learning/kernels/trainable_fidelity_statevector_kernel.py @@ -0,0 +1,102 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +"""Trainable Fidelity Statevector Kernel""" + +from __future__ import annotations + +from typing import Sequence, Type + +import numpy as np +from qiskit import QuantumCircuit +from qiskit.circuit import Parameter, ParameterVector +from qiskit.quantum_info import Statevector + + +from .fidelity_statevector_kernel import FidelityStatevectorKernel, SV +from .trainable_kernel import TrainableKernel + + +class TrainableFidelityStatevectorKernel(TrainableKernel, FidelityStatevectorKernel): + r""" + A trainable version of the + :class:`~qiskit_machine_learning.kernels.FidelityStatevectorKernel`. + + Finding good quantum kernels for a specific machine learning task is a big challenge in quantum + machine learning. One way to choose the kernel is to add trainable parameters to the feature + map, which can be used to fine-tune the kernel. + + This kernel has trainable parameters :math:`\theta` that can be bound using training algorithms. + The kernel entries are given as + + .. math:: + + K_{\theta}(x,y) = |\langle \phi_{\theta}(x) | \phi_{\theta}(y) \rangle|^2 + """ + + def __init__( + self, + *, + feature_map: QuantumCircuit | None = None, + statevector_type: Type[SV] = Statevector, + training_parameters: ParameterVector | Sequence[Parameter] | None = None, + cache_size: int | None = None, + auto_clear_cache: bool = True, + shots: int | None = None, + enforce_psd: bool = True, + ) -> None: + """ + Args: + feature_map: Parameterized circuit to be used as the feature map. If ``None`` is given, + :class:`~qiskit.circuit.library.ZZFeatureMap` is used with two qubits. If there's + a mismatch in the number of qubits of the feature map and the number of features + in the dataset, then the kernel will try to adjust the feature map to reflect the + number of features. + statevector_type: The type of Statevector that will be instantiated using the + ``feature_map`` quantum circuit and used to compute the fidelity kernel. This type + should inherit from (and defaults to) :class:`~qiskit.quantum_info.Statevector`. + training_parameters: Iterable containing :class:`~qiskit.circuit.Parameter` objects + which correspond to quantum gates on the feature map circuit which may be tuned. + If users intend to tune feature map parameters to find optimal values, this field + should be set. + cache_size: Maximum size of the statevector cache. When ``None`` this is unbounded. + auto_clear_cache: Determines whether the statevector cache is retained when + :meth:`evaluate` is called. The cache is automatically cleared by default. + shots: The number of shots. If ``None``, the exact fidelity is used. Otherwise, the + mean is taken of samples drawn from a binomial distribution with probability equal + to the exact fidelity. + enforce_psd: Project to the closest positive semidefinite matrix if ``x = y``. + Default ``True``. + """ + super().__init__( + feature_map=feature_map, + statevector_type=statevector_type, + training_parameters=training_parameters, + cache_size=cache_size, + auto_clear_cache=auto_clear_cache, + shots=shots, + enforce_psd=enforce_psd, + ) + + # Override the number of features defined in the base class. + self._num_features = feature_map.num_parameters - self._num_training_parameters + self._feature_parameters = [ + parameter + for parameter in feature_map.parameters + if parameter not in training_parameters + ] + self._parameter_dict = {parameter: None for parameter in self.feature_map.parameters} + + def _evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray, is_symmetric: bool): + new_x_vec = self._parameter_array(x_vec) + new_y_vec = self._parameter_array(y_vec) + return super()._evaluate(new_x_vec, new_y_vec, is_symmetric) diff --git a/qiskit_machine_learning/kernels/trainable_kernel.py b/qiskit_machine_learning/kernels/trainable_kernel.py index e37b68ab5..df896a9ec 100644 --- a/qiskit_machine_learning/kernels/trainable_kernel.py +++ b/qiskit_machine_learning/kernels/trainable_kernel.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2022. +# (C) Copyright IBM 2022, 2023. # # 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 @@ -21,6 +21,7 @@ from qiskit.circuit.parameterexpression import ParameterValueType from .base_kernel import BaseKernel +from ..exceptions import QiskitMachineLearningError class TrainableKernel(BaseKernel, ABC): @@ -44,6 +45,8 @@ def __init__( self._parameter_dict = {parameter: None for parameter in training_parameters} + self._feature_parameters: Sequence[Parameter] = [] + def assign_training_parameters( self, parameter_values: Mapping[Parameter, ParameterValueType] | Sequence[ParameterValueType], @@ -93,3 +96,25 @@ def num_training_parameters(self) -> int: Returns the number of training parameters. """ return len(self._training_parameters) + + def _parameter_array(self, x_vec: np.ndarray) -> np.ndarray: + """ + Combines the feature values and the trainable parameters into one array. + """ + self._check_trainable_parameters() + full_array = np.zeros((x_vec.shape[0], self._num_features + self._num_training_parameters)) + for i, x in enumerate(x_vec): + self._parameter_dict.update( + {feature_param: x[j] for j, feature_param in enumerate(self._feature_parameters)} + ) + full_array[i, :] = list(self._parameter_dict.values()) + return full_array + + def _check_trainable_parameters(self) -> None: + for param in self._training_parameters: + if self._parameter_dict[param] is None: + raise QiskitMachineLearningError( + f"Trainable parameter {param} has not been bound. Make sure to bind all" + "trainable parameters to numerical values using `.assign_training_parameters()`" + "before calling `.evaluate()`." + ) diff --git a/releasenotes/notes/add-trainable-fidelity-statevector-kernel-6d9c927d16c1cb35.yaml b/releasenotes/notes/add-trainable-fidelity-statevector-kernel-6d9c927d16c1cb35.yaml new file mode 100644 index 000000000..1f103aed9 --- /dev/null +++ b/releasenotes/notes/add-trainable-fidelity-statevector-kernel-6d9c927d16c1cb35.yaml @@ -0,0 +1,44 @@ +--- +features: + - | + A new :class:`~qiskit_machine_learning.kernels.TrainableFidelityStatevectorKernel` class has + been added that provides a trainable version of + :class:`~qiskit_machine_learning.kernels.FidelityStatevectorKernel`. This relationship mirrors + that between the existing + :class:`~qiskit_machine_learning.kernels.TrainableFidelityQuantumKernel`class and + :class:`~qiskit_machine_learning.kernels.FidelityQuantumKernel`. Thus, + :class:`~qiskit_machine_learning.kernels.TrainableFidelityStatevectorKernel` inherits from both + :class:`~qiskit_machine_learning.kernels.FidelityStatevectorKernel` and + :class:`~qiskit_machine_learning.kernels.TrainableKernel`. + + This class is used with + :class:`~qiskit_machine_learning.kernels.algorithms.QuantumKernelTrainer` in an identical way to + :class:`~qiskit_machine_learning.kernels.TrainableFidelityQuantumKernel`, except for the + arguments specific to + :class:`~qiskit_machine_learning.kernels.TrainableFidelityStatevectorKernel`. + + For an example, see the snippet below: + + .. code-block:: python + + from qiskit.quantum_info import Statevector + from qiskit_machine_learning.kernels import TrainableFidelityStatevectorKernel + from qiskit_machine_learning.kernels.algorithms import QuantumKernelTrainer + + # Instantiate trainable fidelity statevector kernel. + quantum_kernel = TrainableFidelityStatevectorKernel( + feature_map=, + statevector_type=Statevector, + training_parameters=, + cache_size=None, + auto_clear_cache=True, + shots=None, + enforce_psd=True, + ) + + # Instantiate a quantum kernel trainer (QKT). + qkt = QuantumKernelTrainer(quantum_kernel=quantum_kernel) + + # Train the kernel using QKT directly. + qkt_results = qkt.fit(, ) + optimized_kernel = qkt_results.quantum_kernel diff --git a/test/kernels/algorithms/test_fidelity_qkernel_trainer.py b/test/kernels/algorithms/test_fidelity_qkernel_trainer.py index 9625e98a3..6660568a7 100644 --- a/test/kernels/algorithms/test_fidelity_qkernel_trainer.py +++ b/test/kernels/algorithms/test_fidelity_qkernel_trainer.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021, 2022. +# (C) Copyright IBM 2021, 2023. # # 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 @@ -18,6 +18,7 @@ from test import QiskitMachineLearningTestCase +from ddt import ddt, data import numpy as np from qiskit import QuantumCircuit from qiskit.circuit import Parameter, ParameterVector @@ -26,13 +27,20 @@ from scipy.optimize import minimize from qiskit_machine_learning.algorithms.classifiers import QSVC -from qiskit_machine_learning.kernels import TrainableFidelityQuantumKernel +from qiskit_machine_learning.kernels import ( + TrainableFidelityQuantumKernel, + TrainableFidelityStatevectorKernel, +) from qiskit_machine_learning.kernels.algorithms import QuantumKernelTrainer from qiskit_machine_learning.utils.loss_functions import SVCLoss +@ddt class TestQuantumKernelTrainer(QiskitMachineLearningTestCase): - """Test QuantumKernelTrainer Algorithm""" + """Test QuantumKernelTrainer Algorithm + + Tests usage with ``TrainableFidelityQuantumKernel`` and ``TrainableFidelityStatevectorKernel``. + """ def setUp(self): super().setUp() @@ -57,25 +65,34 @@ def setUp(self): self.sample_test = np.asarray([[2.199114860, 5.15221195], [0.50265482, 0.06283185]]) self.label_test = np.asarray([1, 0]) - self.quantum_kernel = TrainableFidelityQuantumKernel( + @data( + TrainableFidelityQuantumKernel, + TrainableFidelityStatevectorKernel, + ) + def test_default_fit(self, trainable_kernel_type): + """Test trainer with default parameters.""" + quantum_kernel = trainable_kernel_type( feature_map=self.feature_map, training_parameters=self.training_parameters, ) - - def test_default_fit(self): - """Test trainer with default parameters.""" - qkt = QuantumKernelTrainer(quantum_kernel=self.quantum_kernel) + qkt = QuantumKernelTrainer(quantum_kernel=quantum_kernel) qkt_result = qkt.fit(self.sample_train, self.label_train) self._fit_and_assert_score(qkt_result) - def test_fit_with_params(self): + @data( + TrainableFidelityQuantumKernel, + TrainableFidelityStatevectorKernel, + ) + def test_fit_with_params(self, trainable_kernel_type): """Test trainer with custom parameters.""" + quantum_kernel = trainable_kernel_type( + feature_map=self.feature_map, + training_parameters=self.training_parameters, + ) loss = SVCLoss(C=0.8, gamma="auto") optimizer = partial(minimize, method="COBYLA", options={"maxiter": 25}) - qkt = QuantumKernelTrainer( - quantum_kernel=self.quantum_kernel, loss=loss, optimizer=optimizer - ) + qkt = QuantumKernelTrainer(quantum_kernel=quantum_kernel, loss=loss, optimizer=optimizer) qkt_result = qkt.fit(self.sample_train, self.label_train) # Ensure user parameters are bound to real values @@ -83,7 +100,11 @@ def test_fit_with_params(self): self._fit_and_assert_score(qkt_result) - def test_asymmetric_trainable_parameters(self): + @data( + TrainableFidelityQuantumKernel, + TrainableFidelityStatevectorKernel, + ) + def test_asymmetric_trainable_parameters(self, trainable_kernel_type): """Test when the number of trainable parameters does not equal to the number of features.""" qc = QuantumCircuit(2) training_parameters = Parameter("θ") @@ -92,7 +113,7 @@ def test_asymmetric_trainable_parameters(self): qc.rz(feature_params[0], 0) qc.rz(feature_params[1], 1) - quantum_kernel = TrainableFidelityQuantumKernel( + quantum_kernel = trainable_kernel_type( feature_map=qc, training_parameters=[training_parameters], ) diff --git a/test/kernels/test_fidelity_statevector_kernel.py b/test/kernels/test_fidelity_statevector_kernel.py index db3a0c4cc..acc66f535 100644 --- a/test/kernels/test_fidelity_statevector_kernel.py +++ b/test/kernels/test_fidelity_statevector_kernel.py @@ -24,10 +24,8 @@ from sklearn.svm import SVC from qiskit import QuantumCircuit -from qiskit.algorithms.state_fidelities import ComputeUncompute from qiskit.circuit import Parameter from qiskit.circuit.library import ZFeatureMap -from qiskit.primitives import Sampler from qiskit.utils import algorithm_globals, optionals from qiskit_machine_learning.kernels import FidelityStatevectorKernel @@ -57,9 +55,6 @@ def setUp(self): self.sample_test = np.asarray([[2.199114860, 5.15221195], [0.50265482, 0.06283185]]) self.label_test = np.asarray([0, 1]) - self.sampler = Sampler() - self.fidelity = ComputeUncompute(self.sampler) - self.properties = { "samples_1": self.sample_train[0], "samples_4": self.sample_train, diff --git a/test/kernels/test_trainable_fidelity_qkernel.py b/test/kernels/test_trainable_fidelity_qkernel.py index 2d840270b..36a342922 100644 --- a/test/kernels/test_trainable_fidelity_qkernel.py +++ b/test/kernels/test_trainable_fidelity_qkernel.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021, 2022. +# (C) Copyright IBM 2021, 2023. # # 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 @@ -9,24 +9,28 @@ # 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. -""" Test TrainableQuantumKernel using primitives """ +""" Test trainable quantum kernels using primitives """ +import itertools import unittest from test import QiskitMachineLearningTestCase import numpy as np -from ddt import ddt, data +from ddt import ddt, data, idata, unpack from qiskit.circuit import Parameter from qiskit.circuit.library import ZZFeatureMap from qiskit_machine_learning import QiskitMachineLearningError -from qiskit_machine_learning.kernels import TrainableFidelityQuantumKernel +from qiskit_machine_learning.kernels import ( + TrainableFidelityQuantumKernel, + TrainableFidelityStatevectorKernel, +) @ddt class TestPrimitivesTrainableQuantumKernelClassify(QiskitMachineLearningTestCase): - """Test trainable QuantumKernel.""" + """Test the Primitive-based trainable quantum kernel, and the trainable statevector kernel.""" def setUp(self): super().setUp() @@ -43,10 +47,11 @@ def setUp(self): ) self.sample_test = np.array([0.0, 1.0, 2.0]) - def test_training_parameters(self): + @data(TrainableFidelityQuantumKernel, TrainableFidelityStatevectorKernel) + def test_training_parameters(self, trainable_kernel_type): """Test assigning/re-assigning user parameters""" - kernel = TrainableFidelityQuantumKernel( + kernel = trainable_kernel_type( feature_map=self.feature_map, training_parameters=self.training_parameters, ) @@ -103,66 +108,61 @@ def test_training_parameters(self): # Ensure the values are properly bound np.testing.assert_array_equal(kernel.parameter_values, param_binds) - @data("params_1", "params_2") - def test_evaluate_symmetric(self, params): + @idata( + itertools.product( + [TrainableFidelityQuantumKernel, TrainableFidelityStatevectorKernel], + [ + ([0.0, 0.0, 0.0], [[1.0, 0.03351197], [0.03351197, 1.0]]), + ([0.1, 0.531, 4.12], [[1.0, 0.082392], [0.082392, 1.0]]), + ], + ) + ) + @unpack + def test_evaluate_symmetric(self, trainable_kernel_type, params_solution): """Test kernel evaluations for different training parameters""" - if params == "params_1": - training_params = [0.0, 0.0, 0.0] - else: - training_params = [0.1, 0.531, 4.12] - - kernel = TrainableFidelityQuantumKernel( + kernel = trainable_kernel_type( feature_map=self.feature_map, training_parameters=self.training_parameters, ) - kernel.assign_training_parameters(training_params) + kernel.assign_training_parameters(params_solution[0]) kernel_matrix = kernel.evaluate(self.sample_train) # Ensure that the calculations are correct - np.testing.assert_allclose( - kernel_matrix, self._get_symmetric_solution(params), rtol=1e-7, atol=1e-7 + np.testing.assert_allclose(kernel_matrix, params_solution[1], rtol=1e-7, atol=1e-7) + + @idata( + itertools.product( + [TrainableFidelityQuantumKernel, TrainableFidelityStatevectorKernel], + [ + ([0.0, 0.0, 0.0], [[0.00569059], [0.07038205]]), + ([0.1, 0.531, 4.12], [[0.10568674], [0.122404]]), + ], ) - - def _get_symmetric_solution(self, params): - if params == "params_1": - return np.array([[1.0, 0.03351197], [0.03351197, 1.0]]) - return np.array([[1.0, 0.082392], [0.082392, 1.0]]) - - @data("params_1", "params_2") - def test_evaluate_asymmetric(self, params): + ) + @unpack + def test_evaluate_asymmetric(self, trainable_kernel_type, params_solution): """Test kernel evaluations for different training parameters""" - if params == "params_1": - training_params = [0.0, 0.0, 0.0] - else: - training_params = [0.1, 0.531, 4.12] - - kernel = TrainableFidelityQuantumKernel( + kernel = trainable_kernel_type( feature_map=self.feature_map, training_parameters=self.training_parameters, ) - kernel.assign_training_parameters(training_params) + kernel.assign_training_parameters(params_solution[0]) kernel_matrix = kernel.evaluate(self.sample_train, self.sample_test) # Ensure that the calculations are correct - np.testing.assert_allclose( - kernel_matrix, self._get_asymmetric_solution(params), rtol=1e-7, atol=1e-7 - ) - - def _get_asymmetric_solution(self, params): - if params == "params_1": - return np.array([[0.00569059], [0.07038205]]) - return np.array([[0.10568674], [0.122404]]) + np.testing.assert_allclose(kernel_matrix, params_solution[1], rtol=1e-7, atol=1e-7) - def test_incomplete_binding(self): + @data(TrainableFidelityQuantumKernel, TrainableFidelityStatevectorKernel) + def test_incomplete_binding(self, trainable_kernel_type): """Test if an exception is raised when not all training parameter are bound.""" # assign all parameters except the last one training_params = { self.training_parameters[i]: 0 for i in range(len(self.training_parameters) - 1) } - kernel = TrainableFidelityQuantumKernel( + kernel = trainable_kernel_type( feature_map=self.feature_map, training_parameters=self.training_parameters, ) @@ -171,9 +171,10 @@ def test_incomplete_binding(self): with self.assertRaises(QiskitMachineLearningError): kernel.evaluate(self.sample_train) - def test_properties(self): + @data(TrainableFidelityQuantumKernel, TrainableFidelityStatevectorKernel) + def test_properties(self, trainable_kernel_type): """Test properties of the trainable quantum kernel.""" - kernel = TrainableFidelityQuantumKernel( + kernel = trainable_kernel_type( feature_map=self.feature_map, training_parameters=self.training_parameters, )