feat: add PEG base matrix constructor with shift optimization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -512,6 +512,195 @@ def optimize_degree_distribution(m_base=7, lam_b=0.1, top_k=10,
|
||||
return results[:top_k]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PEG Base Matrix Constructor
|
||||
# =============================================================================
|
||||
|
||||
def _build_full_h_from_base(H_base, z=32):
|
||||
"""Expand a QC base matrix to a full binary parity-check matrix."""
|
||||
m_base_local = H_base.shape[0]
|
||||
n_base_local = H_base.shape[1]
|
||||
m_full = m_base_local * z
|
||||
n_full = n_base_local * z
|
||||
H_full = np.zeros((m_full, n_full), dtype=np.int8)
|
||||
for r in range(m_base_local):
|
||||
for c in range(n_base_local):
|
||||
shift = int(H_base[r, c])
|
||||
if shift < 0:
|
||||
continue
|
||||
for zz in range(z):
|
||||
col_idx = c * z + (zz + shift) % z
|
||||
H_full[r * z + zz, col_idx] = 1
|
||||
return H_full
|
||||
|
||||
|
||||
def _generate_random_pattern(vn_degrees, m_base):
|
||||
"""
|
||||
Generate a random connection pattern matching target VN degrees.
|
||||
|
||||
Keeps the staircase backbone fixed and randomly places extra connections
|
||||
among available positions.
|
||||
"""
|
||||
n_base = len(vn_degrees)
|
||||
pattern = -np.ones((m_base, n_base), dtype=np.int16)
|
||||
|
||||
# Col 0 (info) connects to all rows
|
||||
for r in range(m_base):
|
||||
pattern[r, 0] = 0
|
||||
|
||||
# Staircase backbone
|
||||
for r in range(m_base):
|
||||
pattern[r, r + 1] = 0
|
||||
for r in range(1, m_base):
|
||||
pattern[r, r] = 0
|
||||
|
||||
# Current degrees from backbone
|
||||
current_degrees = [0] * n_base
|
||||
for r in range(m_base):
|
||||
for c in range(n_base):
|
||||
if pattern[r, c] >= 0:
|
||||
current_degrees[c] += 1
|
||||
|
||||
# Randomly place extra connections
|
||||
for c in range(1, n_base):
|
||||
needed = vn_degrees[c] - current_degrees[c]
|
||||
if needed <= 0:
|
||||
continue
|
||||
|
||||
available = [r for r in range(m_base) if pattern[r, c] < 0]
|
||||
if len(available) < needed:
|
||||
continue
|
||||
chosen = np.random.choice(available, size=needed, replace=False)
|
||||
for r in chosen:
|
||||
pattern[r, c] = 0
|
||||
|
||||
return pattern
|
||||
|
||||
|
||||
def construct_base_matrix(vn_degrees, z=32, n_trials=1000):
|
||||
"""
|
||||
Construct a concrete base matrix with circulant shifts optimized
|
||||
for maximum girth and guaranteed full rank.
|
||||
|
||||
Algorithm:
|
||||
1. Try multiple random connection placements (extras beyond staircase)
|
||||
2. For each placement, try random shift assignments
|
||||
3. Keep the best (full-rank, highest girth) result
|
||||
|
||||
Returns (H_base, girth).
|
||||
"""
|
||||
from ldpc_analysis import compute_girth
|
||||
|
||||
m_base = len(vn_degrees) - 1
|
||||
n_base = len(vn_degrees)
|
||||
assert vn_degrees[0] == m_base
|
||||
|
||||
expected_rank = m_base * z
|
||||
best_H = None
|
||||
best_girth = 0
|
||||
best_has_rank = False
|
||||
|
||||
# Divide trials between placement variations and shift optimization
|
||||
n_patterns = max(10, n_trials // 20)
|
||||
n_shifts_per_pattern = max(20, n_trials // n_patterns)
|
||||
|
||||
for _p in range(n_patterns):
|
||||
pattern = _generate_random_pattern(vn_degrees, m_base)
|
||||
|
||||
positions = [(r, c) for r in range(m_base) for c in range(n_base)
|
||||
if pattern[r, c] >= 0]
|
||||
|
||||
for _s in range(n_shifts_per_pattern):
|
||||
H_candidate = pattern.copy()
|
||||
for r, c in positions:
|
||||
H_candidate[r, c] = np.random.randint(0, z)
|
||||
|
||||
# Check rank
|
||||
H_full = _build_full_h_from_base(H_candidate, z)
|
||||
rank = np.linalg.matrix_rank(H_full.astype(float))
|
||||
has_full_rank = rank >= expected_rank
|
||||
|
||||
if has_full_rank and not best_has_rank:
|
||||
girth = compute_girth(H_candidate, z)
|
||||
best_girth = girth
|
||||
best_H = H_candidate.copy()
|
||||
best_has_rank = True
|
||||
elif has_full_rank and best_has_rank:
|
||||
girth = compute_girth(H_candidate, z)
|
||||
if girth > best_girth:
|
||||
best_girth = girth
|
||||
best_H = H_candidate.copy()
|
||||
elif not best_has_rank:
|
||||
girth = compute_girth(H_candidate, z)
|
||||
if best_H is None or girth > best_girth:
|
||||
best_girth = girth
|
||||
best_H = H_candidate.copy()
|
||||
|
||||
if best_has_rank and best_girth >= 8:
|
||||
return best_H, best_girth
|
||||
|
||||
if best_H is None:
|
||||
# Fallback: return pattern with zero shifts
|
||||
best_H = _generate_random_pattern(vn_degrees, m_base)
|
||||
best_girth = compute_girth(best_H, z)
|
||||
|
||||
return best_H, best_girth
|
||||
|
||||
|
||||
def verify_matrix(H_base, z=32):
|
||||
"""
|
||||
Comprehensive validity checks for a base matrix.
|
||||
|
||||
Returns dict with check results.
|
||||
"""
|
||||
from ldpc_analysis import compute_girth, peg_encode
|
||||
|
||||
m_base_local = H_base.shape[0]
|
||||
n_base_local = H_base.shape[1]
|
||||
k = z
|
||||
|
||||
# Build full matrix
|
||||
H_full = _build_full_h_from_base(H_base, z)
|
||||
|
||||
# Column degrees
|
||||
col_degrees = []
|
||||
for c in range(n_base_local):
|
||||
col_degrees.append(int(np.sum(H_base[:, c] >= 0)))
|
||||
|
||||
# Full rank check
|
||||
expected_rank = m_base_local * z
|
||||
actual_rank = np.linalg.matrix_rank(H_full.astype(float))
|
||||
full_rank = actual_rank >= expected_rank
|
||||
|
||||
# Parity submatrix rank (columns k onwards)
|
||||
H_parity = H_full[:, k:]
|
||||
parity_rank = np.linalg.matrix_rank(H_parity.astype(float))
|
||||
parity_full_rank = parity_rank >= min(H_parity.shape)
|
||||
|
||||
# Girth
|
||||
girth = compute_girth(H_base, z)
|
||||
|
||||
# Encoding test
|
||||
encodable = False
|
||||
try:
|
||||
info = np.random.randint(0, 2, k).astype(np.int8)
|
||||
codeword = peg_encode(info, H_base, H_full, z=z)
|
||||
syndrome = H_full @ codeword % 2
|
||||
encodable = np.all(syndrome == 0)
|
||||
except Exception:
|
||||
encodable = False
|
||||
|
||||
return {
|
||||
'col_degrees': col_degrees,
|
||||
'full_rank': full_rank,
|
||||
'actual_rank': actual_rank,
|
||||
'expected_rank': expected_rank,
|
||||
'parity_rank': parity_full_rank,
|
||||
'girth': girth,
|
||||
'encodable': encodable,
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CLI placeholder (will be extended in later tasks)
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user