From a4afbdaa6bbfde937fa1fa839136f6dcce455d7e Mon Sep 17 00:00:00 2001 From: lukken <lukken@astron.nl> Date: Fri, 5 Jul 2024 09:52:12 +0200 Subject: [PATCH] Formatting and allow to run performance tests --- README.md | 11 ++++-- loafty/imaging/__init__.py | 12 +++++-- loafty/imaging/_simple.py | 23 +++++++----- tests/measurements/data.py | 4 +-- tests/test_xst_imaging.py | 72 +++++++++++++++++++++++++++----------- tox.ini | 4 +++ 6 files changed, 91 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 2670485..c4b5fe0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # loafty - - + + +[](https://opensource.org/licenses/Apache-2.0) This repository serves as a Python test bed for evaluating the performance of crosslet statistic (XST) all-sky imaging for LOFAR2.0 with the vision on @@ -37,6 +38,12 @@ pip install . tox -e py310 ``` +To evaluate the performance run, this might take some time: + +```shell +tox -e performance +``` + ## Results CPU | CPU | Algorithm | X | Y | Mean | Min | Max | std dev | diff --git a/loafty/imaging/__init__.py b/loafty/imaging/__init__.py index 4d6b181..447ddf4 100644 --- a/loafty/imaging/__init__.py +++ b/loafty/imaging/__init__.py @@ -8,10 +8,18 @@ from ._simple import sky_imager_simple try: from ._cupy import sky_imager_cupy # noqa - __all__ = ["sky_imager_cupy", "sky_imager_numpy_ravel_32", "sky_imager_numpy_np_exp"] + __all__ = [ + "sky_imager_cupy", + "sky_imager_numpy_ravel_32", + "sky_imager_numpy_np_exp", + ] sky_imager = sky_imager_cupy except ImportError: - __all__ = ["sky_imager_numpy_ravel_32", "sky_imager_numpy_np_exp", "sky_imager_simple"] + __all__ = [ + "sky_imager_numpy_ravel_32", + "sky_imager_numpy_np_exp", + "sky_imager_simple", + ] sky_imager = sky_imager_numpy_ravel_32 diff --git a/loafty/imaging/_simple.py b/loafty/imaging/_simple.py index fcaaf37..e4ae76b 100644 --- a/loafty/imaging/_simple.py +++ b/loafty/imaging/_simple.py @@ -31,7 +31,7 @@ def sky_imager_simple( for l_ix in range(npix_l): for m_ix in range(npix_m): - img[npix_l-l_ix-1, npix_m-m_ix-1] = np.mean( + img[npix_l - l_ix - 1, npix_m - m_ix - 1] = np.mean( visibilities * np.exp( -2j @@ -73,15 +73,22 @@ def sky_imager_simple_numba( l = 1 - l_ix * 2 / npix_l n = np.sqrt(1 - l * l - m * m) - 1 img[m_ix, l_ix] = np.mean( - visibilities * np.exp( - -2j * np.pi * freq * - (baselines[:, :, 0] * l + - baselines[:, :, 1] * m + - baselines[:, :, 2] * n) / - SPEED_OF_LIGHT - ) + visibilities + * np.exp( + -2j + * np.pi + * freq + * ( + baselines[:, :, 0] * l + + baselines[:, :, 1] * m + + baselines[:, :, 2] * n ) + / SPEED_OF_LIGHT + ) + ) return np.real(img) + + # # # def sky_imager_cupy(visibilities, baselines, freq, npix_l, npix_m): diff --git a/tests/measurements/data.py b/tests/measurements/data.py index fd9b8ab..e38f634 100644 --- a/tests/measurements/data.py +++ b/tests/measurements/data.py @@ -8,7 +8,7 @@ class MeasureData: @dataclass class ResultData: - min: float = float('inf') - max: float = float('-inf') + min: float = float("inf") + max: float = float("-inf") mean: float = 0.0 std: float = 0.0 diff --git a/tests/test_xst_imaging.py b/tests/test_xst_imaging.py index 7b8f071..109e005 100644 --- a/tests/test_xst_imaging.py +++ b/tests/test_xst_imaging.py @@ -8,6 +8,7 @@ import logging import math import os import time +from typing import Callable, Optional import numpy as np @@ -29,6 +30,20 @@ logger = logging.getLogger() class TestXstImaging(BaseTestCase): """Test performance and verify correctness of xst all-sky imaging""" + @classmethod + def setUpClass(cls): + cls.measure_performance = False + if os.getenv("MEASURE_PERFORMANCE"): + cls.measure_performance = True + + if cls.measure_performance: + logger.info("Running performance analysis, this might take a while") + else: + logger.info( + "Verifying image results only, use `tox -e performance` to analyse " + "performace" + ) + def setUp(self): self.baselines = np.load( os.path.join(os.path.dirname(__file__), "baselines.npy") @@ -42,8 +57,11 @@ class TestXstImaging(BaseTestCase): # ) # np.save("image", self.image) - def _measure_imager(self, fn, name, x=205, y=205): - measure = Measure(Settings(iterations=30, image_size_x=x, image_size_y=y)) + def _measure_imager(self, fn: Callable, name: str, x: int = 205, y: int = 205): + if not self.measure_performance: + return + + measure = Measure(Settings(iterations=2, image_size_x=x, image_size_y=y)) measure.run( lambda var_x, var_y: fn( self.visibilities[0], self.baselines, self.frequency, var_x, var_y @@ -52,10 +70,16 @@ class TestXstImaging(BaseTestCase): result = measure.compute() logger.info( "[%s]: min: %.2f max: %.2f, mean: %.2f, stddev: %.2f", - name, result.min, result.max, result.mean, result.std + name, + result.min, + result.max, + result.mean, + result.std, ) - def _verify_imager(self, fn, fn2=None, x=205, y=205): + def _verify_imager( + self, fn: Callable, fn2: Optional[Callable] = None, x: int = 205, y: int = 205 + ): """Compare against precomputed stored results or two implementations :warning: This method relies on L, M, and N being derived in the same way across @@ -65,16 +89,23 @@ class TestXstImaging(BaseTestCase): """ if fn2 is None: - reference_image = np.load(os.path.join(os.path.dirname(__file__), f"image_{x}_{y}.npy")) + reference_image = np.load( + os.path.join(os.path.dirname(__file__), f"image_{x}_{y}.npy") + ) else: - reference_image = lambda var_x, var_y: fn2( + + def reference_image(var_x, var_y): + return fn2( + self.visibilities[0], self.baselines, self.frequency, var_x, var_y + ) + + reference_image = reference_image(x, y) + + def result_image(var_x, var_y): + return fn( self.visibilities[0], self.baselines, self.frequency, var_x, var_y ) - reference_image = reference_image(x, y) - result_image = lambda var_x, var_y: fn( - self.visibilities[0], self.baselines, self.frequency, var_x, var_y - ) result_image = result_image(x, y) # Can be used for diagnostic comparison of differences @@ -84,27 +115,26 @@ class TestXstImaging(BaseTestCase): # Create a circle as mask just below unit length. and remove those results from # the evaluation. The (all-sky imaging) computation does not resolve beyond the - # horizon. This boundary can shift slightly due to numerical error. - npix_l, npix_m = np.meshgrid( - np.linspace(-1, 1, x), np.linspace(1, -1, y) - ) + # horizon. This boundary can shift slightly due to numerical error between + # implementations. + npix_l, npix_m = np.meshgrid(np.linspace(-1, 1, x), np.linspace(1, -1, y)) c = npix_l**2 + npix_m**2 < 0.99 reference_image = np.where(c, reference_image, float("nan")) result_image = np.where(c, result_image, float("nan")) - np.testing.assert_allclose( - reference_image, result_image, rtol=1e-04 - ) + np.testing.assert_allclose(reference_image, result_image, rtol=1e-04) def test_sky_imager_simple(self): + """Testing the performance and verifying of simple imager""" self._verify_imager(sky_imager_simple) - # self._measure_imager(sky_imager_simple, "sky imager simple") + self._measure_imager(sky_imager_simple, "sky imager simple") def test_sky_imager_numpy_np_exp(self): + """Testing the performance and verifying of numpy exp imager""" self._verify_imager(sky_imager_numpy_np_exp) - # self._measure_imager(sky_imager_numpy_np_exp, "sky imager numpy exp") + self._measure_imager(sky_imager_numpy_np_exp, "sky imager numpy exp") def test_sky_imager_numpy_ravel_32(self): - """Testing the performance of ravelling numpy imager""" + """Testing the performance and verifying of ravelling numpy imager""" self._verify_imager(sky_imager_numpy_ravel_32) - # self._measure_imager(sky_imager_numpy_ravel_32, "sky imager numpy ravel") + self._measure_imager(sky_imager_numpy_ravel_32, "sky imager numpy ravel") diff --git a/tox.ini b/tox.ini index b839b2a..3b1fe10 100644 --- a/tox.ini +++ b/tox.ini @@ -19,6 +19,10 @@ commands = {envpython} --version {envpython} -m pytest -v -rP --log-level=DEBUG {posargs} +[testenv:performance] +setenv = + MEASURE_PERFORMANCE=1 + [testenv:coverage] commands = {envpython} --version -- GitLab