Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The FidelityQuantumKernel cannot be executed on the hardware for larger problems. #701

Closed
OkuyanBoga opened this issue Oct 23, 2023 · 3 comments
Labels
type: bug 🐞 Something isn't working

Comments

@OkuyanBoga
Copy link
Collaborator

Environment

  • Qiskit Machine Learning version: 0.6.1
  • Python version: 3.10.8
  • Operating system: Linux

Using IBM Lab:

Software Version
qiskit 0.44.1
qiskit-terra 0.25.1
qiskit_aer 0.12.2
qiskit_machine_learning 0.6.1
qiskit_ibm_provider 0.6.3
qiskit_ibm_runtime 0.11.3
Python version 3.10.8
Python compiler GCC 10.4.0
Python build main, Nov 22 2022 08:26:04
OS Linux
CPUs 8
Memory (Gb) 31.142810821533203
Mon Oct 23 18:57:54 2023 UTC

What is happening?

Hi,
I think there is a problem with the way _get_kernel_entries handles the jobs sent to hardware.

The error is:

3242 Circuit count exceeded.

I think the problem stems from

def _get_kernel_entries(
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.
"""
num_circuits = left_parameters.shape[0]
if num_circuits != 0:
job = self._fidelity.run(

Also, it is really hard to train quantum fidelity kernel on the hardware with the sessions:
IBMRuntimeError: 'Failed to run program: \'400 Client Error: Bad Request for url: https://api.quantum-computing.ibm.com/runtime/jobs. {"errors":[{"code":1217,"message":"Session has been closed.","solution":"Reduce time between submitting subsequent jobs in a session.","more_info":"https://docs.quantum-computing.ibm.com/errors"}]}\''

How can we reproduce the issue?

from pylab import cm
from sklearn import metrics
import numpy as np
import matplotlib
import matplotlib.pyplot as plt

# Qiskit imports
from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.visualization import circuit_drawer
from qiskit.algorithms.optimizers import SPSA
from qiskit.circuit.library import ZZFeatureMap
from qiskit_machine_learning.kernels import TrainableFidelityQuantumKernel
from qiskit_machine_learning.kernels.algorithms import QuantumKernelTrainer
from qiskit_machine_learning.algorithms import QSVC
from qiskit_machine_learning.datasets import ad_hoc_data


class QKTCallback:
    """Callback wrapper class."""

    def __init__(self) -> None:
        self._data = [[] for i in range(5)]

    def callback(self, x0, x1=None, x2=None, x3=None, x4=None):
        """
        Args:
            x0: number of function evaluations
            x1: the parameters
            x2: the function value
            x3: the stepsize
            x4: whether the step was accepted
        """
        self._data[0].append(x0)
        self._data[1].append(x1)
        self._data[2].append(x2)
        self._data[3].append(x3)
        self._data[4].append(x4)

    def get_callback_data(self):
        return self._data

    def clear_callback_data(self):
        self._data = [[] for i in range(5)]

adhoc_dimension = 2
X_train, y_train, X_test, y_test, adhoc_total = ad_hoc_data(
    training_size=40,
    test_size=5,
    n=adhoc_dimension,
    gap=0.3,
    plot_data=False,
    one_hot=False,
    include_sample_total=True,
)

# Create a rotational layer to train. We will rotate each qubit the same amount.
training_params = ParameterVector("θ", 1)
fm0 = QuantumCircuit(2)
fm0.ry(training_params[0], 0)
fm0.ry(training_params[0], 1)

# Use ZZFeatureMap to represent input data
fm1 = ZZFeatureMap(2)

# Create the feature map, composed of our two circuits
fm = fm0.compose(fm1)

print(circuit_drawer(fm))
print(f"Trainable parameters: {training_params}")

# Instantiate quantum kernel
quant_kernel = TrainableFidelityQuantumKernel(feature_map=fm, training_parameters=training_params)

# Set up the optimizer
cb_qkt = QKTCallback()
spsa_opt = SPSA(maxiter=10, callback=cb_qkt.callback, learning_rate=0.05, perturbation=0.05)

# Instantiate a quantum kernel trainer.
qkt = QuantumKernelTrainer(
    quantum_kernel=quant_kernel, loss="svc_loss", optimizer=spsa_opt, initial_point=[np.pi / 2]
)

## Train using simulator
# Train the kernel using QKT directly
qka_results = qkt.fit(X_train, y_train)
optimized_kernel = qka_results.quantum_kernel
print(qka_results)
# Use QSVC for classification
qsvc = QSVC(quantum_kernel=optimized_kernel)

# Fit the QSVC
qsvc.fit(X_train, y_train)

# Predict the labels
labels_test = qsvc.predict(X_test)

# Evalaute the test accuracy
accuracy_test = metrics.balanced_accuracy_score(y_true=y_test, y_pred=labels_test)
print(f"accuracy test: {accuracy_test}")

## HW Settings
from qiskit_ibm_runtime import QiskitRuntimeService, Session, Estimator, Options, Sampler
from qiskit.algorithms.state_fidelities import ComputeUncompute
service = QiskitRuntimeService()

backend = service.get_backend('ibm_lagos')

options = Options()
options.optimization_level = 0
options.resilience_level = 0
options.execution.shots = 100
options.initial_layout = [1,3,0,2,4,5,6]

## Train using HW
with Session(service=service, backend=backend) as session:
    sampler = Sampler(options=options)
    fidelity = ComputeUncompute(sampler=sampler, local=True)
    qk = TrainableFidelityQuantumKernel(feature_map=fm, training_parameters=training_params, fidelity=fidelity)
    qkt = QuantumKernelTrainer(quantum_kernel=qk, loss="svc_loss", optimizer=spsa_opt, initial_point=[np.pi / 2])
    qka_results = qkt.fit(X_train, y_train)
    optimized_kernel = qka_results.quantum_kernel
    print(qka_results)
    
    # Use QSVC for classification
    qsvc = QSVC(quantum_kernel=optimized_kernel)

    # Fit the QSVC
    qsvc.fit(X_train, y_train)

What should happen?

Traceback (most recent call last):
  Cell In[11], line 6
    qka_results = qkt.fit(X_train, y_train)
  File /opt/conda/lib/python3.10/site-packages/qiskit_machine_learning/kernels/algorithms/quantum_kernel_trainer.py:215 in fit
    opt_results = self._optimizer.minimize(
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/optimizers/spsa.py:558 in minimize
    fx_estimate, update = self._compute_update(fun, x, k, next(eps), lse_solver)
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/optimizers/spsa.py:489 in _compute_update
    value, gradient, hessian = self._point_estimate(loss, x, eps, num_samples)
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/optimizers/spsa.py:466 in _point_estimate
    value_sample, gradient_sample, hessian_sample = self._point_sample(
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/optimizers/spsa.py:427 in _point_sample
    values = _batch_evaluate(loss, points, self._max_evals_grouped)
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/optimizers/spsa.py:728 in _batch_evaluate
    return [
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/optimizers/spsa.py:729 in <listcomp>
    function(*point) if isinstance(point, tuple) else function(point) for point in points
  File /opt/conda/lib/python3.10/site-packages/qiskit_machine_learning/utils/loss_functions/kernel_loss_functions.py:109 in evaluate
    kmatrix = quantum_kernel.evaluate(np.array(data))
  File /opt/conda/lib/python3.10/site-packages/qiskit_machine_learning/kernels/trainable_fidelity_quantum_kernel.py:112 in evaluate
    return super().evaluate(x_vec, y_vec)
  File /opt/conda/lib/python3.10/site-packages/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py:105 in evaluate
    kernel_matrix = self._get_symmetric_kernel_matrix(
  File /opt/conda/lib/python3.10/site-packages/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py:196 in _get_symmetric_kernel_matrix
    kernel_entries = self._get_kernel_entries(left_parameters, right_parameters)
  File /opt/conda/lib/python3.10/site-packages/qiskit_machine_learning/kernels/fidelity_quantum_kernel.py:218 in _get_kernel_entries
    kernel_entries = np.real(job.result().fidelities)
  File /opt/conda/lib/python3.10/site-packages/qiskit/primitives/primitive_job.py:55 in result
    return self._future.result()
  File /opt/conda/lib/python3.10/concurrent/futures/_base.py:458 in result
    return self.__get_result()
  File /opt/conda/lib/python3.10/concurrent/futures/_base.py:403 in __get_result
    raise self._exception
  File /opt/conda/lib/python3.10/concurrent/futures/thread.py:58 in run
    result = self.fn(*self.args, **self.kwargs)
  File /opt/conda/lib/python3.10/site-packages/qiskit/algorithms/state_fidelities/compute_uncompute.py:158 in _run
    job = self._sampler.run(circuits=circuits, parameter_values=values, **opts.__dict__)
  File /opt/conda/lib/python3.10/site-packages/qiskit_ibm_runtime/sampler.py:122 in run
    return super().run(
  File /opt/conda/lib/python3.10/site-packages/qiskit/primitives/base/base_sampler.py:147 in run
    return self._run(
  File /opt/conda/lib/python3.10/site-packages/qiskit_ibm_runtime/sampler.py:152 in _run
    return self._run_primitive(
  File /opt/conda/lib/python3.10/site-packages/qiskit_ibm_runtime/base_primitive.py:183 in _run_primitive
    raise IBMInputValueError(
IBMInputValueError: 'Number of circuits, 3160 exceeds the maximum for this backend, 900)'

Any suggestions?

I made a workaround and I can submit a PR regarding this.:

import numpy as np
from typing import Sequence

class ModifiedTrainableFidelityQuantumKernel(TrainableFidelityQuantumKernel):

    def _get_kernel_entries(
        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. The number of circuits per job is limited by the max_circuits_per_job parameter.
        """
        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)
        
        return kernel_entries
