Reduce redundant circuit copies and parameter bindings in CircuitSampler#9228
Reduce redundant circuit copies and parameter bindings in CircuitSampler#9228n-ejima wants to merge 6 commits into
Conversation
|
|
|
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the the following people are requested to review this:
|
Pull Request Test Coverage Report for Build 4742200433
💛 - Coveralls |
ikkoham
left a comment
There was a problem hiding this comment.
@n-ejima Thank you for your first contribution. Your proposal is great!
This PR seems like it would definitely improve the performance of VQE. Do you have any benchmarking results? The benchmark can show the impact of this PR.
For the code, we don't want to change the QuantumCircuit class if possible. The type signature change of .assign_parameters affects the linter (CI fails). Therefore, I propose to create a method inside CircuitSampler that is assigning parameters to QuantumCircuit.
jakelishman
left a comment
There was a problem hiding this comment.
This PR is suggesting a massive mix-up of object ownership semantics that I don't think is safe to bring near QuantumCircuit, and will be fragile even for use within opflow. I would suggest that if opflow deliberately wants to share ownership of various internal objects that it can guarantee no references to will leak out to users and each object will only be mutated once, it's going to need to do its own thing a little bit; I'm not comfortable adding these changes into QuantumCircuit itself.
On a second note: with the deprecation of opflow pending, is this something we need to pursue? How does the non-deprecated replacement for this behave?
I don't think so, if the focus here is really around opflow and CircuitSampler. The author may not have known that, with the refactoring of algorithms over to using primitives in the last release, that the former algorithms and the way they worked, along with opflow and QuantumInstance they used, will shortly be deprecated and removed thereafter. |
|
Thanks Steve - for sure, I wasn't suggesting it was your fault here @n-ejima, sorry that wasn't clear. |
This graph shows the processing time of parameter bindings when running VQE in below experimental environment. Circuit copy and parameter assignment account for a large part of parameter bindings. |
|
I fixed the code not to change QuantumCircuit class. With this fixing, the 2nd issue I mentioned is not resolved. I think only reducing the number of circuit copy (1st issue) has enough effect to the performance.
|
ikkoham
left a comment
There was a problem hiding this comment.
Sorry for my lazy review.
The direction (reduce redundant circuit copies) seems nice. The performance improvement is significant and may be use the same idea in primitives.
I have minor comments to improve codes.
| self._transpiled_circ_cache = self.quantum_instance.transpile( | ||
| circuits, pass_manager=self.quantum_instance.unbound_pass_manager | ||
| ) | ||
|
|
There was a problem hiding this comment.
These codes should be outside of try.
| self._reuse_global_phase = [] | ||
| for circ in self._transpiled_circ_cache: | ||
| shadow = circ.copy() | ||
| shadow._increment_instances() |
There was a problem hiding this comment.
Do we need _increment_instances and _name_update here?
| if param_bindings is not None: | ||
| if self._reuse_circs != []: | ||
| # copy quantum circuit if len(param_bindings) > 1 | ||
| append_size = len(param_bindings) - len(self._reuse_circs[0]) |
There was a problem hiding this comment.
If the length of param_bindings is smaller on the second calls, won't this be a negative number?
| # restore the original parameter table and global phase in shadow_circs | ||
| for i, circ in enumerate(self._reuse_circs): | ||
| for j, _ in enumerate(circ): | ||
| for param in self._reuse_parameter_tables[i][j]: |
There was a problem hiding this comment.
self._reuse_parameter_tables[i][j].items() is readable since self._reuse_parameter_tables[i][j][param] is frequently appeared.
| for k, instr in enumerate(self._reuse_parameter_tables[i][j][param]): | ||
| instr[0].params = ( | ||
| self._preserved_parameter_tables[i][j][param] | ||
| .__getstate__()[k][0] |
There was a problem hiding this comment.
Is it difficult to avoid using __getstatete__?
|
It's definitely great to reduce copies, however I'm not quite sure that this is the right approach here. If I understand correctly, the idea is to directly change the parameters inside the This approach is a bit volatile as e.g. parameter changes are not propagated down to sub-instructions and decomposing the circuit will lose the parameter assignment. There are two other ways we could implement this or speed up the assignment generally:
|
|
@Cryoris Thanks. I think the approach of making reassignable is nice. However, I personally do not want to change |
I don't intended to update the parameters without
As @ikkoham said, if QC will be modified to have both original and assigned parameters, I want to wait for it. |
|
@Cryoris Do you have a plan to implement re-assignable parameters for 0.24? |
|
We discussed this in the team and came to the conclusion that we would rather not include this PR into the release, for two main reasons
Your benchmarks are definitely impressive and also serve as a good indicator of how much faster Qiskit can be, once the circuits are efficiently copied 🙂 I hope this makes sense to you, let me know if you want to discuss further! |
|
I'm sorry that we couldn't move this to merge at the time. Now that |


Summary
The current code has these two issues:
I fixed the above issues by:
Details and comments