-
Notifications
You must be signed in to change notification settings - Fork 22
Implement balanced product codes #318
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
base: main
Are you sure you want to change the base?
Conversation
a3e2d5b to
e092a47
Compare
Uses GAP to find all permutations of a matrix that have an orbit of a certain length
Add an option to add padding/fixed points to a permutation matrix returned by to_matrix
These lines can never run
cded71a to
fff6fc3
Compare
|
Sorry about the somewhat messy git history- I had a bunch of type check issues and mocking issues that were a little ugly. I typically develop things in c++, contributing in python is new to me and I still have a lot to learn :) Should be in a good state now |
|
No worries, the effort to pass all checks is greatly appreciated! |
|
Hmm, maybe if it takes a long time to find suitable R and C, and moreover the choice of these matrices is not unique, we should make them inputs to the code (and test validity/compatibility at initialization time). We can then provide tools for finding suitable choices of R and C, and provide example code in the docstring for BalancedProductCode. Also, maybe it would be nice to add an alternative construction based on Eq 4 on page 7 of arXiv:2505.13679? Something like a |
|
Earlier, I was thinking of caching r and c across successive calls, but passing them as inputs makes much more sense. While adding the alternate construction method, I realized it most of the work needed for the distance balancing feature from Section III. So I went ahead and implemented that too, although I set it to default to false since it involves calculating the distance of the input codes. |
|
I implemented the new features and made tests for them. Is there anything else I can improve? |
|
Sorry for the delay! I'll take another look next week 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is all awesome and looking good! I have just a few requests. I may do one more pass over this PR afterwards, but also at some point I'm happy to just merge and perform some additional cleanup myself 🙂
| R: qldpc.abstract.GroupMember, | ||
| C: qldpc.abstract.GroupMember, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we name these something like perm_r and perm_c and accept comb.Permutation | npt.NDArray[np.int_] inputs (with import sympy.combinatorics as comb)? I imagine many users will prefer to pass explicit permutation matrices. Initialization can convert these objects to matrices for validity checks as needed, e.g.
size = binaryMatrix.shape[0]
mat_r = perm_r.to_matrix(size) if isinstance(perm_r, comb.Permutation) else perm_r| self._r = R | ||
| self._c = C |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not save the binaryMatrix to self while we're at it? (Also very minor: please rename to something like seed_matrix)
qldpc/codes/quantum.py
Outdated
| check_z = np.hstack( | ||
| (np.eye(binaryMatrix.shape[1], dtype=int) + self._r.to_matrix(), binaryMatrix) | ||
| ) | ||
| CSSCode.__init__(self, check_x, check_z) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super nit: CSSCode.__init__(self, checks_x, checks_z, field=2, is_subsystem_code=False).
Also, I think we can make checks_z ~ [1 + R, -I] and add a field: int | None = None argument that we pass to CSSCode.__init__ as well. The construction seems to work equally well for arbitrary finite fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you on having an arbitrary field as an argument. Where do you get the alternative definition of checks_z with the negative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's just from me working out on scratch paper where I can put a minus sign to make this a valid CSS code, Hx @ Hz.T = 0 (in words, this condition simply enforces that X-type and Z-type stabilizers commute). We should do the same with the other BPCode construction. Note that the minus sign has no effect for qubits (binary matrices), for which -M = M.
qldpc/codes/quantum.py
Outdated
| """ | ||
| Allows for the creation of balanced codes from a classical code and a CSS code. | ||
|
|
||
| https://arxiv.org/pdf/2505.13679v1 | ||
|
|
||
| If 'distance_balancing', function will determine if x and z errors have the same distance | ||
| and if not, will construct the code to maximize the final distance. This involve exact distance calculations | ||
| which will only work on relatively small codes | ||
|
|
||
| """ | ||
|
|
||
| @classmethod | ||
| def from_codes( | ||
| cls, code_q: CSSCode, code_c: ClassicalCode, distance_balancing: bool = False | ||
| ) -> qldpc.codes.BalancedProductCode: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move the docstring inside the method:
| """ | |
| Allows for the creation of balanced codes from a classical code and a CSS code. | |
| https://arxiv.org/pdf/2505.13679v1 | |
| If 'distance_balancing', function will determine if x and z errors have the same distance | |
| and if not, will construct the code to maximize the final distance. This involve exact distance calculations | |
| which will only work on relatively small codes | |
| """ | |
| @classmethod | |
| def from_codes( | |
| cls, code_q: CSSCode, code_c: ClassicalCode, distance_balancing: bool = False | |
| ) -> qldpc.codes.BalancedProductCode: | |
| @classmethod | |
| def from_codes( | |
| cls, code_q: CSSCode, code_c: ClassicalCode, distance_balancing: bool = False | |
| ) -> qldpc.codes.BalancedProductCode: | |
| """Construct a balanced product code from a quantum CSS code and a classical code. | |
| If 'distance_balancing', this function will determine if x and z errors have the same distance | |
| and if not, it will construct the code to maximize the final distance. This involve exact distance calculations | |
| which will only work on relatively small codes. | |
| See Eq. (4) of https://arxiv.org/pdf/2505.13679v1 | |
| """ |
qldpc/external/groups.py
Outdated
|
|
||
|
|
||
| def get_permutation_symmetry_of_matrix( | ||
| symmetry_length: int, n: int, m: int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we give n and m descriptive names? Also, again the docstring should go inside the function.
qldpc/external/groups.py
Outdated
| def parse_permutation_output(output: str) -> list[qldpc.abstract.GroupMember]: | ||
| perm_list = [] | ||
| for perm in output.strip("[] ").split(", "): | ||
| result = [] | ||
| cycles = re.findall(r"\([^()]+\)", perm) | ||
| for cycle in cycles: | ||
| cycle = cycle.strip("()") | ||
| elements = tuple(int(x) - 1 for x in cycle.split(",")) | ||
| result.append(elements) | ||
| # Remove trivial permutation | ||
| if result: | ||
| perm_list.append(qldpc.abstract.GroupMember.from_sympy(Permutation(result))) | ||
| return perm_list |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this performing the same parsing as in this code? If so, can we factor it out into a common function?
| matrix = np.zeros([self.size] * 2, dtype=int) | ||
| size = dimension if dimension is not None else self.size | ||
| matrix = np.eye(size, dtype=int) | ||
| for ii in range(self.size): | ||
| matrix[ii, self.apply(ii)] = 1 | ||
| j = self.apply(ii) | ||
| # Since using identity matrix not zero, have to clear the row | ||
| matrix[ii, :] = 0 | ||
| matrix[ii, j] = 1 | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep the old construction and do something like
if dimension > len(matrix):
matrix = scipy.linalg.block_diag(matrix, np.eye(dimension - len(matrix), dtype=int)
return matrixAnd maybe dimension should be size?
|
It looks like there are also conflicts that prevent automatic merging into |
|
Oh also: in keeping with naming conventions for quantum codes in |
|
Thanks for the comments! I have time this weekend, so I should have everything good by Monday. |
|
@ryan-kersten could you please make a checklist of your main todo's for this PR? That way somebody else can take over to finish the PR in the future if it turns out that you don't have the time 🙂 |
|
@ryan-kersten alternative request: to open the same PR but from a branch of this repository. That way I can finish it up. |
Closes #305
The main computational complexity in implementing this code was finding permutation R and C where
RM=MC^t for a matrix M, where R and C have an orbit of length L. I've never used GAP before, is there any better way to find these permutations than using 'filtered' to find all the symmetry group elements of a certain order?