Replace Python numerical kernels with native C execution
This commit is contained in:
1 parent
48da6be21c
commit
3b38f73fe0
227 files changed
+16801
-75499
No files matched your search
@@ -8,7 +8,6 @@ from unittest.mock import patch
|
||||
from app.main import _app_lifespan, app
|
||||
from app.simulation.warmup import (
|
||||
SimulationWarmupReport,
|
||||
_run_numerical_warmup,
|
||||
_reset_simulation_warmup_for_tests,
|
||||
warm_up_simulation_runtime,
|
||||
)
|
||||
@@ -17,6 +16,9 @@ from app.simulation.warmup import (
|
||||
class SimulationWarmupTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
_reset_simulation_warmup_for_tests()
|
||||
self.engine_environment = patch.dict(os.environ, {"SIMULATION_NUMERIC_ENGINE": "native"})
|
||||
self.engine_environment.start()
|
||||
self.addCleanup(self.engine_environment.stop)
|
||||
|
||||
def tearDown(self) -> None:
|
||||
_reset_simulation_warmup_for_tests()
|
||||
@@ -24,7 +26,7 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
def test_warmup_runs_only_once_per_process(self) -> None:
|
||||
with (
|
||||
patch.dict(os.environ, {"SIMULATIONAPP_WARMUP": "on"}),
|
||||
patch("app.simulation.warmup._run_numerical_warmup") as run_warmup,
|
||||
patch("app.simulation.warmup._run_native_warmup") as run_warmup,
|
||||
):
|
||||
first = warm_up_simulation_runtime()
|
||||
second = warm_up_simulation_runtime()
|
||||
@@ -36,7 +38,7 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
def test_disabled_warmup_does_not_touch_numerical_runtime(self) -> None:
|
||||
with (
|
||||
patch.dict(os.environ, {"SIMULATIONAPP_WARMUP": "off"}),
|
||||
patch("app.simulation.warmup._run_numerical_warmup") as run_warmup,
|
||||
patch("app.simulation.warmup._run_native_warmup") as run_warmup,
|
||||
):
|
||||
report = warm_up_simulation_runtime()
|
||||
|
||||
@@ -47,7 +49,7 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
with (
|
||||
patch.dict(os.environ, {"SIMULATIONAPP_WARMUP": "on"}),
|
||||
patch(
|
||||
"app.simulation.warmup._run_numerical_warmup",
|
||||
"app.simulation.warmup._run_native_warmup",
|
||||
side_effect=RuntimeError("broken warmup"),
|
||||
),
|
||||
self.assertLogs("app.simulation.warmup", level="ERROR"),
|
||||
@@ -61,7 +63,7 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
with (
|
||||
patch.dict(os.environ, {"SIMULATIONAPP_WARMUP": "on"}),
|
||||
patch(
|
||||
"app.simulation.warmup._run_numerical_warmup",
|
||||
"app.simulation.warmup._run_native_warmup",
|
||||
side_effect=MemoryError("out of memory"),
|
||||
),
|
||||
self.assertRaises(MemoryError),
|
||||
@@ -77,6 +79,7 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
return_value=report,
|
||||
) as warmup:
|
||||
async with _app_lifespan(app):
|
||||
self.assertEqual(app.state.simulation_numeric_engine, "native")
|
||||
self.assertEqual(
|
||||
app.state.simulation_warmup,
|
||||
report.as_dict(),
|
||||
@@ -85,6 +88,31 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
|
||||
asyncio.run(enter_lifespan())
|
||||
|
||||
def test_default_startup_checks_native_without_python_warmup(self) -> None:
|
||||
with (
|
||||
patch.dict(os.environ, {"SIMULATIONAPP_WARMUP": "on"}),
|
||||
patch("app.simulation.warmup._run_native_warmup") as native,
|
||||
):
|
||||
os.environ.pop("SIMULATION_NUMERIC_ENGINE", None)
|
||||
|
||||
async def enter_lifespan() -> None:
|
||||
async with _app_lifespan(app):
|
||||
self.assertEqual(app.state.simulation_numeric_engine, "native")
|
||||
self.assertEqual(app.state.simulation_warmup["status"], "completed")
|
||||
|
||||
asyncio.run(enter_lifespan())
|
||||
native.assert_called_once_with()
|
||||
|
||||
def test_native_failure_does_not_fall_back_to_python(self) -> None:
|
||||
with (
|
||||
patch.dict(os.environ, {"SIMULATION_NUMERIC_ENGINE": "native", "SIMULATIONAPP_WARMUP": "on"}),
|
||||
patch("app.simulation.warmup._run_native_warmup", side_effect=RuntimeError("C compiler unavailable")),
|
||||
self.assertLogs("app.simulation.warmup", level="ERROR"),
|
||||
):
|
||||
report = warm_up_simulation_runtime()
|
||||
self.assertEqual(report.status, "failed")
|
||||
self.assertIn("C compiler unavailable", report.error or "")
|
||||
|
||||
def test_real_numerical_warmup_completes(self) -> None:
|
||||
with patch.dict(os.environ, {"SIMULATIONAPP_WARMUP": "on"}):
|
||||
report = warm_up_simulation_runtime()
|
||||
@@ -92,29 +120,6 @@ class SimulationWarmupTests(unittest.TestCase):
|
||||
self.assertEqual(report.status, "completed", report.error)
|
||||
self.assertGreater(report.duration_ms, 0.0)
|
||||
|
||||
def test_numerical_warmup_exercises_sparse_lsmr_algebraic_path(self) -> None:
|
||||
import scipy.optimize
|
||||
|
||||
actual_least_squares = scipy.optimize.least_squares
|
||||
optimizer_calls: list[dict[str, object]] = []
|
||||
|
||||
def recording_least_squares(*args, **kwargs):
|
||||
optimizer_calls.append(dict(kwargs))
|
||||
return actual_least_squares(*args, **kwargs)
|
||||
|
||||
with patch.object(
|
||||
scipy.optimize,
|
||||
"least_squares",
|
||||
recording_least_squares,
|
||||
):
|
||||
_run_numerical_warmup()
|
||||
|
||||
self.assertEqual(len(optimizer_calls), 1)
|
||||
call = optimizer_calls[0]
|
||||
self.assertEqual(call["tr_solver"], "lsmr")
|
||||
sparsity = call["jac_sparsity"]
|
||||
self.assertEqual(sparsity.shape, (2, 2))
|
||||
self.assertEqual(sparsity.nnz, 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user