@OkuyanBoga OkuyanBoga changed the title FidelityQuantumKernel cannot be run in the hardware for larger problems. The FidelityQuantumKernel cannot be executed on the hardware for larger problems. Oct 23, 2023
@woodsp-ibm
Copy link
Member

woodsp-ibm commented Oct 25, 2023

Had you tried raising an issue against IBM Runtime https://github.com/Qiskit/qiskit-ibm-runtime/issues For the defined Sampler interface there is no common notion of any max circuits limit - the only limitation, as such, to calling run, as its defined, would be machine memory to hold all the circuits. I would have expected this as such to work and internally if for some reason it needed to split them to smaller batches for some internal limits it (the Sampler) should do that itself.

@oscar-wallis
Copy link
Collaborator

@OkuyanBoga can this issue be closed now?

@OkuyanBoga
Copy link
Collaborator Author

I believe Extended sessions and #772 will solve the issue.

OkuyanBoga added a commit that referenced this issue Feb 29, 2024
…ntum kernel trainer fixing #701 and #600 (#772)

* Added an option for num_circuits per job for kernels to fix #701

* Updated documentation and format the style.

* Removed deepcopy dependency in quantum_kernel_trainer.py

* Added release notes

* quick fix for spell test

* Added unit tests for max_circuits_per_job

* Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml

Small release note bugfix

* Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml

* Minor modifications for the unit test

* Removed copy of TrainableKernel

---------

Co-authored-by: oscar-wallis <[email protected]>
mergify bot pushed a commit that referenced this issue Feb 29, 2024
…ntum kernel trainer fixing #701 and #600 (#772)

* Added an option for num_circuits per job for kernels to fix #701

* Updated documentation and format the style.

* Removed deepcopy dependency in quantum_kernel_trainer.py

* Added release notes

* quick fix for spell test

* Added unit tests for max_circuits_per_job

* Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml

Small release note bugfix

* Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml

* Minor modifications for the unit test

* Removed copy of TrainableKernel

---------

Co-authored-by: oscar-wallis <[email protected]>
(cherry picked from commit 2f49e9e)
adekusar-drl pushed a commit that referenced this issue Feb 29, 2024
…ntum kernel trainer fixing #701 and #600 (#772) (#780)

* Added an option for num_circuits per job for kernels to fix #701

* Updated documentation and format the style.

* Removed deepcopy dependency in quantum_kernel_trainer.py

* Added release notes

* quick fix for spell test

* Added unit tests for max_circuits_per_job

* Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml

Small release note bugfix

* Update fix-701-max_circuits_per_job-and-600-deepcopy-dependency-e6eda2e5b986c1be.yaml

* Minor modifications for the unit test

* Removed copy of TrainableKernel

---------

Co-authored-by: oscar-wallis <[email protected]>
(cherry picked from commit 2f49e9e)

Co-authored-by: M. Emre Sahin <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug 🐞 Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants