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

transpile ansatz and pauli-lists separately in VQE #6454

Closed
wants to merge 27 commits into from

Conversation

hhorii
Copy link
Contributor

@hhorii hhorii commented May 24, 2021

Summary

A parameterized circuit of ansatz is transpiled once in VQE

Details and comments

A parameterized circuit of ansatz is copied to multiple circuits in OpFlow and they are transpiled though they share the same ansatz.
This PR transpiles ansatz and puli-lists separately and generates multiple circuits by combining them.

  • transpile ansatz
  • extract qubit-mapping from transpiled ansatz
  • transpile pauli-lists with extracted qubit-mapping
  • merge transpled ansatz and pauli-lists

This issue will resolve a part of #6432.

@hhorii hhorii requested review from manoelmarques, woodsp-ibm and a team as code owners May 24, 2021 08:45
@woodsp-ibm woodsp-ibm added mod: algorithms Related to the Algorithms module mod: opflow Related to the Opflow module labels May 24, 2021
@Cryoris
Copy link
Contributor

Cryoris commented May 25, 2021

Couldn't we just solve this issue by transpiling the circuit before calling expectation.convert in the VQE (and then maybe skipping the transpilation in the circuit sampler)?

@hhorii
Copy link
Contributor Author

hhorii commented May 25, 2021

Couldn't we just solve this issue by transpiling the circuit before calling expectation.convert in the VQE (and then maybe skipping the transpilation in the circuit sampler)?

f4aec15 does so. But it is not correct if coupling_map is specified and the final mapping is different in transpile.

@mtreinish mtreinish added this to the 0.18 milestone May 25, 2021
@Cryoris
Copy link
Contributor

Cryoris commented May 25, 2021

What if we pre-transpile but then still transpile again in the circuit sampler? Since we already did most the optimizations that should be pretty fast, no?

I'm not sure the steps you outlined above are the right approach. This scenario of ansatz + Pauli expectations is very common and we should improve the transpilation in general, not only within the VQE. What about making PauliExpectation.convert return a special type of ListOp where the CircuitSampler knows how to efficiently transpile it?

@woodsp-ibm
Copy link
Member

I guess going forwards this can be related to #6451
In the short term, the transpile is a one time thing right for all the circuits. For each eval they are then set with parameters and executed. So this is really speeding up their creation. And of course with include_custom things work faster as there is just the one ansatz circuit for the expectation.

@hhorii
Copy link
Contributor Author

hhorii commented May 27, 2021

What if we pre-transpile but then still transpile again in the circuit sampler? Since we already did most the optimizations that should be pretty fast, no?

In Macbook Pro, original UCCSD circuits are traspiled in 620 seconds. Pre-transpiled circuits are transpiled in 129 seconds. Pre-transpile takes 4 second.

@Cryoris
Copy link
Contributor

Cryoris commented May 28, 2021

Thanks for checking the runtimes @hhorii! I agree this is quite a strong speedup and we should definitely allow to only transpile once. To keep the old behavior available, should we add a flag that allows to keep the old behavior (e.g. if you run on real HW you might want to transpile twice)?

@hhorii hhorii changed the title [WIP] transpile ansatz and pauli-lists separately in VQE transpile ansatz and pauli-lists separately in VQE Jun 10, 2021
@hhorii hhorii force-pushed the transpile_only_ansatz branch from 0338cbc to bab18b5 Compare June 11, 2021 07:21

