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

qiskit-community / qiskit-machine-learning / 8545395904

03 Apr 2024 08:55PM CUT coverage: 92.711% (+0.08%) from 92.636%
8545395904

Pull #793

github

web-flow
Merge 240d02fb3 into 97513d377
Pull Request #793: Patches einsum dimensionality in `torch_connector` - #716

25 of 25 new or added lines in 1 file covered. (100.0%)

10 existing lines in 1 file now uncovered.

1908 of 2058 relevant lines covered (92.71%)

0.93 hits per line

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

86.59
/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py
1
# This code is part of a Qiskit project.
2
#
3
# (C) Copyright IBM 2021, 2024.
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
from functools import partial
1✔
17
from typing import Sequence
1✔
18

19
import numpy as np
1✔
20

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

26
from qiskit_machine_learning.kernels import TrainableKernel
1✔
27

28

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

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

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

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

45

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

51
    **Example**
52

53
    .. code-block::
54

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

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

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

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

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

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

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

89
    def __init__(
1✔
90
        self,
91
        quantum_kernel: TrainableKernel,
92
        loss: str | KernelLoss | None = None,
93
        optimizer: Optimizer | Minimizer | None = None,
94
        initial_point: Sequence[float] | None = None,
95
    ):
96
        """
97
        Args:
98
            quantum_kernel: a trainable quantum kernel to be trained. The
99
                :attr:`~.TrainableKernel.parameter_values` will be modified in place after the training.
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 be altered.
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
        # Randomly initialize the initial point if one was not passed
202
        if self._initial_point is None:
1✔
203
            self._initial_point = algorithm_globals.random.random(num_params)
1✔
204

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

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

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

230
        return result
1✔
231

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

239
        self._loss = loss
1✔
240

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

248
        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