diff --git a/README.md b/README.md index 2670485582c42e7723045e94888ed508a18b3855..c4b5fe003b931dd6d36eb8bf78ba8fb6ccffdcfe 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 4d6b181003d22c414944450adecf307592869990..447ddf43cac8f5e4d8a33e3702c21f9509f6bea0 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 fcaaf37666dc8830cb0d33a3132e7f70513bdfcc..e4ae76bbb5f774b0dbf1aef277e468a0f79d7296 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 fd9b8abdda49aeba5795a44f8fcf48e884743621..e38f6341b622ca3c47acb168b1eac02dfc3fc92f 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 7b8f071a326922fd6b44fa67b282715ab8986a3c..109e0059c01cbfa3a77702a9e7570ce3c97f7bc9 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 b839b2aa19179944da7fe598c364966e1b77719d..3b1fe109c8ca68b134d92f146905df7c42f08a5f 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