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