Skip to content

Reduce redundant circuit copies and parameter bindings in CircuitSampler#9228

Closed
n-ejima wants to merge 6 commits into
Qiskit:mainfrom
n-ejima:fix_param_update
Closed

Reduce redundant circuit copies and parameter bindings in CircuitSampler#9228
n-ejima wants to merge 6 commits into
Qiskit:mainfrom
n-ejima:fix_param_update

Conversation

@n-ejima
Copy link
Copy Markdown

@n-ejima n-ejima commented Dec 2, 2022

Summary

The current code has these two issues:

  1. QuantumCircuit is copied at every parameter binding, but the old copies are not used repeatedly.
  2. All of the parameters included in QuantumCircuit are updated even when the values of parameters are not changed.

I fixed the above issues by:

  1. Copying each of transpiled circuits only once and reusing the single copy per circuit.
  2. Only updating the parameters whose values are different from the previous ones.
  • This can be done because parameter values remain in transpiled circuits by reusing the copied circuits.

Details and comments

@qiskit-bot qiskit-bot added the Community PR PRs from contributors that are not 'members' of the Qiskit repo label Dec 2, 2022
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Dec 2, 2022

CLA assistant check
All committers have signed the CLA.

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@qiskit-bot
Copy link
Copy Markdown
Collaborator

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:

@coveralls
Copy link
Copy Markdown

coveralls commented Dec 2, 2022

Pull Request Test Coverage Report for Build 4742200433

  • 55 of 55 (100.0%) changed or added relevant lines in 1 file are covered.
  • 18 unchanged lines in 3 files lost coverage.
  • Overall coverage decreased (-0.007%) to 85.925%

Files with Coverage Reduction New Missed Lines %
qiskit/transpiler/passes/synthesis/unitary_synthesis.py 1 95.14%
crates/qasm2/src/lex.rs 5 91.14%
crates/qasm2/src/parse.rs 12 97.11%
Totals Coverage Status
Change from base Build 4737986268: -0.007%
Covered Lines: 71122
Relevant Lines: 82772

💛 - Coveralls

Copy link
Copy Markdown
Contributor

@ikkoham ikkoham left a comment

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

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?

@woodsp-ibm
Copy link
Copy Markdown
Member

with the deprecation of opflow pending, is this something we need to pursue?

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.

@jakelishman
Copy link
Copy Markdown
Member

Thanks Steve - for sure, I wasn't suggesting it was your fault here @n-ejima, sorry that wasn't clear.

@n-ejima
Copy link
Copy Markdown
Author

n-ejima commented Dec 5, 2022

@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.

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.
This PR can reduce a large amount of processing time of parameter bindings.

image

image

@n-ejima
Copy link
Copy Markdown
Author

n-ejima commented Dec 22, 2022

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.

  • Result of executing VQE on the H8 molecule (# of qubit:13, # of parameter:184,# of iteration:1861)
    • total executing time of assign_parameter
      • before fixing: 888 sec, overall VQE executingtime: 2015 sec
      • reducing circuit copy: 167 sec (assign_parameter + processing to reduce circuit copy), overall VQE executing time: 1210 sec

@n-ejima n-ejima requested review from ikkoham and jakelishman and removed request for ikkoham and jakelishman December 22, 2022 07:32
Copy link
Copy Markdown
Contributor

@ikkoham ikkoham left a comment

Choose a reason for hiding this comment

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

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
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These codes should be outside of try.

self._reuse_global_phase = []
for circ in self._transpiled_circ_cache:
shadow = circ.copy()
shadow._increment_instances()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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]:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it difficult to avoid using __getstatete__?

@ikkoham ikkoham added this to the 0.23.0 milestone Jan 10, 2023
@ikkoham ikkoham self-assigned this Jan 10, 2023
@Cryoris
Copy link
Copy Markdown
Collaborator

Cryoris commented Jan 10, 2023

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 instruction.params and avoid going through .bind_parameters, right?

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:

  • general speedup: make circuit copies fast by taking the parameter out of the instruction (see circuit deepcopy too slow: singleton gates #3800), this is something @jakelishman is already preparing
  • make parameters re-assignable: this is closer to the approach in this PR and would mean that we refactor the parameters to additionally store a value they are currently assigned to and make the backends handle these new types

@ikkoham
Copy link
Copy Markdown
Contributor

ikkoham commented Jan 11, 2023

@Cryoris Thanks. QuantumCircuit's deepcopy speedup should be done. This is a problem in many areas, not just this one.

I think the approach of making reassignable is nice. However, I personally do not want to change QC it's already too complicated. If someone could do it by 0.24, I agree that we wait for that change and then allow this PR to use re-assign.

@n-ejima
Copy link
Copy Markdown
Author

n-ejima commented Jan 12, 2023

@Cryoris

If I understand correctly, the idea is to directly change the parameters inside the instruction.params and avoid going through .bind_parameters, right?

I don't intended to update the parameters without assign_parameter(). In my approach, parameters are updated by assign_prameters(), and after executing the quantum circuit, parameters are restored to its original state like 0.5 * t[0] maintaining both QuantumCircuit and QC's ParameterTable have the same instruction.params instance.

make parameters re-assignable: this is closer to the approach in this PR and would mean that we refactor the parameters to additionally store a value they are currently assigned to and make the backends handle these new types

As @ikkoham said, if QC will be modified to have both original and assigned parameters, I want to wait for it.

@ikkoham ikkoham modified the milestones: 0.23.0, 0.24.0 Jan 17, 2023
@ikkoham
Copy link
Copy Markdown
Contributor

ikkoham commented Feb 2, 2023

@Cryoris Do you have a plan to implement re-assignable parameters for 0.24?

@kdk kdk changed the title Reduce redundant circuit copies and parameter bindings Reduce redundant circuit copies and parameter bindings in CircuitSampler Apr 6, 2023
@mtreinish mtreinish removed this from the 0.24.0 milestone Apr 11, 2023
@ikkoham ikkoham added this to the 0.24.0 milestone Apr 14, 2023
@Cryoris
Copy link
Copy Markdown
Collaborator

Cryoris commented Apr 19, 2023

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

  1. This is a workaround for the expensive circuit copies, which we are currently working to resolve. With light-weight circuits, this workaround would not be necessary anymore and the problem is properly fixed, without the trouble of mixed ownerships mentioned above
  2. Opflow is being deprecated in this release, so adding a workaround solution as this adds a lot of overhead to code that will be removed soon.

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!

@Cryoris Cryoris removed this from the 0.24.0 milestone Apr 19, 2023
@jakelishman
Copy link
Copy Markdown
Member

I'm sorry that we couldn't move this to merge at the time. Now that opflow has been removed, as of #11111, this PR is obsolete, so I'm going to close it. Thanks for the effort at the time, and sorry that it didn't merge while opflow was live.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community PR PRs from contributors that are not 'members' of the Qiskit repo performance

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

9 participants