Skip to content

Commit 1b2822b

Browse files
committed
fix
1 parent cfa7fd0 commit 1b2822b

4 files changed

Lines changed: 156 additions & 8 deletions

File tree

crates/pecos-relay-bp/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
pub mod builder;
1111
pub mod config;
12-
pub mod convert;
12+
pub(crate) mod convert;
1313
pub mod core_traits;
1414
pub mod decoder;
1515
pub mod errors;

python/pecos-rslib/pecos_rslib.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,7 @@ class decoders:
15411541
...
15421542

15431543
def gamma0(self, val: float | None) -> decoders.RelayBpBuilder:
1544-
"""Set initial damping factor (default: 0.9, None = disabled)."""
1544+
"""Set initial damping factor (None = disabled)."""
15451545
...
15461546

15471547
def pre_iter(self, val: int) -> decoders.RelayBpBuilder:

python/pecos-rslib/src/decoder_bindings.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,16 +1615,28 @@ fn parse_stopping_criterion(s: &str) -> PyResult<RustStoppingCriterion> {
16151615
}
16161616

16171617
/// Convert a dense check matrix from Python lists to an ndarray `Array2`.
1618-
fn dense_check_matrix_to_array2(check_matrix: &[Vec<u8>]) -> Array2<u8> {
1618+
///
1619+
/// # Errors
1620+
///
1621+
/// Returns `PyValueError` if the rows have inconsistent lengths.
1622+
fn dense_check_matrix_to_array2(check_matrix: &[Vec<u8>]) -> PyResult<Array2<u8>> {
16191623
let rows = check_matrix.len();
16201624
let cols = if rows > 0 { check_matrix[0].len() } else { 0 };
1625+
for (i, row) in check_matrix.iter().enumerate() {
1626+
if row.len() != cols {
1627+
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
1628+
"check_matrix row {i} has length {} but row 0 has length {cols}",
1629+
row.len()
1630+
)));
1631+
}
1632+
}
16211633
let mut arr = Array2::<u8>::zeros((rows, cols));
16221634
for (i, row) in check_matrix.iter().enumerate() {
16231635
for (j, &val) in row.iter().enumerate() {
16241636
arr[[i, j]] = val;
16251637
}
16261638
}
1627-
arr
1639+
Ok(arr)
16281640
}
16291641

16301642
/// Builder for Relay BP ensemble decoder.
@@ -1676,7 +1688,7 @@ impl PyRelayBpBuilder {
16761688
error_priors,
16771689
max_iter: 200,
16781690
alpha: None,
1679-
gamma0: Some(0.9),
1691+
gamma0: None,
16801692
pre_iter: 80,
16811693
num_sets: 300,
16821694
set_max_iter: 60,
@@ -1703,7 +1715,7 @@ impl PyRelayBpBuilder {
17031715
slf
17041716
}
17051717

1706-
/// Set initial damping factor (default: 0.9, None = disabled).
1718+
/// Set initial damping factor (None = disabled).
17071719
///
17081720
/// Returns:
17091721
/// Self for method chaining.
@@ -1768,7 +1780,7 @@ impl PyRelayBpBuilder {
17681780
/// `RuntimeError`: If the configuration is invalid.
17691781
fn build(&self) -> PyResult<PyRelayBpDecoder> {
17701782
let stopping_criterion = parse_stopping_criterion(&self.stopping)?;
1771-
let arr = dense_check_matrix_to_array2(&self.check_matrix);
1783+
let arr = dense_check_matrix_to_array2(&self.check_matrix)?;
17721784

17731785
RustRelayBpBuilder::new(&arr.view())
17741786
.error_priors(&self.error_priors)
@@ -1936,7 +1948,7 @@ impl PyMinSumBpBuilder {
19361948
/// Raises:
19371949
/// `RuntimeError`: If the configuration is invalid.
19381950
fn build(&self) -> PyResult<PyMinSumBpDecoder> {
1939-
let arr = dense_check_matrix_to_array2(&self.check_matrix);
1951+
let arr = dense_check_matrix_to_array2(&self.check_matrix)?;
19401952

19411953
RustMinSumBpBuilder::new(&arr.view())
19421954
.error_priors(&self.error_priors)

python/quantum-pecos/tests/pecos/decoders/test_decoder_bindings.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,5 +359,141 @@ def test_methods(self) -> None:
359359
assert result_peel is not None
360360

361361

362+
class TestMinSumBpDecoder:
363+
"""Tests for MinSumBpDecoder via MinSumBpBuilder."""
364+
365+
def test_create_decoder(self) -> None:
366+
"""Test construction via builder."""
367+
from pecos_rslib.decoders import MinSumBpBuilder
368+
369+
H = [[1, 1, 0], [0, 1, 1]]
370+
decoder = MinSumBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).build()
371+
372+
assert decoder is not None
373+
374+
def test_decode_trivial(self) -> None:
375+
"""Test decoding zero syndrome."""
376+
from pecos_rslib.decoders import MinSumBpBuilder
377+
378+
H = [[1, 1, 0], [0, 1, 1]]
379+
decoder = MinSumBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).build()
380+
381+
result = decoder.decode([0, 0])
382+
assert result.converged
383+
assert result.decoding == [0, 0, 0]
384+
385+
def test_decode_single_error(self) -> None:
386+
"""Test decoding a single-error syndrome."""
387+
from pecos_rslib.decoders import MinSumBpBuilder
388+
389+
H = [[1, 1, 0], [0, 1, 1]]
390+
decoder = MinSumBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).build()
391+
392+
result = decoder.decode([1, 0])
393+
assert result.converged
394+
assert result.decoding == [1, 0, 0]
395+
396+
def test_builder_chaining(self) -> None:
397+
"""Test builder method chaining."""
398+
from pecos_rslib.decoders import MinSumBpBuilder
399+
400+
H = [[1, 1, 0], [0, 1, 1]]
401+
decoder = MinSumBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).max_iter(100).alpha(0.8).gamma0(0.5).build()
402+
403+
result = decoder.decode([0, 0])
404+
assert result.converged
405+
406+
def test_properties(self) -> None:
407+
"""Test check_count and bit_count properties."""
408+
from pecos_rslib.decoders import MinSumBpBuilder
409+
410+
H = [[1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 1]]
411+
decoder = MinSumBpBuilder(H, error_priors=[0.01, 0.01, 0.01, 0.01]).build()
412+
413+
assert decoder.check_count == 3
414+
assert decoder.bit_count == 4
415+
416+
417+
class TestRelayBpDecoder:
418+
"""Tests for RelayBpDecoder via RelayBpBuilder."""
419+
420+
def test_create_decoder(self) -> None:
421+
"""Test construction via builder."""
422+
from pecos_rslib.decoders import RelayBpBuilder
423+
424+
H = [[1, 1, 0], [0, 1, 1]]
425+
decoder = RelayBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).build()
426+
427+
assert decoder is not None
428+
429+
def test_decode_trivial(self) -> None:
430+
"""Test decoding zero syndrome."""
431+
from pecos_rslib.decoders import RelayBpBuilder
432+
433+
H = [[1, 1, 0], [0, 1, 1]]
434+
decoder = RelayBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).seed(42).build()
435+
436+
result = decoder.decode([0, 0])
437+
assert result.converged
438+
assert result.decoding == [0, 0, 0]
439+
440+
def test_decode_single_error(self) -> None:
441+
"""Test decoding a single-error syndrome."""
442+
from pecos_rslib.decoders import RelayBpBuilder
443+
444+
H = [[1, 1, 0], [0, 1, 1]]
445+
decoder = RelayBpBuilder(H, error_priors=[0.003, 0.003, 0.003]).seed(42).build()
446+
447+
result = decoder.decode([1, 0])
448+
assert result.converged
449+
assert result.decoding == [1, 0, 0]
450+
451+
def test_builder_chaining(self) -> None:
452+
"""Test builder method chaining with relay-specific options."""
453+
from pecos_rslib.decoders import RelayBpBuilder
454+
455+
H = [[1, 1, 0], [0, 1, 1]]
456+
decoder = (
457+
RelayBpBuilder(H, error_priors=[0.003, 0.003, 0.003])
458+
.max_iter(100)
459+
.gamma0(0.9)
460+
.pre_iter(40)
461+
.num_sets(20)
462+
.set_max_iter(30)
463+
.seed(42)
464+
.stopping("n_conv_1")
465+
.build()
466+
)
467+
468+
result = decoder.decode([0, 0])
469+
assert result.converged
470+
471+
def test_properties(self) -> None:
472+
"""Test check_count and bit_count properties."""
473+
from pecos_rslib.decoders import RelayBpBuilder
474+
475+
H = [[1, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 1]]
476+
decoder = RelayBpBuilder(H, error_priors=[0.01, 0.01, 0.01, 0.01]).build()
477+
478+
assert decoder.check_count == 3
479+
assert decoder.bit_count == 4
480+
481+
def test_seed_reproducibility(self) -> None:
482+
"""Test that same seed gives same results."""
483+
from pecos_rslib.decoders import RelayBpBuilder
484+
485+
H = [[1, 1, 0], [0, 1, 1]]
486+
priors = [0.003, 0.003, 0.003]
487+
488+
d1 = RelayBpBuilder(H, error_priors=priors).seed(123).build()
489+
d2 = RelayBpBuilder(H, error_priors=priors).seed(123).build()
490+
491+
r1 = d1.decode([1, 0])
492+
r2 = d2.decode([1, 0])
493+
494+
assert r1.decoding == r2.decoding
495+
assert r1.converged == r2.converged
496+
497+
362498
if __name__ == "__main__":
363499
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)