Skip to content

Commit c3a6db5

Browse files
authored
edits conftest to run single thread for hac (#1070)
1 parent 3595cb9 commit c3a6db5

File tree

1 file changed

+31
-47
lines changed

1 file changed

+31
-47
lines changed

tests/conftest.py

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,34 @@
11
"Pytest configuration for pyfixest tests."
22

33
import os
4-
5-
import pytest
6-
7-
8-
@pytest.fixture(scope="session", autouse=True)
9-
def single_thread_blas():
10-
"""
11-
Force single-threaded BLAS for deterministic HAC standard errors.
12-
13-
What Claude says:
14-
15-
Multi-threaded BLAS libraries (OpenBLAS, MKL, Accelerate) can produce
16-
slightly different numerical results due to different parallel reduction
17-
orders when computing matrix multiplications. This causes sporadic test
18-
failures in HAC variance calculations even though both R and Python
19-
implementations are mathematically correct.
20-
21-
The differences arise because floating-point arithmetic is not associative:
22-
(a + b) + c ≠ a + (b + c) in IEEE 754. Different thread scheduling can
23-
change the order of operations, leading to different rounding errors.
24-
25-
By forcing single-threaded execution, we ensure deterministic results
26-
that match R's fixest package exactly.
27-
"""
28-
# Store original values to restore after tests
29-
original_values = {}
30-
31-
env_vars = [
32-
"OMP_NUM_THREADS",
33-
"OPENBLAS_NUM_THREADS",
34-
"MKL_NUM_THREADS",
35-
"VECLIB_MAXIMUM_THREADS",
36-
"NUMEXPR_NUM_THREADS",
37-
]
38-
39-
for var in env_vars:
40-
original_values[var] = os.environ.get(var)
41-
os.environ[var] = "1"
42-
43-
yield
44-
45-
# Restore original values after all tests complete
46-
for var, value in original_values.items():
47-
if value is None:
48-
os.environ.pop(var, None)
49-
else:
50-
os.environ[var] = value
4+
import sys
5+
6+
# Force single-threaded BLAS for deterministic HAC standard errors.
7+
#
8+
# Multi-threaded BLAS libraries (OpenBLAS, MKL, Accelerate) can produce
9+
# slightly different numerical results due to different parallel reduction
10+
# orders when computing matrix multiplications. This causes sporadic test
11+
# failures in HAC variance calculations even though both R and Python
12+
# implementations are mathematically correct.
13+
#
14+
# The differences arise because floating-point arithmetic is not associative:
15+
# (a + b) + c ≠ a + (b + c) in IEEE 754. Different thread scheduling can
16+
# change the order of operations, leading to different rounding errors.
17+
#
18+
# By forcing single-threaded execution, we ensure deterministic results
19+
# that match R's fixest package exactly.
20+
#
21+
# This must be set at module level (before numpy/scipy imports) because
22+
# BLAS libraries read these environment variables when first initialized.
23+
24+
# Check if HAC tests are being run
25+
_run_hac_tests = any(
26+
arg in sys.argv for arg in ["test_hac_vs_fixest.py", "-m hac", "--markers=hac"]
27+
) or any("test_hac_vs_fixest" in arg for arg in sys.argv)
28+
29+
if _run_hac_tests:
30+
os.environ["OMP_NUM_THREADS"] = "1"
31+
os.environ["OPENBLAS_NUM_THREADS"] = "1"
32+
os.environ["MKL_NUM_THREADS"] = "1"
33+
os.environ["VECLIB_MAXIMUM_THREADS"] = "1"
34+
os.environ["NUMEXPR_NUM_THREADS"] = "1"

0 commit comments

Comments
 (0)