# pylint: disable=unused-argument
@deprecate_arguments({"circuit_sfns": "operator"})
def sample_circuits(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering about the extent of the changes here. Should we be keeping sample_circuits for people to use as it was and add a new method that takes operator to do what we want now. If I had done code that used sample_circuits - its a public interface here after all - I think the changes here means it no longer works for my existing code - deprecated means it should keep on working as it had done until we remove the deprecated function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is true. However, I personally feel uncomfortable that this method, which has no unit tests, is public. Considering the future maintenance, I think the method should be deprecated now. I can't think of a use case for this method by itself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@woodsp-ibm Thanks a lot. I finally decided not to change this method.

Copy link
Contributor

@Cryoris Cryoris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ikkoham! I have a few questions about this PR 🙂

  1. I'm sure I fully understand the approach, would you mind explaining it briefly? How do you know the common circuit is the right-hand side state in the composed op? What if you have multiple composed ops?
  2. Couldn't we just transpile the circuit in VQE and not transpile it again in the circuit sampler? Then we wouldn't have to change a lot (or maybe nothing at all) in the Circuit Sampler.

@@ -318,7 +324,7 @@ def construct_expectation(

observable_meas = self.expectation.convert(StateFn(operator, is_measurement=True))
ansatz_circuit_op = CircuitStateFn(wave_function)
return observable_meas.compose(ansatz_circuit_op).reduce()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the call to reduce removed? 🙂

Copy link
Contributor

@ikkoham ikkoham Jun 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes copies ansatz's quantum circuit. In order to take advantage of lazy evaluation, we should reduce at a later stage. Since CircuitSampler do the reduce, we do not need to do the reduce at this stage, and it will degrade the performance.

Comment on lines -126 to +130
del self._p2v[key]
del self._v2p[self._p2v[key]]
del self._p2v[key]
elif isinstance(key, Qubit):
del self._v2p[key]
del self._p2v[self._v2p[key]]
del self._v2p[key]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should probably not be touched

Copy link
Contributor

@ikkoham ikkoham Jun 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? This is clearly a bug, @hhorii found. This is necessary to execute our code. Should we separate the PR?

@ikkoham
Copy link
Contributor

ikkoham commented Jun 16, 2021

@Cryoris Thanks. This approach is to compile ansatz circuit only once, instead of N times when N Pauli are given.

How do you know the common circuit is the right-hand side state in the composed op? What if you have multiple composed ops?

In terms of OpFlow's design, ansatz is contained in StateFn on the right side, and information about observables is contained in MeasurementFn (=~StateFn) on the left side. Also, it is not possible to create or evaluate a ComposedOp with more than three StateFn. We don't need optimize the performance except for the case which this PR supports for now.

Couldn't we just transpile the circuit in VQE and not transpile it again in the circuit sampler? Then we wouldn't have to change a lot (or maybe nothing at all) in the Circuit Sampler.

It is my understanding that the CircuitSampler should be responsible for transpiling quantum circuits, not the VQE.
It might be a good idea to create a class that is responsible for transpiling, such as CircuitTranspiler as a Opflow Converter.

@ikkoham
Copy link
Contributor

ikkoham commented Jun 24, 2021

@Qiskit/terra-core Is this PR mergeable?

I've made both, and personally it's better to have the transpile closed in CircuitSampler. But this is a performance issue that needs to be included in the next release (0.18.0). So if committers really wants to transpile on the VQE side, I will complete #6602 asap.

@woodsp-ibm
Copy link
Member

@Cryoris you had comments against this approach to start with - can you respond to the above please.

diff_circuits.append(diff_circuit)

if transpiled_common_circuit._layout is not None:
layout = transpiled_common_circuit._layout.copy()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we need to set the "final" layout here, however, _layout stores the initial layout of the transpiled common circuit.

Unfortunately, we have no simple way to get the final layout of a transpiled circuit so far.
The following could work (I know it's not smart. Does anyone think of any better idea?):

  • Adding dummy measures to the common circuit before transpiling
  • Constructing the final layout from virtuals-clbits and physical-clbits maps stored in original and transpiled measures respectively
  • Removing the dummy measures from the common circuit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. You are right. We assume _layout is the final layout.
I have fixed the PR from your advice. Is this correct? 7418ac2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have shown how one can get the required final layout (permutation) here: #6827

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much. It's really useful.

@kdk kdk removed this from the 0.18 milestone Jun 29, 2021
@ajavadia
Copy link
Member

ajavadia commented Jun 29, 2021

I think this solution is nice because it is common and not specific to VQE, but it points to a deficiency in the software architecture. This code has to reverse-engineer a circuit to figure out what the ansatz is, transpile that, and then glue it back.
I think we should refactor the code a bit to make this nicer. In particular, I think the common task we want to perform here is to calculate expectation values of a circuit with respect to some operator(s). We should have a canonical way of doing this in Qiskit, which we can then optimize to be fast at transpiling etc. The algorithms (VQE, QAOA, etc.) should be refactored to use this expval interface.
I think @Cryoris has some ideas on how to implement this, so let's hold off on merging this until the new expval architecture emerges. Related to #6451

@ajavadia ajavadia added the on hold Can not fix yet label Jun 29, 2021
@ikkoham
Copy link
Contributor

ikkoham commented Jun 29, 2021

@ajavadia Thank you so much.
This assumption (what is ansatz, what is measurement) is natural in the opflow, for example:
https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/opflow/expectations/pauli_expectation.py#L97-L98
So I thought this is enough.

However, I agree we need more refactoring here. CircuitSampler (especially cache structure) is too complicated. I think many caches should be in the operator itself (e.g. reduce()), and we should split translation and sampling like CircuitTranspiler (#6454 (comment)). I thought this performance improvement is priority and I had to include it in this release, so I tried to force it in with minimal changes, but it's obviously better to refactor it in the long run. At that time, I hope that we will be able to easily take the final layout.

Then there's the unnecessary calculation of taking sqrt and squaring it in the current method. I think this unnecessary calculation should be removed. Also, we need an API that can take variance or standard error. With the existing CircuitSampler, I cannot create a method to use the SaveExpectationValueVariance to calculate variance #5743. I have tried to fix this by experiment class but I cannot do it in the current architecture.

Finally in my opinion, I thought it would be a better idea to make a major revision when the expectation value experiment class come in the future.

@ajavadia
Copy link
Member

@ikkoham thanks these are all great points. Let's gather these requirements under #6451 and then design the interfaces.

What do you mean by the "expectation value experiment class"?

The issue with final_layout: is it related to #5350 and #5280?

@woodsp-ibm
Copy link
Member

@hhorii I think this can be closed right, given primitives will deal with aspects like this going forwards and VQE will be refactored to run using primitives.

@ikkoham
Copy link
Contributor

ikkoham commented Jun 23, 2022

Yes, this is the logic already applied on the qiskit-ibm-runtime side (precisely, server side), so we can close it. Thank you for pointing out.

@ikkoham ikkoham closed this Jun 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: algorithms Related to the Algorithms module mod: opflow Related to the Opflow module on hold Can not fix yet performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants