• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qiskit-community / qiskit-machine-learning / 7982675612

19 Feb 2024 01:23PM CUT coverage: 92.567%. Remained the same
7982675612

push

github

web-flow
Fix tutorials to work with Qiskit 1.0 (#769)

1868 of 2018 relevant lines covered (92.57%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.9
/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py
1
# This code is part of a Qiskit project.
2
#
3
# (C) Copyright IBM 2021, 2023.
4
#
5
# This code is licensed under the Apache License, Version 2.0. You may
6
# obtain a copy of this license in the LICENSE.txt file in the root directory
7
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
#
9
# Any modifications or derivative works of this code must retain this
10
# copyright notice, and modified files need to carry a notice indicating
11
# that they have been altered from the originals.
12

13
"""Quantum Kernel Trainer"""
1✔
14
from __future__ import annotations
1✔
15

16
import copy
1✔
17
from functools import partial
1✔
18
from typing import Sequence
1✔
19

20
import numpy as np
1✔
21

22
from qiskit_algorithms.optimizers import Optimizer, SPSA, Minimizer
1✔
23
from qiskit_algorithms.utils import algorithm_globals
1✔
24
from qiskit_algorithms.variational_algorithm import VariationalResult
1✔
25
from qiskit_machine_learning.utils.loss_functions import KernelLoss, SVCLoss
1✔
26

27
from qiskit_machine_learning.kernels import TrainableKernel
1✔
28

29

30
class QuantumKernelTrainerResult(VariationalResult):
1✔
31
    """Quantum Kernel Trainer Result."""
32

33
    def __init__(self) -> None:
1✔
34
        super().__init__()
1✔
35
        self._quantum_kernel: TrainableKernel = None
1✔
36

37
    @property
1✔
38
    def quantum_kernel(self) -> TrainableKernel | None:
1✔
39
        """Return the optimized quantum kernel object."""
40
        return self._quantum_kernel
1✔
41

42
    @quantum_kernel.setter
1✔
43
    def quantum_kernel(self, quantum_kernel: TrainableKernel) -> None:
1✔
44
        self._quantum_kernel = quantum_kernel
1✔
45

46

47
class QuantumKernelTrainer:
1✔
48
    """
49
    Quantum Kernel Trainer.
50
    This class provides utility to train quantum kernel feature map parameters.
51

52
    **Example**
53

54
    .. code-block::
55

56
        # Create 2-qubit feature map
57
        qc = QuantumCircuit(2)
58

59
        # Vectors of input and trainable user parameters
60
        input_params = ParameterVector("x_par", 2)
61
        training_params = ParameterVector("θ_par", 2)
62

63
        # Create an initial rotation layer of trainable parameters
64
        for i, param in enumerate(training_params):
65
            qc.ry(param, qc.qubits[i])
66

67
        # Create a rotation layer of input parameters
68
        for i, param in enumerate(input_params):
69
            qc.rz(param, qc.qubits[i])
70

71
        quant_kernel = TrainableFidelityQuantumKernel(
72
            feature_map=qc,
73
            training_parameters=training_params,
74
        )
75

76
        loss_func = ...
77
        optimizer = ...
78
        initial_point = ...
79

80
        qk_trainer = QuantumKernelTrainer(
81
                                        quantum_kernel=quant_kernel,
82
                                        loss=loss_func,
83
                                        optimizer=optimizer,
84
                                        initial_point=initial_point,
85
                                        )
86
        qkt_results = qk_trainer.fit(X_train, y_train)
87
        optimized_kernel = qkt_results.quantum_kernel
88
    """
89

90
    def __init__(
1✔
91
        self,
92
        quantum_kernel: TrainableKernel,
93
        loss: str | KernelLoss | None = None,
94
        optimizer: Optimizer | Minimizer | None = None,
95
        initial_point: Sequence[float] | None = None,
96
    ):
97
        """
98
        Args:
99
            quantum_kernel: a trainable quantum kernel to be trained.
100
            loss: A loss function available via string is "svc_loss" which is the same as
101
                :class:`~qiskit_machine_learning.utils.loss_functions.SVCLoss`. If a string is
102
                passed as the loss function, then the underlying
103
                :class:`~qiskit_machine_learning.utils.loss_functions.SVCLoss` object will exhibit
104
                default behavior.
105
            optimizer: An instance of :class:`~qiskit_algorithms.optimizers.Optimizer` or a
106
                callable to be used in training. Refer to
107
                :class:`~qiskit_algorithms.optimizers.Minimizer` for more information on the
108
                callable protocol. Since no analytical gradient is defined for kernel loss
109
                functions, gradient-based optimizers are not recommended for training kernels. When
110
                `None` defaults to :class:`~qiskit_algorithms.optimizers.SPSA`.
111
            initial_point: Initial point from which the optimizer will begin.
112

113
        Raises:
114
            ValueError: unknown loss function.
115
        """
116
        # Class fields
117
        self._quantum_kernel = quantum_kernel
1✔
118
        self._initial_point = initial_point
1✔
119
        # call setter
120
        self.optimizer = optimizer
1✔
121

122
        # Loss setter
123
        self._set_loss(loss)
1✔
124

125
    @property
1✔
126
    def quantum_kernel(self) -> TrainableKernel:
1✔
127
        """Return the quantum kernel object."""
128
        return self._quantum_kernel
1✔
129

130
    @quantum_kernel.setter
1✔
131
    def quantum_kernel(self, quantum_kernel: TrainableKernel) -> None:
1✔
132
        """Set the quantum kernel."""
133
        self._quantum_kernel = quantum_kernel
×
134

135
    @property
1✔
136
    def loss(self) -> KernelLoss:
1✔
137
        """Return the loss object."""
138
        return self._loss
×
139

140
    @loss.setter
1✔
141
    def loss(self, loss: str | KernelLoss | None) -> None:
1✔
142
        """
143
        Set the loss.
144

145
        Args:
146
            loss: a loss function to set
147

148
        Raises:
149
            ValueError: Unknown loss function
150
        """
151
        self._set_loss(loss)
×
152

153
    @property
1✔
154
    def optimizer(self) -> Optimizer | Minimizer:
1✔
155
        """Return an optimizer to be used in training."""
156
        return self._optimizer
×
157

158
    @optimizer.setter
1✔
159
    def optimizer(self, optimizer: Optimizer | Minimizer | None) -> None:
1✔
160
        """Set the optimizer."""
161
        if optimizer is None:
1✔
162
            optimizer = SPSA()
1✔
163
        self._optimizer = optimizer
1✔
164

165
    @property
1✔
166
    def initial_point(self) -> Sequence[float] | None:
1✔
167
        """Return initial point"""
168
        return self._initial_point
×
169

170
    @initial_point.setter
1✔
171
    def initial_point(self, initial_point: Sequence[float] | None) -> None:
1✔
172
        """Set the initial point"""
173
        self._initial_point = initial_point
×
174

175
    def fit(
1✔
176
        self,
177
        data: np.ndarray,
178
        labels: np.ndarray,
179
    ) -> QuantumKernelTrainerResult:
180
        """
181
        Train the QuantumKernel by minimizing loss over the kernel parameters. The input
182
        quantum kernel will not be altered, and an optimized quantum kernel will be returned.
183

184
        Args:
185
            data (numpy.ndarray): ``(N, D)`` array of training data, where ``N`` is the
186
                              number of samples and ``D`` is the feature dimension
187
            labels (numpy.ndarray): ``(N, 1)`` array of target values for the training samples
188

189
        Returns:
190
            QuantumKernelTrainerResult: the results of kernel training
191

192
        Raises:
193
            ValueError: No trainable user parameters specified in quantum kernel
194
        """
195
        # Number of parameters to tune
196
        num_params = len(self._quantum_kernel.training_parameters)
1✔
197
        if num_params == 0:
1✔
198
            msg = "Quantum kernel cannot be fit because there are no user parameters specified."
1✔
199
            raise ValueError(msg)
1✔
200

201
        # Bind inputs to objective function
202
        output_kernel = copy.deepcopy(self._quantum_kernel)
1✔
203

204
        # Randomly initialize the initial point if one was not passed
205
        if self._initial_point is None:
1✔
206
            self._initial_point = algorithm_globals.random.random(num_params)
1✔
207

208
        # Perform kernel optimization
209
        loss_function = partial(
1✔
210
            self._loss.evaluate, quantum_kernel=self.quantum_kernel, data=data, labels=labels
211
        )
212
        if callable(self._optimizer):
1✔
213
            opt_results = self._optimizer(fun=loss_function, x0=self._initial_point)
1✔
214
        else:
215
            opt_results = self._optimizer.minimize(
1✔
216
                fun=loss_function,
217
                x0=self._initial_point,
218
            )
219

220
        # Return kernel training results
221
        result = QuantumKernelTrainerResult()
1✔
222
        result.optimizer_evals = opt_results.nfev
1✔
223
        result.optimal_value = opt_results.fun
1✔
224
        result.optimal_point = opt_results.x
1✔
225
        result.optimal_parameters = dict(zip(output_kernel.training_parameters, opt_results.x))
1✔
226

227
        # Return the QuantumKernel in optimized state
228
        output_kernel.assign_training_parameters(result.optimal_parameters)
1✔
229
        result.quantum_kernel = output_kernel
1✔
230

231
        return result
1✔
232

233
    def _set_loss(self, loss: str | KernelLoss | None) -> None:
1✔
234
        """Internal setter."""
235
        if loss is None:
1✔
236
            loss = SVCLoss()
1✔
237
        elif isinstance(loss, str):
1✔
238
            loss = self._str_to_loss(loss)
×
239

240
        self._loss = loss
1✔
241

242
    def _str_to_loss(self, loss_str: str) -> KernelLoss:
1✔
243
        """Function which maps strings to default KernelLoss objects."""
244
        if loss_str == "svc_loss":
×
245
            loss_obj = SVCLoss()
×
246
        else:
247
            raise ValueError(f"Unknown loss {loss_str}!")
×
248

249
        return loss_obj
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc