qiskit-community / qiskit-machine-learning

Quantum Machine Learning
https://qiskit-community.github.io/qiskit-machine-learning/
Apache License 2.0
695 stars 328 forks source link

SamplerQNN: TypeError: run() takes 2 positional arguments but 3 were given #839

Closed xaviervasques closed 2 weeks ago

xaviervasques commented 1 month ago

Environment

What is happening?

Hello,

I am trying to run a SamplerQNN but since new version of qiskit I am not able to run it on real hardware (on local machine it works).

I have the following code that I run with my local machine and that works perfectly:

# Import necessary libraries
import warnings
import numpy as np
from sklearn.datasets import load_iris
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score, KFold
from sklearn.preprocessing import LabelEncoder

from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.primitives import Sampler
from qiskit_machine_learning.neural_networks import SamplerQNN
from qiskit_machine_learning.algorithms.classifiers import NeuralNetworkClassifier
from qiskit_algorithms.optimizers import COBYLA

# Configure the logging levels and suppress warnings
warnings.filterwarnings("ignore")

# Set random seed for reproducibility
np.random.seed(42)

# Parameters
feature_dimensions = [2]  # We will use 2 features for binary classification
number_classes = 2  # We'll work with binary classification

# Load a standard dataset (Iris)
iris = load_iris()
X, y = iris.data, iris.target

# Filter to keep only two classes for binary classification
binary_class_indices = y < 2
X = X[binary_class_indices]
y = y[binary_class_indices]

# Use only the first 2 features (reduce the dimensionality)
X = X[:, :2]

# Initialize results list
results = []

# Define the Interpret Function
def parity(x):
    return '{:b}'.format(x).count('1') % 2

# Use a predefined QNNCircuit with 2 qubits (for 2 features)
from qiskit_machine_learning.circuit.library import QNNCircuit
qc = QNNCircuit(2)

# Extract input and weight parameters
input_params = qc.input_parameters
weight_params = qc.weight_parameters

sampler = Sampler()

sampler_qnn = SamplerQNN(
    circuit=qc,
    input_params=input_params,
    weight_params=weight_params,
    interpret=parity,
    output_shape=2,  # Number of classes
    sampler=sampler
)

# Define the optimizer
optimizer = COBYLA(maxiter=50)

# Rescale the data
rescale_pipeline = make_pipeline(StandardScaler())
X_rescaled = rescale_pipeline.fit_transform(X)

# Cross-validation setup
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores_train = []
scores_test = []

# Label encoding for binary classification
label_encoder = LabelEncoder()
y_encoded = label_encoder.fit_transform(y)

for train_index, test_index in kf.split(X_rescaled):
    X_train, X_test = X_rescaled[train_index], X_rescaled[test_index]
    y_train, y_test = y_encoded[train_index], y_encoded[test_index]

    # Create a new instance of the classifier for each fold
    quantum_classifier = NeuralNetworkClassifier(
        neural_network=sampler_qnn,
        optimizer=optimizer
    )

    # Fit the classifier
    quantum_classifier.fit(X_train, y_train)
    training_accuracy = quantum_classifier.score(X_train, y_train)
    scores_train.append(training_accuracy)

    # Evaluate the classifier
    testing_accuracy = quantum_classifier.score(X_test, y_test)
    scores_test.append(testing_accuracy)

mean_score = np.mean(scores_test)
print(f"Rescaling method: StandardScaler, Feature Dimension: {feature_dimensions}")
print("Training scores:", scores_train)
print("Cross-validation scores:", scores_test)
print(f"Mean cross-validation score: {mean_score}")

# Store the results
results.append({
    'Rescaling Method': 'StandardScaler',
    'Feature Dimension': feature_dimensions,
    'Scores train': scores_train,
    'Scores test': scores_test,
    'Mean Score': mean_score
})

# Convert results to DataFrame for analysis
import pandas as pd
results_df = pd.DataFrame(results)
print(results_df)
results_df.to_csv(f'results_quantum_iris.csv')

When I want to run it on real hardware, I did the following modifications and doesn't work:

# Import necessary libraries
import warnings
import numpy as np
from sklearn.datasets import load_iris
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score, KFold
from sklearn.preprocessing import LabelEncoder

from qiskit import QuantumCircuit
from qiskit.circuit import ParameterVector
from qiskit.primitives import Sampler
from qiskit_machine_learning.neural_networks import SamplerQNN
from qiskit_machine_learning.algorithms.classifiers import NeuralNetworkClassifier
from qiskit_algorithms.optimizers import COBYLA

# Configure the logging levels and suppress warnings
warnings.filterwarnings("ignore")

# Set random seed for reproducibility
np.random.seed(42)

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator, SamplerV2 as Sampler
# Initialize the Qiskit Runtime Service
service = QiskitRuntimeService(channel="ibm_quantum", instance="ibm-q/open/main", token="IBM_QUANTUM_TOKEN")
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

# Parameters
feature_dimensions = [2]  # We will use 2 features for binary classification
number_classes = 2  # We'll work with binary classification

# Load a standard dataset (Iris)
iris = load_iris()
X, y = iris.data, iris.target

# Filter to keep only two classes for binary classification
binary_class_indices = y < 2
X = X[binary_class_indices]
y = y[binary_class_indices]

# Use only the first 2 features (reduce the dimensionality)
X = X[:, :2]

# Initialize results list
results = []

# Define the Interpret Function
def parity(x):
    return '{:b}'.format(x).count('1') % 2

# Use a predefined QNNCircuit with 2 qubits (for 2 features)
from qiskit_machine_learning.circuit.library import QNNCircuit
qc = QNNCircuit(2)

# Extract input and weight parameters
input_params = qc.input_parameters
weight_params = qc.weight_parameters

sampler = Sampler(backend)

sampler_qnn = SamplerQNN(
    circuit=qc,
    input_params=input_params,
    weight_params=weight_params,
    interpret=parity,
    output_shape=2,  # Number of classes
    sampler=sampler
)

# Define the optimizer
optimizer = COBYLA(maxiter=50)

# Rescale the data
rescale_pipeline = make_pipeline(StandardScaler())
X_rescaled = rescale_pipeline.fit_transform(X)

# Cross-validation setup
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores_train = []
scores_test = []

# Label encoding for binary classification
label_encoder = LabelEncoder()
y_encoded = label_encoder.fit_transform(y)

for train_index, test_index in kf.split(X_rescaled):
    X_train, X_test = X_rescaled[train_index], X_rescaled[test_index]
    y_train, y_test = y_encoded[train_index], y_encoded[test_index]

    # Create a new instance of the classifier for each fold
    quantum_classifier = NeuralNetworkClassifier(
        neural_network=sampler_qnn,
        optimizer=optimizer
    )

    # Fit the classifier
    quantum_classifier.fit(X_train, y_train)
    training_accuracy = quantum_classifier.score(X_train, y_train)
    scores_train.append(training_accuracy)

    # Evaluate the classifier
    testing_accuracy = quantum_classifier.score(X_test, y_test)
    scores_test.append(testing_accuracy)

mean_score = np.mean(scores_test)
print(f"Rescaling method: StandardScaler, Feature Dimension: {feature_dimensions}")
print("Training scores:", scores_train)
print("Cross-validation scores:", scores_test)
print(f"Mean cross-validation score: {mean_score}")

# Store the results
results.append({
    'Rescaling Method': 'StandardScaler',
    'Feature Dimension': feature_dimensions,
    'Scores train': scores_train,
    'Scores test': scores_test,
    'Mean Score': mean_score
})

# Convert results to DataFrame for analysis
import pandas as pd
results_df = pd.DataFrame(results)
print(results_df)
results_df.to_csv(f'results_quantum_iris.csv')

I get the following error message:

(quantum_hw) xavi@MacBook-Air-de-Xavier Desktop % python code_hw.py
Traceback (most recent call last):
  File "/Users/xavi/Desktop/code_hw.py", line 97, in <module>
    quantum_classifier.fit(X_train, y_train)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/algorithms/trainable_model.py", line 199, in fit
    self._fit_result = self._fit_internal(X, y)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/algorithms/classifiers/neural_network_classifier.py", line 116, in _fit_internal
    return self._minimize(function)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/algorithms/trainable_model.py", line 295, in _minimize
    optimizer_result = self._optimizer.minimize(
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/qiskit_algorithms/optimizers/scipy_optimizer.py", line 148, in minimize
    raw_result = minimize(
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_minimize.py", line 719, in minimize
    res = _minimize_cobyla(fun, x0, args, constraints, callback=callback,
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_cobyla_py.py", line 35, in wrapper
    return func(*args, **kwargs)
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_cobyla_py.py", line 278, in _minimize_cobyla
    sf = _prepare_scalar_function(fun, x0, args=args, jac=_jac)
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_optimize.py", line 288, in _prepare_scalar_function
    sf = ScalarFunction(fun, x0, args, grad, hess,
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_differentiable_functions.py", line 166, in __init__
    self._update_fun()
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_differentiable_functions.py", line 262, in _update_fun
    self._update_fun_impl()
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_differentiable_functions.py", line 163, in update_fun
    self.f = fun_wrapped(self.x)
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/scipy/optimize/_differentiable_functions.py", line 145, in fun_wrapped
    fx = fun(np.copy(x), *args)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/algorithms/objective_functions.py", line 152, in objective
    probs = self._neural_network_forward(weights)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/algorithms/objective_functions.py", line 102, in _neural_network_forward
    self._last_forward = self._neural_network.forward(self._X, weights)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/neural_networks/neural_network.py", line 229, in forward
    output_data = self._forward(input_, weights_)
  File "/Users/xavi/Desktop/qiskit-machine-learning/qiskit_machine_learning/neural_networks/sampler_qnn.py", line 391, in _forward
    job = self.sampler.run([self._circuit] * num_samples, parameter_values)
TypeError: run() takes 2 positional arguments but 3 were given

If I change

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator, SamplerV2 as Sampler

to

from qiskit.primitives import Sampler

I have the following error message:

(quantum_hw) xavi@MacBook-Air-de-Xavier Desktop % python code_hw.py 
Traceback (most recent call last):
  File "/Users/xavi/Desktop/code_hw.py", line 59, in <module>
    sampler = Sampler(backend)
  File "/opt/anaconda3/envs/quantum_hw/lib/python3.9/site-packages/qiskit/utils/deprecation.py", line 97, in wrapper
    return func(*args, **kwargs)
TypeError: __init__() takes 1 positional argument but 2 were given

Can you please help me ?

Thank you in advance

How can we reproduce the issue?

I provided the code above

What should happen?

The output should be something like:

Rescaling method: StandardScaler, Feature Dimension: [2]
Training scores: [0.6125, 0.5375, 0.5375, 0.575, 0.625]
Cross-validation scores: [0.6, 0.3, 0.5, 0.35, 0.55]
Mean cross-validation score: 0.45999999999999996
  Rescaling Method Feature Dimension                            Scores train                  Scores test  Mean Score
0   StandardScaler               [2]  [0.6125, 0.5375, 0.5375, 0.575, 0.625]  [0.6, 0.3, 0.5, 0.35, 0.55]        0.46

Any suggestions?

No response

edoaltamura commented 1 month ago

Hi @xaviervasques, thanks for raising this issue. It appears because V2 primitives are not supported in Qiskit Machine Learning 0.7.2 ( see https://github.com/qiskit-community/qiskit-machine-learning/issues/742 and https://github.com/qiskit-community/qiskit-machine-learning/issues/786) but are the only type that Qiskit IBM Runtime supports for running jobs on real backends.

The upcoming 0.8.0 version includes support for V2 primitives, so this error should disappear after you run with upgraded Qiskit Machine Learning (pip install -U qiskit-machine-learning). We expect the 0.8.0 version to come out at the end of October 2024.

More details on this error

Proposed temporary solution

We should expect your experiment to run on hardware with the upcoming 0.8.0 version and V2 primitive support. Until then, you may run simulations of SamplerQNN in local testing mode with Qiskit Aer backends and the V1 primitives as

from qiskit_ibm_runtime import EstimatorV1 as Estimator, SamplerV1 as Sampler

sampler = Sampler(backend)

sampler_qnn = SamplerQNN(
    circuit=qc,
    input_params=input_params,
    weight_params=weight_params,
    interpret=parity,
    output_shape=2,  # Number of classes
    sampler=sampler
)

With this setup, you will be able to switch to submitting to the real device just by changing the definitions of backend and restoring from qiskit_ibm_runtime import EstimatorV2 as Estimator, SamplerV2 as Sampler as you originally had. I hope this helps!

xaviervasques commented 2 weeks ago

Thank you Edoardo. I will wait for the 0.8.0, hope it is coming soon :)

edoaltamura commented 2 weeks ago

Yes indeed, we are looking forward to it! The feature will be in the main branch as soon as the PR is merged, but live from pip install --update once 0.8.0 is released.

Monish-KS commented 2 weeks ago

Looking forward to the 0.8.0.