From d02040472edc9e5cda85ba7c042b479318e35a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Tue, 24 Jun 2025 11:48:26 +0200 Subject: [PATCH 1/2] Make more tests use a deterministic rng --- CHANGELOG.rst | 2 ++ tests/test_lda.py | 18 ++++++++++-------- tests/test_postprocessing.py | 21 +++++++++++++++------ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7896ac7d..88a0fa3d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,8 @@ Changelog Not released ------------ +* Make more tests deterministic (#204). + v0.6.4 (2025/06/24) ------------------- diff --git a/tests/test_lda.py b/tests/test_lda.py index 5e6ccc1d..e2a18bfa 100644 --- a/tests/test_lda.py +++ b/tests/test_lda.py @@ -450,16 +450,18 @@ def test_simple_format_check(): pois = [[i * npois + e for e in range(npois)] for i in range(nv)] ns = npois * nv + rng = get_rng() + # Wrong traces shape - traces = np.random.randint(0, maxt, (n, npois - 1), dtype=np.int16) - x = np.random.randint(0, nc, (n, nv), dtype=np.uint16) + traces = rng.integers(0, maxt, (n, npois - 1), dtype=np.int16) + x = rng.integers(0, nc, (n, nv), dtype=np.uint16) lda_acc = LdaAcc(nc=nc, pois=pois) with pytest.raises(ScalibError, match="POI out of bounds."): lda_acc.fit_u(traces, x) # Wrong labels shape [too much variables] - traces = np.random.randint(0, maxt, (n, ns), dtype=np.int16) - x = np.random.randint(0, nc, (n, nv + 1), dtype=np.uint16) + traces = rng.integers(0, maxt, (n, ns), dtype=np.int16) + x = rng.integers(0, nc, (n, nv + 1), dtype=np.uint16) lda_acc = LdaAcc(nc=nc, pois=pois) expected_error_msg = ( "Number of variables {} does not match previously-fitted classes*".format( @@ -473,8 +475,8 @@ def test_simple_format_check(): # Not tested, would imply significant impact on performances # Validate matrices shape - traces = np.random.randint(0, maxt, (n, ns), dtype=np.int16) - x = np.random.randint(0, nc, (n, nv), dtype=np.uint16) + traces = rng.integers(0, maxt, (n, ns), dtype=np.int16) + x = rng.integers(0, nc, (n, nv), dtype=np.uint16) lda_acc = LdaAcc(nc=nc, pois=pois) lda_acc.fit_u(traces, x) mus = lda_acc.get_mus() @@ -496,7 +498,7 @@ def test_simple_format_check(): lda = Lda(lda_acc, p=p) # Wrong new traces for predictions - new_traces = np.random.randint(0, 256, (20, ns + 1), dtype=np.int16) + new_traces = rng.integers(0, 256, (20, ns + 1), dtype=np.int16) e_err_msg = "Traces length {} does not match previously-fitted traces*".format( ns + 1 ) @@ -505,7 +507,7 @@ def test_simple_format_check(): # Validate probas shape n_new = 20 - new_traces = np.random.randint(0, 256, (n_new, ns), dtype=np.int16) + new_traces = rng.integers(0, 256, (n_new, ns), dtype=np.int16) pr = lda.predict_proba(new_traces) assert len(pr) == nv for prs in pr: diff --git a/tests/test_postprocessing.py b/tests/test_postprocessing.py index 5335c7ee..41c14d0e 100644 --- a/tests/test_postprocessing.py +++ b/tests/test_postprocessing.py @@ -1,9 +1,10 @@ import pytest import numpy as np -import random from scalib.postprocessing import rank_accuracy import os +from utils_test import get_rng + static_probs = [ [0.5, 0.25, 0.25, 0.125], [0.5, 0.25, 0.125, 0.25], @@ -28,9 +29,10 @@ def test_rank_accuracy_hist(): nc = 256 nsubkeys = 4 acc = 0.2 + rng = get_rng() costs = np.zeros((nsubkeys, nc)) + 0.1 - secret_key = np.random.randint(0, nc, nsubkeys) + secret_key = rng.integers(0, nc, (nsubkeys,)) costs[np.arange(nsubkeys), secret_key] = 1.0 rmin, r, rmax = rank_accuracy( @@ -47,12 +49,13 @@ def test_rank_accuracy_scaled_vs_hist(): nsubkeys = 16 max_error = 0.5 k_probs = np.zeros((nsubkeys, nc)) + rng = get_rng() secret_key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] for j in range(nsubkeys): for i in range(nc): - k_probs[j][i] = 1 / random.randint(2, 20) + k_probs[j][i] = 1 / rng.integers(2, 20, (1,))[0] rmin, r, rmax = rank_accuracy(-np.log10(k_probs), secret_key, method="hist") lrmin, lr, lrmax = (np.log2(rmin), np.log2(r), np.log2(rmax)) @@ -69,8 +72,10 @@ def test_rank_accuracy(): nsubkeys = 4 acc = 0.2 + rng = get_rng() + costs = np.zeros((nsubkeys, nc)) + 0.1 - secret_key = np.random.randint(0, nc, nsubkeys) + secret_key = rng.integers(0, nc, nsubkeys) costs[np.arange(nsubkeys), secret_key] = 1.0 rmin, r, rmax = rank_accuracy(-np.log10(costs), secret_key, acc_bit=acc) @@ -87,11 +92,13 @@ def test_rank_accuracy_scaled_vs_ntl(): max_error = 0.5 k_probs = np.zeros((nsubkeys, nc)) + rng = get_rng() + secret_key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] for j in range(nsubkeys): for i in range(nc): - k_probs[j][i] = 1 / random.randint(2, 20) + k_probs[j][i] = 1 / rng.integers(2, 20, 1)[0] rmin, r, rmax = rank_accuracy(-np.log10(k_probs), secret_key, method="histbignum") lrmin, lr, lrmax = (np.log2(rmin), np.log2(r), np.log2(rmax)) @@ -113,10 +120,12 @@ def test_rank_accuracy_scaled_edge_cases(): k_probs = np.zeros((nsubkeys, nc)) secret_key = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + rng = get_rng() + for j in range(nsubkeys): for i in range(nc): if i < 6: - k_probs[j][i] = 1 / random.randint(2, 5) + k_probs[j][i] = 1 / rng.integers(2, 5, 1)[0] else: k_probs[j][i] = 1 / 16 From 79e858ec888eb78973413aaba7aa1f7828df2bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Cassiers?= Date: Tue, 24 Jun 2025 12:01:06 +0200 Subject: [PATCH 2/2] README --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 1e85808b..553c3836 100644 --- a/README.rst +++ b/README.rst @@ -69,8 +69,7 @@ To install from source: .. code-block:: - git clone https://github.com/simple-crypto/SCALib - pip install ./SCALib + pip install scalib --no-binary scalib See `CONTRIBUTING.rst `__ for advanced build configuration.