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

pass_manager param in EstimatorQNN generates error for V2 estimator on statevector simulator #872

Closed
ironfrown opened this issue Dec 9, 2024 · 9 comments · Fixed by #879

Comments

@ironfrown
Copy link

ironfrown commented Dec 9, 2024

Environment

  • 0.9.0:
  • 3.11.10:
  • Ubuntu 22.04.5 LTS:

What is happening?

I am running EstimatorQNN with V2 AerEstimator (and 'statevector' and 'GPU' options), on a simple circuit (ZZFeatureMap+RealAmplitudes). When the circuit has been processed by generate_preset_pass_manager before calling EstimatorQNN, it works fine. When a "raw" circuit has been passed into EstimatorQNN with a pass_manager option, it fails as it expects the circuit to have a non-empty layout (which is None). The error is:

File ~/miniconda3/envs/qiskit-gpu/lib/python3.11/site-packages/qiskit_machine_learning/neural_networks/estimator_qnn.py:185, in EstimatorQNN.init(self, circuit, estimator, observables, input_params, weight_params, gradient, input_gradients, default_precision, pass_manager)
183 else:
184 circuit = pass_manager.run(circuit)
--> 185 self.num_virtual_qubits = circuit.layout._input_qubit_count
187 self._org_circuit = circuit
189 if observables is None:
AttributeError: 'NoneType' object has no attribute '_input_qubit_count'

How can we reproduce the issue?

from qiskit_aer import AerSimulator
from qiskit.primitives import StatevectorEstimator as Estimator # For device="CPU"
from qiskit_aer.primitives import EstimatorV2 as AerEstimator # For device="GPU"

### Select a device
device = 'GPU' if 'GPU' in AerSimulator().available_devices() else 'CPU'
sim = AerSimulator(method='statevector', device=device)

if (device == 'GPU'):
    estimator = AerEstimator(
        options={'backend_options':{'method': sim.options.method, 
                                    'device' : sim.options.device, 
                                    'cuStateVec_enable' : True}}
    )
else:
    estimator = Estimator()

print(f'Selected device: {device}')

### Create a circuit

from qiskit import QuantumCircuit
from qiskit.circuit.library import ZZFeatureMap, RealAmplitudes
from qiskit_machine_learning.neural_networks import EstimatorQNN
from qiskit_machine_learning.gradients import ParamShiftEstimatorGradient
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

num_qubits = 2

feature_map = ZZFeatureMap(feature_dimension=num_qubits)
ansatz = RealAmplitudes(num_qubits=num_qubits)

qc = QuantumCircuit(num_qubits)
qc.compose(feature_map, inplace=True)
qc.compose(ansatz, inplace=True)

### Define / apply pass manager

pm = generate_preset_pass_manager(backend=sim, optimization_level=1)
pm_qc = pm.run(qc)

### Run estimator QNN

qnn = EstimatorQNN(
    circuit=qc, #pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator),
    pass_manager=pm # comment out if qc pre-processed
)

print('Forward step:', qnn.forward(input_data=[1, 2], weights=[1, 2, 3, 4, 5, 6, 7, 8]))

What should happen?

Supplied pass manager should be applied to the "raw" circuit without errors

Any suggestions?

No response

@OkuyanBoga
Copy link
Collaborator

OkuyanBoga commented Dec 9, 2024

Hi @ironfrown, thank you very much for the feedback.

Unfortunately, there are too many different V2 primitives available to check and bug-fix after a qiskit update, hopefully, new release will solve this issue. Can you please check and confirm if it still exists with 0.8.1?

@ironfrown
Copy link
Author

ironfrown commented Dec 10, 2024

Hi @OkuyanBoga, unfortunately ML 0.8.1 has not addressed all issues. Here are some example runs on a simple "raw" circuit of ZZFeatureMap + TwoLocal with V2 primitives estimator and SparsePauliOp observables:

  • EstimatorQNN with 'raw' circuit fails with "unknown instruction: ZZFeatureMap"
  • EstimatorQNN with 'raw' or pass-manager pre-processed circuit, with pass_manager passed in via params fails with "'NoneType' object has no attribute '_input_qubit_count'"
  • EstimatorQNN without gradient or pass manager but with pass-manager pre-processed circuit works but gives a warning "No gradient function provided", which is fine
  • EstimatorQNN with gradient, observables, no pass manager passed in, but with pass manager pre-processed circuit works

It seems that EstimatorQNN is now a complex class, which may be a challenge for the Qiskit ML novices!

@ironfrown
Copy link
Author

@OkuyanBoga, note however that adding measurements to the parameterised circuit (as recommended in the migration guide) and training it with EstimatorQNN + NeuralNetworkRegressor makes its processing impossibly slow!

@OkuyanBoga
Copy link
Collaborator

Hi @ironfrown, thanks again for the feedback.

Unfortunately, updates from Qiskit make it challenging even for us, and we are trying our best to reduce the effect on the end users.

I strongly suggest using Qiskit<1.3 for now due to issues in Qiskit 1.3 and Qiskit-Aer. In Qiskit 1.3, circuits such as ZZFeatureMap are replaced with functions such as zzfeaturemap, and we are not supporting functions yet. If you are not using Qiskit 1.3, can you please share your code to reproduce the first two issues?

I think the raw circuits should not fail the check for _input_qubit_count in the following code lines:

if hasattr(circuit.layout, "_input_qubit_count"):
self.num_virtual_qubits = circuit.layout._input_qubit_count
else:
if pass_manager is None:
self.num_virtual_qubits = circuit.num_qubits
else:
circuit = pass_manager.run(circuit)
self.num_virtual_qubits = circuit.layout._input_qubit_count

We plan to do another bug-fix update (0.8.2) before the new year, so we can address all of these as much as possible. Unfortunately, we are also experiencing performance issues with every V2 primitive, especially when using transpilation. We think this issue stems from gradients when gradient algorithms create new circuits and tranpile them each step. We are planning to speed up the process in the following major release (0.9).

@ironfrown
Copy link
Author

@OkuyanBoga, I am using Qiskit 1.2.4 and installed ML 0.8.1. The code attached in the section "How can we reproduce the issue?" demonstrates the first two issues.

When you run it as is, it shows the layout error (problem 2), i.e. use:

qnn = EstimatorQNN(
    circuit=qc, #pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator),
    pass_manager=pm # comment out if qc pre-processed
)

When you comment out the "pass_manager=pm" line in EstimatorQNN class call, it demonstrates the 'unknown instruction: ZZFeatureMap' issue (problem 1), i.e. use:

qnn = EstimatorQNN(
    circuit=qc, #pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator),
    # pass_manager=pm # comment out if qc pre-processed
)

When you pass the pass-manager preprocessed circuit and comment out the pass_manager option, it works fine (the last point), i.e. use:

qnn = EstimatorQNN(
    circuit=pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator),
    # pass_manager=pm # comment out if qc pre-processed
)

Note that using the pass manager option in gradient does not change these errors.

@OkuyanBoga
Copy link
Collaborator

Thanks @ironfrown.

  • For the first one, gradient also requires a pass manager since it is creating circuits:
# Wrong:
qnn = EstimatorQNN(
    circuit=qc, #pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator),
    pass_manager=pm # comment out if qc pre-processed
)

# Correct:
qnn = EstimatorQNN(
    circuit=qc, #pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator=estimator, pass_manager=pm),
    pass_manager=pm # comment out if qc pre-processed
)
  • For the second one, both EstimatorQNN and gradient also require a pass manager because of your estimator:
# Correct for qiskit.primitives.StatevectorEstimator
# Wrong if estimator is from qiskit-ibm-runtime or requires transpiled circuits:
qnn = EstimatorQNN(
    circuit=qc, #pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator),
    # pass_manager=pm # comment out if qc pre-processed
)

# Correct 1:
qnn = EstimatorQNN(
    circuit=qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator=estimator, pass_manager=pm),
    pass_manager=pm
)
# Correct 2:
qnn = EstimatorQNN(
    circuit=pm_qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator, gradient=ParamShiftEstimatorGradient(estimator=estimator, pass_manager=pm),
)
# Correct 3 if you want to use default gradient (ParamShiftEstimatorGradient):
qnn = EstimatorQNN(
    circuit=qc,
    input_params=feature_map.parameters,
    weight_params=ansatz.parameters,
    estimator=estimator,
    pass_manager=pm,
)

Unfortunately, the lack of standards in V2 primitives can be really frustrating. We tried to minimise this in Qiskit ML for end users. I don't think there is an issue with the algorithm, but we will check if we can add checks based on the given estimator.

@ironfrown
Copy link
Author

@OkuyanBoga, I thought you wanted to replicate the errors - just run the provided script and see the results, you can change the options to what you think should be correct!

@ironfrown
Copy link
Author

ironfrown commented Dec 11, 2024

Inclusion of pass_manager=pm in the gradient makes no difference, the errors will show! Note also that inclusion of pass_manager in gradient for circuits with many parameters leads to the exponential memory use and the system crash.

@OkuyanBoga
Copy link
Collaborator

@ironfrown I am kindly trying to help by any means, it could be by replicating and fixing the errors or correcting the way algorithms run. If you identify any potential improvements, we welcome you to submit a pull request. Any contributions are very welcome.

As I mentioned before, this problem arises from the lack of standards in V2 primitives, backends and transpilers between qiskit, qiskit-aer, qiskit-ibm-runtime, and we will add checks based on the given estimator to fix the issue for the upcoming minor release 0.8.2.

We are also working on improving gradients with transpilation, it will be included in the next major release.

You can add a layout to the pass manager to temporarily fix the issue :
pm = generate_preset_pass_manager(backend=sim, optimization_level=1, initial_layout=[1, 0])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants