Skip to content
This repository has been archived by the owner on Jun 12, 2023. It is now read-only.

Migrate topological codes fitters to use retworkx #552

Merged

Conversation

mtreinish
Copy link
Collaborator

@mtreinish mtreinish commented Jan 28, 2021

Summary

This commit migrates the networkx usage in the topological codes fitters
module away from networkx to use retworkx instead. This should provide
a speedup as retworkx is typically much faster than networkx. But, more
importantly this module is the last usage of networkx in qiskit and that
causes noticeable import overhead as networkx is slow to import. Moving
to retworkx like the rest of Qiskit will remove this overhead.

Details and comments

TODO:

  • Fix implementation (tests are failing so there are definite bugs here or in retworkx's max_weight_matching)
  • Update tox.ini after retworkx 0.8.0 release to not use dev branch adding max_weight_matching (Add max weight matching function Qiskit/rustworkx#229)
  • Leverage rx.PyGraph(multigraph=False) instead of tracking edges manually with a set
  • Add release notes about the requirements change

This commit migrates the networkx usage in the topological codes fitters
module away from networkx to use retworkx instead. This should provide
a speedup as retworkx is typically much faster than networkx. But, more
importantly this module is the last usage of networkx in qiksit and that
causes noticeable import overhead as networkx is slow to import. Moving
to retworkx like the rest of Qiskit will remove this overhead.
@mtreinish
Copy link
Collaborator Author

@quantumjim I'm having a hard time debugging the failures I'm hitting here. retworkx behaves a bit differently than networkx nodes are integer indexed (like an array except there can be holes in case of node removals) and the networkx code in the fitters depends heavily on the associative nature of networkx (access nodes by the payload). If you have a simpler or more deterministic test case than what's in the unit tests that would be really helpful.

@mtreinish
Copy link
Collaborator Author

ok, now I'm really confused the one topological codes test passes on the linux 3.6 and 3.7 job: https://github.com/Qiskit/qiskit-ignis/pull/552/checks?check_run_id=1787089496#step:6:571 and https://github.com/Qiskit/qiskit-ignis/pull/552/checks?check_run_id=1787089459#step:6:609

But it's failing reliably on Python 3.8 locally for me and in CI on python 3.8 for macOS: https://github.com/Qiskit/qiskit-ignis/pull/552/checks?check_run_id=1787089507#step:6:653

There shouldn't be any environment difference like this in the changes here since nothing should be dependent on a specific python version or OS in the diff here or on the retworkx side.

@mtreinish mtreinish added the on hold Can not fix yet label Jan 28, 2021
@quantumjim
Copy link
Member

I'm guessing that the max_weight_matching function of retworkx fails in strange platform specific ways that (presumably) don't show up in these tests.

The matching problem that needs to be solved here is finding a minimum weight matching that must be perfect, but only on a certain subset of nodes. It was done by using the max_weight_matching of networkx, but with negative weights

The max_weight_matching function is used to do minimum weight matching, simply by making the weights negative. Perfect matching is enforced with maxcardinality=True, and the 'imperfect' bits are dealt with by a hack that involves weights that are zero. So I'd suggest trying adding some more tests to retworkx under these conditions.

I'd like to find some more specific examples, but I had trouble installing your fork of Ignis (and the corresponding version of retworkx) so I can't check for cases of bad matchings myself. But just to suggest something, how does it deal with a case like the following?

from qiskit.ignis.verification.topological_codes import *
from qiskit import execute, Aer, QuantumCircuit

code = RepetitionCode(25,1)

errors = RepetitionCode(25,0).circuit['0']
for j, bit in enumerate('0111001110000000111001110'):
    if bit=='1':
        errors.x(errors.qregs[1][j])
        
raw_results = {'0':execute(errors+code.circuit['0'],Aer.get_backend('qasm_simulator')).result().get_counts()}
string = list(code.process_results(raw_results)['0'].keys())[0]

#decoder = GraphDecoder(code)

assert decoder.matching(string)=='0 0'

@mtreinish
Copy link
Collaborator Author

Yeah I think you're right. I managed to find some cases where the retworkx max_weight_matching is differing from the networkx result (and I assume networkx is correct). I added more tests to the PR (see: Qiskit/rustworkx@0c6ea26 ) that fail I'll work on fixing that in the PR and rerun things here when I have a fix (it might be a while, that algorithm is not simple).

As for testing this locally it shouldn't be too bad to install. For retworkx you just need to have the rust compiler installed on your local system (rustup makes this easy: https://rustup.rs/) then you can run: pip install git+https://github.com/mtreinish/retworkx@max_weight_matching which should build and install it from the PR branch. The ignis PR you can just checkout locally with: git pull https://github.com/mtreinish/qiskit-ignis.git retworkx-topological-codes

I did run your example locally and it does fail, the matching() call returns 0 1 instead of the expected 0 0. So I think all signs point to a bug in the retworkx PR adding max_weight_matching.

@mtreinish
Copy link
Collaborator Author

mtreinish commented Feb 24, 2021

@quantumjim ok, I've got this to the point where your example script is passing locally now. There was a bug in getting the distance from the syndrome graph and putting it into the error graph. However the unit tests are still failing (although it seems like a nondeterministic failure, I can get it to pass some of the time).

I don't think it's an issue Qiskit/rustworkx#229 anymore, mostly because I ramped up the testing in that PR even more so it's testing random graphs against networkx's output and it's the same most of the time (and when it's not there are multiple maximum weight matchings and the retworkx code is still picking a valid matching).

@quantumjim
Copy link
Member

It's giving out error rates of around 0.05 that should be more like 0.001. So there is definitely something wrong. I'd guess that the passes are lucky coincidences.

For a test that's deterministic and more specific, I'd suggest trying out all single Pauli errors individually to verify that they are corrected with certainty.

Here's a script that does that
https://gist.github.com/quantumjim/40d6616d61f53d3905aeb534e29eaf13
I don't have time at the moment to do all the fiddly details of turning this into part of the proper test. But it would be interesting to know if it runs well with the retworkx version.

@mtreinish
Copy link
Collaborator Author

Thanks that script was helpful and I figured out my bug. I forgot to tell retworkx how to get an integer weight from an edge, so it was incorrectly defaulting all the edge weights to 1. I added the missing weight_fn kwarg to the function call and it fixes it. Thanks for the help.

@mtreinish mtreinish changed the title [WIP] Migrate topological codes fitters to use retworkx Migrate topological codes fitters to use retworkx Feb 25, 2021
@mtreinish mtreinish removed the on hold Can not fix yet label Mar 2, 2021
@mtreinish
Copy link
Collaborator Author

retworkx 0.8.0 was released earlier today that included the max_weight_matching function so this is ready for review now.

@chriseclectic chriseclectic merged commit 1537c75 into qiskit-community:master Mar 3, 2021
@mtreinish mtreinish deleted the retworkx-topological-codes branch March 3, 2021 14:25
@mtreinish mtreinish added the Changelog: API Change Include in the Changed section of the changelog label Apr 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Changelog: API Change Include in the Changed section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants