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

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

Closed
xaviervasques opened this issue Oct 11, 2024 · 4 comments · Fixed by #843
Closed

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

xaviervasques opened this issue Oct 11, 2024 · 4 comments · Fixed by #843
Labels
Hardware runtime 💻 Running jobs on IBM quantum devices
Milestone

Comments

@xaviervasques
Copy link

xaviervasques commented Oct 11, 2024

Environment

  • Qiskit Machine Learning version:
    Name: qiskit-machine-learning
    Version: 0.8.0
    (I have tried 0.7, 0.5 ...)
  • Python version:
    Python 3.9.20
  • Operating system:
    MacOS

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
Copy link
Collaborator

Hi @xaviervasques, thanks for raising this issue. It appears because V2 primitives are not supported in Qiskit Machine Learning 0.7.2 ( see #742 and #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

  • When using {Sampler,Estimator}V2 and call .run(...) the arguments must be structured as a Primitive Unified Bloc (PUB) which is of type tuple, or a list of PUBs (list[tuple]). This structure is different from that of the V1 primitives and leads to the error above (TypeError: run() takes 2 positional arguments but 3 were given) when V1-like arguments are used in V2 primitives. .run(...) is triggered in your code when you call SamplerQNN(...).fit(...).
  • When using from qiskit.primitives import Sampler you are implicitly using V1 Base primitives, which do not have the additional functionality to be run on hardware directly and also returns an argument mismatch error from SamplerQNN(...).fit(...). Qiskit IBM Runtime has derived primitives that do just that, however, V1 primitives can no longer run on real backends.

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!

@edoaltamura edoaltamura added the Hardware runtime 💻 Running jobs on IBM quantum devices label Oct 22, 2024
@edoaltamura edoaltamura added this to the 0.8.0 milestone Oct 22, 2024
@xaviervasques
Copy link
Author

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

@edoaltamura
Copy link
Collaborator

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
Copy link

Looking forward to the 0.8.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Hardware runtime 💻 Running jobs on IBM quantum devices
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants