From f42a724ad35617bce43ff49f33de15da67d4f66f Mon Sep 17 00:00:00 2001 From: "clement.marsone" Date: Mon, 27 Jul 2026 17:50:08 +0200 Subject: [PATCH 1/3] PCVL-1251: Fix BSCount to BSSamples --- perceval/utils/conversion.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/perceval/utils/conversion.py b/perceval/utils/conversion.py index 86466728d..e399d5b24 100644 --- a/perceval/utils/conversion.py +++ b/perceval/utils/conversion.py @@ -174,10 +174,25 @@ def sample_count_to_samples(sample_count: BSCount, **kwargs) -> BSSamples: :return: the sample list """ + total = sample_count.total() try: count = _deduce_count(**kwargs) except RuntimeError: - count = sum(sample_count.values()) + count = total + if count < 0: + raise RuntimeError(f"A sample count must be positive (got {count})") + if count <= total: + # Shuffle then keep only the requested number of samples + samples = BSSamples() + sample_list = [ + state + for state, nb in sample_count.items() + for _ in range(nb) + ] + random.shuffle(sample_list) + samples.extend(sample_list[:count]) + return samples + # Else, do random sampling return sample_count_to_probs(sample_count).sample(count, non_null=False) From 5bd03af60d530fb8fe74e2cb5fcc154952ec1328 Mon Sep 17 00:00:00 2001 From: "clement.marsone" Date: Tue, 28 Jul 2026 15:11:59 +0200 Subject: [PATCH 2/3] PCVL-1251: shuffle with random sampling with simplified test --- perceval/utils/conversion.py | 29 +++++++++++++++------------- tests/utils/test_utils_conversion.py | 8 ++++---- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/perceval/utils/conversion.py b/perceval/utils/conversion.py index e399d5b24..e1922a12d 100644 --- a/perceval/utils/conversion.py +++ b/perceval/utils/conversion.py @@ -181,19 +181,22 @@ def sample_count_to_samples(sample_count: BSCount, **kwargs) -> BSSamples: count = total if count < 0: raise RuntimeError(f"A sample count must be positive (got {count})") - if count <= total: - # Shuffle then keep only the requested number of samples - samples = BSSamples() - sample_list = [ - state - for state, nb in sample_count.items() - for _ in range(nb) - ] - random.shuffle(sample_list) - samples.extend(sample_list[:count]) - return samples - # Else, do random sampling - return sample_count_to_probs(sample_count).sample(count, non_null=False) + # Shuffle then keep only the requested number of samples + sample_list = [ + state + for state, nb in sample_count.items() + for _ in range(nb) + ] + random.shuffle(sample_list) + samples = BSSamples() + samples.extend(sample_list[:count]) + # If more samples requested then do random sampling + if count > total: + samples.extend( + sample_count_to_probs(sample_count).sample(count - total, + non_null=False) + ) + return samples class ConversionHelper: diff --git a/tests/utils/test_utils_conversion.py b/tests/utils/test_utils_conversion.py index c550edb9a..e5f15ae3e 100644 --- a/tests/utils/test_utils_conversion.py +++ b/tests/utils/test_utils_conversion.py @@ -96,16 +96,16 @@ def test_probs_to_sample_count(count): assert sum(list(output.values())) == count -def test_sample_count_to_samples(): +@pytest.mark.parametrize("count", [1000, 1e9, 170, 1, 0]) +def test_sample_count_to_samples(count): sample_count = BSCount({ b0: 280, b1: 120, b2: 400, b3: 200 }) - samples = sample_count_to_samples(sample_count) - for state, count in sample_count.items(): - assert count * 0.7 < samples.count(state) < count * 1.3 + samples = sample_count_to_samples(sample_count, max_samples=int(count)) + assert len(samples) == count def test_probs_to_samples(): From 2d608ba88e97b73edb4613672b6e8ce9357cde16 Mon Sep 17 00:00:00 2001 From: "clement.marsone" Date: Wed, 29 Jul 2026 09:01:48 +0200 Subject: [PATCH 3/3] PCVL-1251: adaptating test --- tests/utils/test_utils_conversion.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/utils/test_utils_conversion.py b/tests/utils/test_utils_conversion.py index e5f15ae3e..112f3f274 100644 --- a/tests/utils/test_utils_conversion.py +++ b/tests/utils/test_utils_conversion.py @@ -96,7 +96,7 @@ def test_probs_to_sample_count(count): assert sum(list(output.values())) == count -@pytest.mark.parametrize("count", [1000, 1e9, 170, 1, 0]) +@pytest.mark.parametrize("count", [1000, 1000000, 170, 1, 0]) def test_sample_count_to_samples(count): sample_count = BSCount({ b0: 280, @@ -105,6 +105,14 @@ def test_sample_count_to_samples(count): b3: 200 }) samples = sample_count_to_samples(sample_count, max_samples=int(count)) + if count == 1000: + assert samples.count(b0) == 280 + assert samples.count(b1) == 120 + assert samples.count(b2) == 400 + assert samples.count(b3) == 200 + if count > 1000: + for _state, _count in samples_to_sample_count(samples).items(): + assert _count * 0.7 < samples.count(_state) < _count * 1.3 assert len(samples) == count