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

Use ParameterReferences type for ParameterTable values #7408

Merged
merged 12 commits into from
Jun 9, 2022

Conversation

kevinhartman
Copy link
Contributor

@kevinhartman kevinhartman commented Dec 14, 2021

Summary

Changes ParameterTable value type from List[(Instruction, int)] to a new collection type, ParameterReferences, which is set-like, and no longer seq-like.

ParameterReferences:

A set of instruction parameter slot references. Items are expected in the form (instruction, param_index). Membership testing is overriden such that items that are otherwise value-wise equal are still considered distinct if their instructions are referentially distinct.

Details and comments

It's not actually necessary to update existing uses of ParameterTable or its values, but usage is updated in QuantumCircuit for performance (mostly to avoid creating lists that are then immediately spooled into ParameterReferences). See ParameterReferences docstring for more information.

Fixes #6725. ParameterReferences is set-like, so there's no need to perform a linear search in _update_parameter_table to check existing references for every instruction parameter.

@kevinhartman kevinhartman requested a review from kdk December 14, 2021 06:59
@kevinhartman kevinhartman requested a review from a team as a code owner December 14, 2021 06:59
@kevinhartman
Copy link
Contributor Author

Tagging @jakelishman esp. for Python best practice insights :)

@coveralls
Copy link

coveralls commented Dec 14, 2021

Pull Request Test Coverage Report for Build 2468737188

  • 55 of 60 (91.67%) changed or added relevant lines in 6 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.0007%) to 84.426%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/circuit/parametertable.py 38 43 88.37%
Totals Coverage Status
Change from base Build 2465465477: -0.0007%
Covered Lines: 54710
Relevant Lines: 64802

💛 - Coveralls

Copy link
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 looks pretty sensible, and looks like it should reduce a potential quadratic complexity in circuit depth when using parameters. Do you have benchmarks for improvement for it to hand?

qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
test/python/circuit/test_parameters.py Outdated Show resolved Hide resolved
@kevinhartman
Copy link
Contributor Author

Do you have benchmarks for improvement for it to hand?

I don't! I should have some time to get some numbers in the coming week, and will report back.

@kevinhartman
Copy link
Contributor Author

@jakelishman, I found at least one place that depends on ParameterReferences being seq-like. Here, for each reference, we need to look up the corresponding copy of the same reference in a new circuit(s).

https://github.com/Qiskit/qiskit-terra/blob/5ff5757a762a677baa7db2fde09559ee87a0c8dc/qiskit/opflow/gradients/circuit_gradients/param_shift.py#L208-L218

I'm not really sure if there's a good way to do this if ParameterReferences is no longer ordered, since ParameterReferences might (by design) contain multiple references that are equivalent, but not the same. We can't do any sort of lookup since we're dealing with a deep copy in the new circuit.

Any ideas on how we should proceed?

@kevinhartman
Copy link
Contributor Author

Here are the asv performance results for time_build_parameterized_circuit, which show a significant speedup for large numbers of gates that re-use a comparatively small set of parameters.

  84.6±1ms         74.6±1ms     0.88  circuit_construction.ParameterizedCircuitConstructionBench.time_build_parameterized_circuit(20, 2048, 8)
1.47±0.03s       1.20±0.01s     0.82  circuit_construction.ParameterizedCircuitConstructionBench.time_build_parameterized_circuit(20, 32768, 128)
   484±6ms          307±3ms     0.63  circuit_construction.ParameterizedCircuitConstructionBench.time_build_parameterized_circuit(20, 8192, 8)
14.8±0.06s       4.91±0.03s     0.33  circuit_construction.ParameterizedCircuitConstructionBench.time_build_parameterized_circuit(20, 131072, 128)
4.23±0.04s       1.23±0.01s     0.29  circuit_construction.ParameterizedCircuitConstructionBench.time_build_parameterized_circuit(20, 32768, 8)
   2.15±0m       5.02±0.03s     0.04  circuit_construction.ParameterizedCircuitConstructionBench.time_build_parameterized_circuit(20, 131072, 8)

@ewinston
Copy link
Contributor

Is there an asv test for a large number of gates with a large number of parameters?

@jakelishman
Copy link
Member

@ewinston: Yeah, the same benchmark suite Kevin showed goes up to 131,072 gates x 131,072 parameters, but I'm guessing he just didn't show them here because there wasn't much change (and in total there's ~36 benchmarks in that suite).

@jakelishman
Copy link
Member

@kevinhartman: sorry, I haven't had chance to look properly, but I feel like that code block there is logically just trying to match up the instructions between circuits, including from a main circuit. I think it might be possible to modify the algorithm in that location a little such that it iterates over a index into the QuantumCircuit.data list, and then uses that index for each circuit to retrieve the instruction tuple, from which it can access the parameter table. I'm not 100% sure, but I'd be hopeful we can change things on the algorithm side rather than requiring ParameterReferences to be sequence-like, because in the end, we don't really want circuit data to have a total ordering - the instruction flow is logically a graph, not a sequence.

(I get that my proposed solution uses the total ordering of data, but I'm mostly just pushing the issue down the line, because when we're ready to move the backing data structure of QuantumCircuit.data to a graph-like one, we'll have to solve these problems far more generally across Terra.)

@kevinhartman
Copy link
Contributor Author

I think it might be possible to modify the algorithm in that location a little such that it iterates over a index into the QuantumCircuit.data list, and then uses that index for each circuit to retrieve the instruction tuple, from which it can access the parameter table.

Hmm, alright! That's helpful. I'll give it a try.

(I get that my proposed solution uses the total ordering of data, but I'm mostly just pushing the issue down the line, because when we're ready to move the backing data structure of QuantumCircuit.data to a graph-like one, we'll have to solve these problems far more generally across Terra.)

I agree completely that it'd be nice to eliminate maintaining an arbitrary ordering of parameter references, at the very least.

Previously, ParameterReferences implemented both Set and MutableSequence.
Now, it implements MutableSet, only. Existing seq-like usages throughout
Terra have been eliminated.
Copy link
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.

Overall this looks solid, and the benchmarks you've got above look absolutely 🔥. I have a point worrying about pickle (using id as a key is basically encoding dangling-pointer risks as a feature, so we need to be very careful), and I think some of the tests still assume the ordered behaviour from a previous iteration of this PR.

Don't forget the release note - we like to trumpet our speed-ups!

qiskit/circuit/parametertable.py Show resolved Hide resolved
qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
qiskit/circuit/parametertable.py Outdated Show resolved Hide resolved
qiskit/circuit/quantumcircuit.py Show resolved Hide resolved
test/python/circuit/test_parameters.py Outdated Show resolved Hide resolved
test/python/circuit/test_parameters.py Outdated Show resolved Hide resolved
test/python/circuit/test_parameters.py Outdated Show resolved Hide resolved
test/python/circuit/test_parameters.py Outdated Show resolved Hide resolved
test/python/circuit/test_parameters.py Outdated Show resolved Hide resolved
@kdk kdk added this to the 0.21 milestone Jun 7, 2022
Copy link
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.

Looks good to me, and the speed-ups are great! Thanks!

@jakelishman jakelishman added Changelog: New Feature Include in the "Added" section of the changelog automerge labels Jun 9, 2022
@mergify mergify bot merged commit 25ab23b into Qiskit:main Jun 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: New Feature Include in the "Added" section of the changelog performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

QuantumCircuit._check_dup_param_spec becomes slow for many gates reusing few parameters
5 participants