Cyclic Hypergraph Product Codes - #431
Conversation
perlinm
left a comment
There was a problem hiding this comment.
Thank you for the contribution! This mostly looks good to me. I have just one proposal regarding the definition of a CHGPCode rather than a C2Code.
Otherwise, I sprinkled some notes for myself to address in a future PR.
| # Circulant matrix Q. | ||
| Id = galois.GF(field or DEFAULT_FIELD_ORDER).Identity(bits) | ||
| Q = Id[:, (np.arange(bits) - 1) % bits] | ||
|
|
||
| # poly(Q). | ||
| matrix = 0 * Id | ||
| for term in poly.as_terms()[0]: | ||
| coeff, exponent = term[1][0][0], term[1][1][0] | ||
| matrix += int(coeff) * np.linalg.matrix_power(Q, exponent) |
There was a problem hiding this comment.
Some thoughts (out of scope for this PR):
This is pretty clean, but I worry there may be some edge cases it does not catch. I struggled a lot with edge cases for the construction of codes.QCCodes, here, though honestly:
- my code there is likely over-engineered, and could use detailed review/refactor by somebody more deeply familiar with
sympy, and qldpcshould factor out the code that converts a Sympy polynomial into anabstract.RingMember, moving it toabstract.py(probably inside theRingMemberclass), and re-use that code here. I'll leave that for a future PR, though.
There was a problem hiding this comment.
I added #432, which we should be able to use here. I'm happy to make that change in a follow-up PR, though.
| with pytest.raises(ValueError, match="not a polynomial"): | ||
| codes.CyclicCode(3, 4) |
There was a problem hiding this comment.
Minor notes for myself (for me to address in a follow-up PR):
- Arguably 4 is a trivial polynomial that would get lifted to an identity matrix (or rather, a multiple thereof), so I would sooner raise the error here that the polynomial is not univariate.
- Leaning into the interpretation of 4 as a trivial polynomial, another error that could be thrown here is that 4 is not a member of
GF(3). However, ifpis prime, then every integer has an unambiguous interpretation as an element ofGF(p)that we should arguably allow (namely,c --> c % p). I think the conversion of polynomials intoRingMembers (currently buried inQCCode) does not support coefficientscthat don't satisfy0 <= c < p, in which case we should modify the conversion to support more integers. The conversion should likewise allow for negative integers (for non-prime fields), if it does not already.
There was a problem hiding this comment.
Regarding point (2) above, this is now dealt with in #432.
| class C2Code(HGPCode): | ||
| """Symmetric cyclic hypergraph product code. | ||
|
|
||
| A C2Code is a hypergraph square of a CyclicCode. | ||
|
|
||
| References: | ||
| - Definition 2 of https://arxiv.org/pdf/2511.09683 | ||
| """ | ||
|
|
||
| def __init__(self, bits: int, poly: sympy.Basic, field: int | None = None) -> None: | ||
| """Construct a C2Code from a block length and a polynomial in one variable.""" | ||
|
|
||
| code = CyclicCode(bits, poly, field) | ||
|
|
||
| super().__init__(code, code, field) | ||
|
|
||
|
|
||
| class CRCode(HGPCode): |
There was a problem hiding this comment.
There is a naming "almost collision" with C4Code and C6Code, which are fixed codes rather than code families. Also, what about CxC codes?
I would propose we define a CHGPCode that accepts poly_a: sympy.Basic, poly_b: sympy.Basic | None = None. If poly_b is None, then poly_b is set to poly_a, making it a C2 code. There now need to be two block lengths, though, so maybe we can provide a bits: int | tuple[int, int] input to likewise determine the block length of both cyclic codes.
CRCode is different enough that it should probably kept as is.
This adds classical
CyclicCodeand quantumCHGPCode, CRCode.This closes issue #400.