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

Fix multiset ordering bug in perturbation module #211

Merged
merged 13 commits into from
Apr 6, 2023

Conversation

DanPuzzuoli
Copy link
Collaborator

@DanPuzzuoli DanPuzzuoli commented Apr 4, 2023

Summary

This is a technical bug that I just discovered while trying to rerun the speed benchmarks for the perturbation theory algorithms paper that the perturbation module is based on. It turns out this bug has existed since this module was introduced (in version 0.3.0), however it was missed up until this point as:

  1. It requires an example of sufficient complexity (>=10 perturbations), and
  2. The speed benchmarks, which were the only example we had run with >=10 perturbations, were run using an old commit in which the bug hadn't yet been introduced.

The issue arises in the file perturbation/dyson_magnus.py, which contains a bunch of manipulation of lists of multisets. Many of the functions contain arguments that are assumed to be lists of multisets that are "ordered and complete". Here, "complete" means the list is closed under taking submultisets (i.e. for any multiset in the list, all sub multisets are in the list), and "ordered" means ordered according to a < b iff:

  1. Either, len(a) < len(b), or
  2. if len(a) == len(b), sortedlist(a) "<" sortedlist(b), where sortedlist(a) means a represented as a sorted list (with repeated entries), and "<" means lexicographic ordering of these lists. (Implicit in this is the assumption that the elements of a and b are themselves ordered, which is fine as we explicitly demand the multisets be composed of integers.)

This may seem kind of convoluted, but for technical reasons it is convenient to order in this way when constructing the perturbation theory computations. (More succinctly, the ordering within multisets of equal size is the "lexicographic" ordering of their sorted-list representation.)

The bug was ultimately introduced when we started using the external multiset package, as opposed to a Multiset class I had personally written. There is no problem with the multiset package itself, I had just made an error during the conversion in which I incorrectly implemented the above ordering criteria. The incorrect implementation was based on a string representation of the multiset, which resulted Multiset([10]) < Multiset([1]) == True. This led to these lists being incorrectly ordered when dealing with at least 10 perturbations, and ultimately led to malfunctioning of the rest of the code which assumes ordering as described above. The ordering, however, was fine so long as less than 10 perturbations were used, which is how this error went unnoticed for so long.

Details and comments

I have corrected the _sorted_multisets function in multiset_utils.py with the correct ordering. I've had to use a workaround in which a dummy class is defined that implements the __lt__ dunder method, and then the multisets are sorted using this as the key. This was suggested by @ihincks as the standard way to pass an ordering to the key argument of list.sort (that can't be represented as a single valued key function). I've also added a test that failed before this change, but passes now.

I'm currently labelling this as a draft as I want to take a bit of time to look through the code to see if any un-explicit ordering assumptions are being made. I think one example is line dyson_magnus.py line 838. It currently reads

            lmult_rule.append((np.array([1.0, 1.0]), np.array([[-1, term_idx], [term_idx, -1]])))

but I think it should be

            lmult_rule.append((np.array([1.0, 1.0]), np.array([[-1, term_idx], [perturbation_labels.index(term), -1]])))

qiskit_dynamics/perturbation/multiset_utils.py Outdated Show resolved Hide resolved
qiskit_dynamics/perturbation/multiset_utils.py Outdated Show resolved Hide resolved
qiskit_dynamics/perturbation/multiset_utils.py Outdated Show resolved Hide resolved
@DanPuzzuoli DanPuzzuoli changed the title Fixing multiset ordering bug in perturbation module Fix multiset ordering bug in perturbation module Apr 6, 2023
@DanPuzzuoli DanPuzzuoli marked this pull request as ready for review April 6, 2023 17:13
@DanPuzzuoli
Copy link
Collaborator Author

DanPuzzuoli commented Apr 6, 2023

This PR is ready for review.

Changes:

  • Corrects the multiset sorting function _sorted_multisets as described in the initial comment.
  • Modifies lines 837 and 838 in dyson_magnus.py along the lines as described in the initial comment. This ensures the correct index of the perturbation_labels is used in the lmult rule, and now only adds the rule if term is in perturbation_labels (otherwise it is actually unneeded).
  • Adds sorting tests for _sorted_multisets that would have caught the original bug.
  • Adds tests validating point 2 above:
    • One checks correct exclusion of the unnecessary matrix multiplications.
    • The other checks correct multiplication rule construction in perturbation_labels is not in "canonical" order (this is the only list of multisets that technically shouldn't be assumed to be in canonical order).
  • Adds an "integration" test that also would have caught the original bug. The test case test_dyson_analytic_case1_1d is one of the cheapest-to-compute test cases for solve_lmde_perturbation, and this PR adds test_dyson_analytic_case1_1d_relabeled, which reproduces this test but relabels the perturbations with indices 1 and 10, which is one case where the bug was expressed.

I've verified that all added tests fail if the changes in points 1 and 2 are reverted to what is currently in main.

ihincks
ihincks previously approved these changes Apr 6, 2023
Copy link
Contributor

@ihincks ihincks left a comment

Choose a reason for hiding this comment

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

the parts i understand lgtm

ms1 <= ms2 if len(ms1) < len(ms2), or if
len(ms1) == len(ms2) and if
str(_multiset_to_sorted_list(ms1)) <= str(_multiset_to_sorted_list(ms2)).
class _MultisetSort:
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: consider renaming to _MultisetSortKey

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Changed.

@DanPuzzuoli DanPuzzuoli merged commit 948809a into qiskit-community:main Apr 6, 2023
DanPuzzuoli added a commit to DanPuzzuoli/qiskit-dynamics that referenced this pull request Jun 8, 2023
DanPuzzuoli added a commit that referenced this pull request Jun 9, 2023
* Temporary upper bound on JAX version <=0.4.6 and update to DynamicsBackend tutorial (#210)

* Add links of API reference to tutorials and userguides (#212)

* Fix multiset ordering bug in perturbation module (#211)

Co-authored-by: Ian Hincks <[email protected]>

* Bump Sphinx Theme to 1.11 (#215)

* Bump Sphinx Theme to 1.11

* Also activate jquery

* Bug fix: Measurement properties automatic padding for DynamicsBackend initialization (#209)

Co-authored-by: Daniel Puzzuoli <[email protected]>

* bounding ipython version for compatibility with python 3.8 (#216)

* Update deploy_documentation.sh to deploy in ecosystem (#219)

* Bound Diffrax version (#226)

* Bounding diffrax and equinox versions. The latest versions require
the latest version of JAX, but due to an unresolved bug in JAX,
Dynamics is only compatible with jax<=0.4.6. This commit also
adds a release note stating exactly what versions of these packages
will work with the latest version of dynamics.

* Upgrade to qiskit_sphinx_theme 1.12 (#224)

* Update links to repo and documentation (#227)

* updating minor comments in pulse sim tutorial to remove confusion (#228)

* adding max step size argument to dynamics_backend tutorial, with explanation (#229)

* fixing typo

---------

Co-authored-by: Kento Ueda <[email protected]>
Co-authored-by: Ian Hincks <[email protected]>
Co-authored-by: Eric Arellano <[email protected]>
Co-authored-by: Arthur Strauss <[email protected]>
Co-authored-by: Luciano Bello <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants