From 941ff1ea84b5ef1ef0292384f86e5336efe3a2f5 Mon Sep 17 00:00:00 2001 From: Emre Date: Tue, 13 Feb 2024 09:49:51 +0000 Subject: [PATCH 01/10] Added an option for num_circuits per job for kernels to fix #701 --- .../kernels/fidelity_quantum_kernel.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py index 24a373b09..005acb81e 100644 --- a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py +++ b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py @@ -207,24 +207,37 @@ def _get_symmetric_kernel_matrix( return kernel_matrix def _get_kernel_entries( - self, left_parameters: np.ndarray, right_parameters: np.ndarray + self, left_parameters: np.ndarray, right_parameters: np.ndarray, max_circuits_per_job: int = 300 ) -> Sequence[float]: """ Gets kernel entries by executing the underlying fidelity instance and getting the results - back from the async job. + back from the async job. The number of circuits per job is limited by the max_circuits_per_job parameter. """ num_circuits = left_parameters.shape[0] - if num_circuits != 0: + kernel_entries = [] + + # Determine the number of chunks needed + num_chunks = (num_circuits + max_circuits_per_job - 1) // max_circuits_per_job + + for i in range(num_chunks): + # Determine the range of indices for this chunk + start_idx = i * max_circuits_per_job + end_idx = min((i + 1) * max_circuits_per_job, num_circuits) + + # Extract the parameters for this chunk + chunk_left_parameters = left_parameters[start_idx:end_idx] + chunk_right_parameters = right_parameters[start_idx:end_idx] + + # Execute this chunk job = self._fidelity.run( - [self._feature_map] * num_circuits, - [self._feature_map] * num_circuits, - left_parameters, - right_parameters, + [self._feature_map] * (end_idx - start_idx), + [self._feature_map] * (end_idx - start_idx), + chunk_left_parameters, + chunk_right_parameters, ) - kernel_entries = job.result().fidelities - else: - # trivial case, only identical samples - kernel_entries = [] + + # Extend the kernel_entries list with the results from this chunk + kernel_entries.extend(job.result().fidelities) return kernel_entries def _is_trivial( From 145ec479e244b6ae8a87c1906252eaa02e3b73da Mon Sep 17 00:00:00 2001 From: Emre Date: Tue, 13 Feb 2024 11:36:44 +0000 Subject: [PATCH 02/10] Updated documentation and format the style. --- .../kernels/fidelity_quantum_kernel.py | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py index 005acb81e..aa81f74f1 100644 --- a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py +++ b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py @@ -46,6 +46,7 @@ def __init__( fidelity: BaseStateFidelity | None = None, enforce_psd: bool = True, evaluate_duplicates: str = "off_diagonal", + max_circuits_per_job: int = 300, ) -> None: """ Args: @@ -73,6 +74,8 @@ def __init__( - ``none`` when training the diagonal is set to `1` and if two identical samples are found in the dataset the corresponding matrix element is set to `1`. When inferring, matrix elements for identical samples are set to `1`. + max_circuits_per_job: Maximum number of circuits per job for the backend. Please + check the backend specifications. Use ``None`` for all entries per job. Default ``300``. Raises: ValueError: When unsupported value is passed to `evaluate_duplicates`. """ @@ -84,10 +87,10 @@ def __init__( f"Unsupported value passed as evaluate_duplicates: {evaluate_duplicates}" ) self._evaluate_duplicates = eval_duplicates - if fidelity is None: fidelity = ComputeUncompute(sampler=Sampler()) self._fidelity = fidelity + self.max_circuits_per_job = max_circuits_per_job def evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray | None = None) -> np.ndarray: x_vec, y_vec = self._validate_input(x_vec, y_vec) @@ -207,37 +210,45 @@ def _get_symmetric_kernel_matrix( return kernel_matrix def _get_kernel_entries( - self, left_parameters: np.ndarray, right_parameters: np.ndarray, max_circuits_per_job: int = 300 + self, left_parameters: np.ndarray, right_parameters: np.ndarray ) -> Sequence[float]: """ Gets kernel entries by executing the underlying fidelity instance and getting the results - back from the async job. The number of circuits per job is limited by the max_circuits_per_job parameter. + back from the async job. """ num_circuits = left_parameters.shape[0] kernel_entries = [] - - # Determine the number of chunks needed - num_chunks = (num_circuits + max_circuits_per_job - 1) // max_circuits_per_job - - for i in range(num_chunks): - # Determine the range of indices for this chunk - start_idx = i * max_circuits_per_job - end_idx = min((i + 1) * max_circuits_per_job, num_circuits) - - # Extract the parameters for this chunk - chunk_left_parameters = left_parameters[start_idx:end_idx] - chunk_right_parameters = right_parameters[start_idx:end_idx] - - # Execute this chunk - job = self._fidelity.run( - [self._feature_map] * (end_idx - start_idx), - [self._feature_map] * (end_idx - start_idx), - chunk_left_parameters, - chunk_right_parameters, - ) - - # Extend the kernel_entries list with the results from this chunk - kernel_entries.extend(job.result().fidelities) + # Check if it is trivial case, only identical samples + if num_circuits != 0: + if self.max_circuits_per_job is None: + job = self._fidelity.run( + [self._feature_map] * num_circuits, + [self._feature_map] * num_circuits, + left_parameters, + right_parameters, + ) + kernel_entries = job.result().fidelities + else: + # Determine the number of chunks needed + num_chunks = ( + num_circuits + self.max_circuits_per_job - 1 + ) // self.max_circuits_per_job + for i in range(num_chunks): + # Determine the range of indices for this chunk + start_idx = i * self.max_circuits_per_job + end_idx = min((i + 1) * self.max_circuits_per_job, num_circuits) + # Extract the parameters for this chunk + chunk_left_parameters = left_parameters[start_idx:end_idx] + chunk_right_parameters = right_parameters[start_idx:end_idx] + # Execute this chunk + job = self._fidelity.run( + [self._feature_map] * (end_idx - start_idx), + [self._feature_map] * (end_idx - start_idx), + chunk_left_parameters, + chunk_right_parameters, + ) + # Extend the kernel_entries list with the results from this chunk + kernel_entries.extend(job.result().fidelities) return kernel_entries def _is_trivial( From 6042e8a4881d508dde51ddee4997c975ce022b19 Mon Sep 17 00:00:00 2001 From: oscar-wallis Date: Fri, 16 Feb 2024 15:22:03 +0000 Subject: [PATCH 03/10] Removed deepcopy dependency in quantum_kernel_trainer.py --- .../kernels/algorithms/quantum_kernel_trainer.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py b/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py index 8dc172ba4..d6af61a4c 100644 --- a/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py +++ b/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2021, 2023. +# (C) Copyright IBM 2021, 2024. # # 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 @@ -13,7 +13,6 @@ """Quantum Kernel Trainer""" from __future__ import annotations -import copy from functools import partial from typing import Sequence @@ -198,13 +197,17 @@ def fit( msg = "Quantum kernel cannot be fit because there are no user parameters specified." raise ValueError(msg) - # Bind inputs to objective function - output_kernel = copy.deepcopy(self._quantum_kernel) - # Randomly initialize the initial point if one was not passed if self._initial_point is None: self._initial_point = algorithm_globals.random.random(num_params) + # Bind inputs to objective function + output_kernel = type(self._quantum_kernel)( + feature_map=self._quantum_kernel.feature_map, + training_parameters=self._quantum_kernel.training_parameters, + ) + output_kernel.assign_training_parameters(parameter_values=self.initial_point) + # Perform kernel optimization loss_function = partial( self._loss.evaluate, quantum_kernel=self.quantum_kernel, data=data, labels=labels From e304afa9b8e157a13cf93a378f579337513db8a7 Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 21 Feb 2024 10:00:19 +0000 Subject: [PATCH 04/10] Added release notes --- ...ndency-on-copy.deepcopy-13ecfc7d61716586.yaml | 15 +++++++++++++++ ...ix-max_circuits_per_job-d4558236e19ca565.yaml | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml create mode 100644 releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml diff --git a/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml b/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml new file mode 100644 index 000000000..d9a4ab491 --- /dev/null +++ b/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml @@ -0,0 +1,15 @@ +--- +issues: + - | + QKT deepcopies kernel internally, fails with runtime primitives #600 + On line 202, the quantum kernel trainer deepcopy, which is incompatible + with the runtime primitives, as they do not support deepcopy. If you try + to run it, a recursion error is raised. (this does not happen with the + Terra reference or Aer primitives) + +fixes: + - | + Removed quantum kernel trainers dependency on copy.deepcopy that was + throwing an error with real backends. Now initialise a new instance + of a class and assign parameters manually. As change is purely + internal, no new documentation required. diff --git a/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml b/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml new file mode 100644 index 000000000..f98400fcf --- /dev/null +++ b/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml @@ -0,0 +1,16 @@ +--- +issues: + - | + The FidelityQuantumKernel cannot be executed on the hardware for larger + problems. #701 + There is a problem with the way _get_kernel_entries handles the jobs sent + to hardware. max_circuits_per_job is half of the kernel size, which is + not suitable for the real hardware for larger problems. + +fixes: + - | + #701: Added a max_circuits_per_job parameter to fidelity quantum kernel used + in the case that if more circuits are submitted than the job limit for the + backend, the circuits are split up and ran through separate jobs. + Documentation was added and formatted for this. + From 6494cbdcf1c0dc7a96fd0a955502d2943adca5aa Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 21 Feb 2024 11:06:57 +0000 Subject: [PATCH 05/10] quick fix for spell test --- qiskit_machine_learning/kernels/fidelity_quantum_kernel.py | 2 +- ...trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml | 4 ++-- .../notes/fix-max_circuits_per_job-d4558236e19ca565.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py index aa81f74f1..db61592c8 100644 --- a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py +++ b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py @@ -1,6 +1,6 @@ # This code is part of a Qiskit project. # -# (C) Copyright IBM 2022, 2023. +# (C) Copyright IBM 2022, 2024. # # 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 diff --git a/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml b/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml index d9a4ab491..2a78aef33 100644 --- a/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml +++ b/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml @@ -1,7 +1,7 @@ --- issues: - | - QKT deepcopies kernel internally, fails with runtime primitives #600 + QKT deepcopy kernel internally, fails with runtime primitives #600 On line 202, the quantum kernel trainer deepcopy, which is incompatible with the runtime primitives, as they do not support deepcopy. If you try to run it, a recursion error is raised. (this does not happen with the @@ -10,6 +10,6 @@ issues: fixes: - | Removed quantum kernel trainers dependency on copy.deepcopy that was - throwing an error with real backends. Now initialise a new instance + throwing an error with real backends. Now create a new instance of a class and assign parameters manually. As change is purely internal, no new documentation required. diff --git a/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml b/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml index f98400fcf..e20b2b07f 100644 --- a/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml +++ b/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml @@ -5,11 +5,11 @@ issues: problems. #701 There is a problem with the way _get_kernel_entries handles the jobs sent to hardware. max_circuits_per_job is half of the kernel size, which is - not suitable for the real hardware for larger problems. + not suitable for the hardware for larger problems. fixes: - | - #701: Added a max_circuits_per_job parameter to fidelity quantum kernel used + Added a max_circuits_per_job parameter to fidelity quantum kernel used in the case that if more circuits are submitted than the job limit for the backend, the circuits are split up and ran through separate jobs. Documentation was added and formatted for this. From ce85ad553bd300df5c620868b9f83a9d40c77641 Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 21 Feb 2024 17:38:07 +0000 Subject: [PATCH 06/10] Added unit tests for max_circuits_per_job --- .../kernels/fidelity_quantum_kernel.py | 9 +++++++-- ...600-deepcopy-dependency-e6eda2e5b986c1be.yaml | 9 +++++++++ ...ndency-on-copy.deepcopy-13ecfc7d61716586.yaml | 15 --------------- ...ix-max_circuits_per_job-d4558236e19ca565.yaml | 16 ---------------- test/kernels/test_fidelity_qkernel.py | 16 ++++++++++++++++ 5 files changed, 32 insertions(+), 33 deletions(-) create mode 100644 releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml delete mode 100644 releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml delete mode 100644 releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml diff --git a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py index db61592c8..fc72b9635 100644 --- a/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py +++ b/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py @@ -46,7 +46,7 @@ def __init__( fidelity: BaseStateFidelity | None = None, enforce_psd: bool = True, evaluate_duplicates: str = "off_diagonal", - max_circuits_per_job: int = 300, + max_circuits_per_job: int = None, ) -> None: """ Args: @@ -75,7 +75,7 @@ def __init__( are found in the dataset the corresponding matrix element is set to `1`. When inferring, matrix elements for identical samples are set to `1`. max_circuits_per_job: Maximum number of circuits per job for the backend. Please - check the backend specifications. Use ``None`` for all entries per job. Default ``300``. + check the backend specifications. Use ``None`` for all entries per job. Default ``None``. Raises: ValueError: When unsupported value is passed to `evaluate_duplicates`. """ @@ -90,6 +90,11 @@ def __init__( if fidelity is None: fidelity = ComputeUncompute(sampler=Sampler()) self._fidelity = fidelity + if max_circuits_per_job is not None: + if max_circuits_per_job < 1: + raise ValueError( + f"Unsupported value passed as max_circuits_per_job: {max_circuits_per_job}" + ) self.max_circuits_per_job = max_circuits_per_job def evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray | None = None) -> np.ndarray: diff --git a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml new file mode 100644 index 000000000..25e51351c --- /dev/null +++ b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + #701 Added a max_circuits_per_job parameter to fidelity quantum kernel used + in the case that if more circuits are submitted than the job limit for the + backend, the circuits are split up and ran through separate jobs. + #600 Removed quantum kernel trainers dependency on copy.deepcopy that was + throwing an error with real backends. Now, it creates a new instance + of the class and assign parameters manually. \ No newline at end of file diff --git a/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml b/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml deleted file mode 100644 index 2a78aef33..000000000 --- a/releasenotes/notes/fix-Removed-quantum-kernel-trainers-dependency-on-copy.deepcopy-13ecfc7d61716586.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -issues: - - | - QKT deepcopy kernel internally, fails with runtime primitives #600 - On line 202, the quantum kernel trainer deepcopy, which is incompatible - with the runtime primitives, as they do not support deepcopy. If you try - to run it, a recursion error is raised. (this does not happen with the - Terra reference or Aer primitives) - -fixes: - - | - Removed quantum kernel trainers dependency on copy.deepcopy that was - throwing an error with real backends. Now create a new instance - of a class and assign parameters manually. As change is purely - internal, no new documentation required. diff --git a/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml b/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml deleted file mode 100644 index e20b2b07f..000000000 --- a/releasenotes/notes/fix-max_circuits_per_job-d4558236e19ca565.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -issues: - - | - The FidelityQuantumKernel cannot be executed on the hardware for larger - problems. #701 - There is a problem with the way _get_kernel_entries handles the jobs sent - to hardware. max_circuits_per_job is half of the kernel size, which is - not suitable for the hardware for larger problems. - -fixes: - - | - Added a max_circuits_per_job parameter to fidelity quantum kernel used - in the case that if more circuits are submitted than the job limit for the - backend, the circuits are split up and ran through separate jobs. - Documentation was added and formatted for this. - diff --git a/test/kernels/test_fidelity_qkernel.py b/test/kernels/test_fidelity_qkernel.py index ffe0f56a5..e4a4964b4 100644 --- a/test/kernels/test_fidelity_qkernel.py +++ b/test/kernels/test_fidelity_qkernel.py @@ -106,10 +106,26 @@ def test_defaults(self): self.assertGreaterEqual(score, 0.5) + def test_max_circuits_per_job(self): + """Test max_circuits_per_job parameters.""" + kernel_all = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=None) + kernel_matrix_all = kernel_all.evaluate(x_vec=self.sample_train) + + kernel_more = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=20) + kernel_matrix_more = kernel_more.evaluate(x_vec=self.sample_train) + + kernel_1 = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=1) + kernel_matrix_1 = kernel_1.evaluate(x_vec=self.sample_train) + + np.testing.assert_equal(kernel_matrix_all, kernel_matrix_more) + np.testing.assert_equal(kernel_matrix_all, kernel_matrix_1) + def test_exceptions(self): """Test quantum kernel raises exceptions and warnings.""" with self.assertRaises(ValueError, msg="Unsupported value of 'evaluate_duplicates'."): _ = FidelityQuantumKernel(evaluate_duplicates="wrong") + with self.assertRaises(ValueError, msg="Unsupported value of 'max_circuits_per_job'."): + _ = FidelityQuantumKernel(max_circuits_per_job=-1) @idata( # params, fidelity, feature map, enforce_psd, duplicate From 9c00c7ab0d5dcb2fd005688a5fac806d1fec65ac Mon Sep 17 00:00:00 2001 From: "M. Emre Sahin" <40424147+OkuyanBoga@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:48:35 +0000 Subject: [PATCH 07/10] Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml Small release note bugfix --- ...job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml index 25e51351c..af9ecd8b9 100644 --- a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml +++ b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml @@ -1,9 +1,10 @@ --- fixes: - | - #701 Added a max_circuits_per_job parameter to fidelity quantum kernel used + Added a max_circuits_per_job parameter to the fidelity quantum kernel used in the case that if more circuits are submitted than the job limit for the - backend, the circuits are split up and ran through separate jobs. - #600 Removed quantum kernel trainers dependency on copy.deepcopy that was + backend, the circuits are split up and run through separate jobs. + - | + Removed quantum kernel trainers dependency on copy.deepcopy that was throwing an error with real backends. Now, it creates a new instance - of the class and assign parameters manually. \ No newline at end of file + of the class and assigns parameters manually. From ff202fe5ca4948d0839ec88d016ec1d50ab490c9 Mon Sep 17 00:00:00 2001 From: "M. Emre Sahin" <40424147+OkuyanBoga@users.noreply.github.com> Date: Wed, 21 Feb 2024 20:56:44 +0000 Subject: [PATCH 08/10] Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml --- ..._job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml index af9ecd8b9..ed0847af0 100644 --- a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml +++ b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml @@ -1,10 +1,10 @@ --- fixes: - | - Added a max_circuits_per_job parameter to the fidelity quantum kernel used + Added a `max_circuits_per_job` parameter to the :class:`.FidelityQuantumKernel` used in the case that if more circuits are submitted than the job limit for the backend, the circuits are split up and run through separate jobs. - | - Removed quantum kernel trainers dependency on copy.deepcopy that was - throwing an error with real backends. Now, it creates a new instance - of the class and assigns parameters manually. + Removed :class:`.QuantumKernelTrainer`dependency on `copy.deepcopy` that was + throwing an error with real backends. Now, it creates a new `instance` + of the :class:`.QuantumKernelTrainer` and assigns parameters manually. From c330f793293cf1c7f353c39369dc7534e0a07eed Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 21 Feb 2024 21:10:10 +0000 Subject: [PATCH 09/10] Minor modifications for the unit test --- ...-deepcopy-dependency-e6eda2e5b986c1be.yaml | 2 +- test/kernels/test_fidelity_qkernel.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml index ed0847af0..018a0c136 100644 --- a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml +++ b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml @@ -5,6 +5,6 @@ fixes: in the case that if more circuits are submitted than the job limit for the backend, the circuits are split up and run through separate jobs. - | - Removed :class:`.QuantumKernelTrainer`dependency on `copy.deepcopy` that was + Removed :class:`.QuantumKernelTrainer` dependency on `copy.deepcopy` that was throwing an error with real backends. Now, it creates a new `instance` of the :class:`.QuantumKernelTrainer` and assigns parameters manually. diff --git a/test/kernels/test_fidelity_qkernel.py b/test/kernels/test_fidelity_qkernel.py index e4a4964b4..6a132f14e 100644 --- a/test/kernels/test_fidelity_qkernel.py +++ b/test/kernels/test_fidelity_qkernel.py @@ -110,15 +110,16 @@ def test_max_circuits_per_job(self): """Test max_circuits_per_job parameters.""" kernel_all = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=None) kernel_matrix_all = kernel_all.evaluate(x_vec=self.sample_train) - - kernel_more = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=20) - kernel_matrix_more = kernel_more.evaluate(x_vec=self.sample_train) - - kernel_1 = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=1) - kernel_matrix_1 = kernel_1.evaluate(x_vec=self.sample_train) - - np.testing.assert_equal(kernel_matrix_all, kernel_matrix_more) - np.testing.assert_equal(kernel_matrix_all, kernel_matrix_1) + with self.subTest("Check when max_circuits_per_job > left_parameters"): + kernel_more = FidelityQuantumKernel( + feature_map=self.feature_map, max_circuits_per_job=20 + ) + kernel_matrix_more = kernel_more.evaluate(x_vec=self.sample_train) + np.testing.assert_equal(kernel_matrix_all, kernel_matrix_more) + with self.subTest("Check when max_circuits_per_job = 1"): + kernel_1 = FidelityQuantumKernel(feature_map=self.feature_map, max_circuits_per_job=1) + kernel_matrix_1 = kernel_1.evaluate(x_vec=self.sample_train) + np.testing.assert_equal(kernel_matrix_all, kernel_matrix_1) def test_exceptions(self): """Test quantum kernel raises exceptions and warnings.""" From 91ea7475a559e389bf2fa724f756b47345bf38b1 Mon Sep 17 00:00:00 2001 From: Emre Date: Tue, 27 Feb 2024 10:01:51 +0000 Subject: [PATCH 10/10] Removed copy of TrainableKernel --- .../algorithms/quantum_kernel_trainer.py | 20 ++++++++----------- ...-deepcopy-dependency-e6eda2e5b986c1be.yaml | 8 ++++++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py b/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py index d6af61a4c..4d272b2ac 100644 --- a/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py +++ b/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py @@ -95,7 +95,8 @@ def __init__( ): """ Args: - quantum_kernel: a trainable quantum kernel to be trained. + quantum_kernel: a trainable quantum kernel to be trained. The + :attr:`~.TrainableKernel.parameter_values` will be modified in place after the training. loss: A loss function available via string is "svc_loss" which is the same as :class:`~qiskit_machine_learning.utils.loss_functions.SVCLoss`. If a string is passed as the loss function, then the underlying @@ -178,7 +179,7 @@ def fit( ) -> QuantumKernelTrainerResult: """ Train the QuantumKernel by minimizing loss over the kernel parameters. The input - quantum kernel will not be altered, and an optimized quantum kernel will be returned. + quantum kernel will be altered. Args: data (numpy.ndarray): ``(N, D)`` array of training data, where ``N`` is the @@ -201,13 +202,6 @@ def fit( if self._initial_point is None: self._initial_point = algorithm_globals.random.random(num_params) - # Bind inputs to objective function - output_kernel = type(self._quantum_kernel)( - feature_map=self._quantum_kernel.feature_map, - training_parameters=self._quantum_kernel.training_parameters, - ) - output_kernel.assign_training_parameters(parameter_values=self.initial_point) - # Perform kernel optimization loss_function = partial( self._loss.evaluate, quantum_kernel=self.quantum_kernel, data=data, labels=labels @@ -225,11 +219,13 @@ def fit( result.optimizer_evals = opt_results.nfev result.optimal_value = opt_results.fun result.optimal_point = opt_results.x - result.optimal_parameters = dict(zip(output_kernel.training_parameters, opt_results.x)) + result.optimal_parameters = dict( + zip(self.quantum_kernel.training_parameters, opt_results.x) + ) # Return the QuantumKernel in optimized state - output_kernel.assign_training_parameters(result.optimal_parameters) - result.quantum_kernel = output_kernel + self.quantum_kernel.assign_training_parameters(result.optimal_parameters) + result.quantum_kernel = self.quantum_kernel return result diff --git a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml index 018a0c136..a9b504fdf 100644 --- a/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml +++ b/releasenotes/notes/fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml @@ -6,5 +6,9 @@ fixes: backend, the circuits are split up and run through separate jobs. - | Removed :class:`.QuantumKernelTrainer` dependency on `copy.deepcopy` that was - throwing an error with real backends. Now, it creates a new `instance` - of the :class:`.QuantumKernelTrainer` and assigns parameters manually. + throwing an error with real backends. Now, it modifies the :class:`.TrainableKernel` + in place. If you would like to use the initial kernel, please call + :meth:`~.TrainableKernel.assign_training_parameters` of the :class:`~.TrainableKernel` + using the :attr:`~.QuantumKernelTrainer.initial_point` attribute of + :class:`~.QuantumKernelTrainer`. +