1from collections.abc import Sequence
2
3import numpy as np
4from qiskit.circuit import Parameter, QuantumCircuit
5from qiskit.primitives import BaseSampler, BaseSamplerV1, BaseSamplerV2
6from qiskit.providers import Options
7from qiskit.transpiler.passmanager import PassManager
8from qiskit_machine_learning.kernels import FidelityQuantumKernel, TrainableFidelityQuantumKernel
9from qiskit_machine_learning.kernels.algorithms import QuantumKernelTrainer
10from qiskit_machine_learning.state_fidelities import BaseStateFidelity, ComputeUncompute
11
12from qlauncher.base.base import Algorithm, Result
13from qlauncher.problems.other.tabular_ml import TabularML
14from qlauncher.routines.qiskit.backends.gate_circuit_backend import GateCircuitBackend
15
16
[docs]
17class ComputeUncomputeCustom(ComputeUncompute):
18 """
19 This is just :class:`qiskit_machine_learning.state_fidelities.ComputeUncompute` that checks
20 if a sampler is an instance of BaseSamplerV1 instead of BaseSampler.
21 The reason was that classes basing BaseSampler were getting isinstance(cls(),BaseSampler) == False
22 probably because of some qiskit shenanigans.
23 """
24
25 def __init__(
26 self,
27 sampler: BaseSampler | BaseSamplerV2,
28 *,
29 options: Options | None = None,
30 local: bool = False,
31 pass_manager: PassManager | None = None,
32 ) -> None:
33 if (not isinstance(sampler, BaseSamplerV1)) and (not isinstance(sampler, BaseSamplerV2)):
34 raise ValueError(f'The sampler should be an instance of BaseSampler or BaseSamplerV2, but got {type(sampler)}')
35 self._sampler: BaseSamplerV1 | BaseSamplerV2 = sampler
36 self._pass_manager = pass_manager
37 self._local = local
38 self._default_options = Options()
39 if options is not None:
40 self._default_options.update_options(**options)
41 BaseStateFidelity.__init__(self) # pylint: disable=non-parent-init-called
42
43
[docs]
44class TrainQSVCKernel(Algorithm[TabularML, GateCircuitBackend]):
45 """
46 Train a quantum kernel with additional parameters to be optimized.
47 The kernel will be optimized to provide maximum accuracy with a support vector classifier on the provided dataset.
48 If no trainable parameters are provided, the algorithm will return
49 a :class:`qiskit_machine_learning.kernels.FidelityQuantumKernel` kernel with a sampler assigned to the provided backend.
50 Otherwise an instance of :class:`qiskit_machine_learning.kernels.TrainableFidelityQuantumKernel` with optimal
51 parameters and a sampler assigned to the provided backend will be returned.
52
53 Args:
54 kernel_circuit(QuantumCircuit): A parametrizable quantum circuit. The measurements will be used to produce kernel output.
55 trainable_params(Sequence[Parameter] | None, optional):
56 The parameters to be optimized during training. If None no optimization will be done. Defaults to None.
57 """
58
59 _algorithm_format = 'tabular_ml'
60
61 def __init__(self, kernel_circuit: QuantumCircuit, trainable_params: Sequence[Parameter] | None = None, **alg_kwargs) -> None:
62 super().__init__(**alg_kwargs)
63 self.kernel = kernel_circuit
64 self.trainable = trainable_params if trainable_params is not None else []
65
[docs]
66 def run(self, problem: TabularML, backend: GateCircuitBackend) -> Result:
67 X = problem.X
68 y = problem.y
69
70 if not isinstance(X, np.ndarray):
71 raise ValueError(f'X is not of type np.ndarray: received {type(X)}')
72
73 if not isinstance(y, np.ndarray):
74 raise ValueError(f'y is not of type np.ndarray: received {type(y)}')
75
76 if isinstance(backend, GateCircuitBackend):
77 sampler = backend.samplerV1
78 else:
79 raise ValueError(f'The accepted backends are QiskitBackend and CirqBackend, got {type(backend)}')
80
81 if len(self.trainable) == 0:
82 return Result(
83 best_bitstring='',
84 best_energy=1,
85 most_common_bitstring='',
86 most_common_bitstring_energy=0,
87 distribution={},
88 energies={},
89 num_of_samples=0,
90 average_energy=0,
91 energy_std=0,
92 result=FidelityQuantumKernel(feature_map=self.kernel, fidelity=ComputeUncomputeCustom(sampler=sampler)),
93 )
94 trainable_kernel = TrainableFidelityQuantumKernel(
95 feature_map=self.kernel, fidelity=ComputeUncomputeCustom(sampler=sampler), training_parameters=self.trainable
96 )
97 kernel_trainer = QuantumKernelTrainer(trainable_kernel)
98 kernel_trainer.fit(X, y)
99
100 return Result(
101 best_bitstring='',
102 best_energy=1,
103 most_common_bitstring='',
104 most_common_bitstring_energy=0,
105 distribution={},
106 energies={},
107 num_of_samples=0,
108 average_energy=0,
109 energy_std=0,
110 result=kernel_trainer.quantum_kernel,
111 )