From e26db97b5cc6e29a1e5ca5082bc6e7c0449df71c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Tue, 11 Aug 2026 15:28:54 +0200 Subject: [PATCH 01/10] Prototype multi threaded file reader (#343) - Reading files from nfs shares tops out at ~1GB/s for single threaded reads. - MultiThreadedFileReader uses our File wrapper to read a generic file in parallel - Placed in aare::experimental to show that it's not production ready --- .gitignore | 4 +- CMakeLists.txt | 7 +- docs/src/MultiThreadedFileReader.rst | 37 ++++ docs/src/index.rst | 2 + .../experimental/MultiThreadedFileReader.rst | 30 +++ docs/src/python/experimental/index.rst | 10 + include/aare/File.hpp | 3 +- include/aare/FileInterface.hpp | 13 +- include/aare/JungfrauDataFile.hpp | 3 +- include/aare/MultiThreadedFileReader.hpp | 104 ++++++++++ include/aare/NumpyFile.hpp | 4 +- include/aare/RawFile.hpp | 3 +- include/aare/utils/math_helpers.hpp | 14 ++ python/CMakeLists.txt | 1 + python/aare/__init__.py | 8 +- python/aare/experimental.py | 6 + python/src/bind_MultiThreadedFileReader.hpp | 175 ++++++++++++++++ python/src/module.cpp | 5 + python/tests/test_MultiThreadedFileReader.py | 147 ++++++++++++++ python/tests/test_PixelHistogram.py | 101 ++++++++++ src/Dtype.cpp | 6 + src/Dtype.test.cpp | 5 +- src/File.cpp | 3 +- src/MultiThreadedFileReader.cpp | 151 ++++++++++++++ src/MultiThreadedFileReader.test.cpp | 189 ++++++++++++++++++ src/NumpyHelpers.test.cpp | 5 +- 26 files changed, 1021 insertions(+), 15 deletions(-) create mode 100644 docs/src/MultiThreadedFileReader.rst create mode 100644 docs/src/python/experimental/MultiThreadedFileReader.rst create mode 100644 docs/src/python/experimental/index.rst create mode 100644 include/aare/MultiThreadedFileReader.hpp create mode 100644 include/aare/utils/math_helpers.hpp create mode 100644 python/aare/experimental.py create mode 100644 python/src/bind_MultiThreadedFileReader.hpp create mode 100644 python/tests/test_MultiThreadedFileReader.py create mode 100644 python/tests/test_PixelHistogram.py create mode 100644 src/MultiThreadedFileReader.cpp create mode 100644 src/MultiThreadedFileReader.test.cpp diff --git a/.gitignore b/.gitignore index fe88c6fe..dc40ceac 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,8 @@ cover/ *.mo *.pot +.* + # Django stuff: *.log local_settings.py @@ -192,4 +194,4 @@ cython_debug/ .ruff_cache/ # user defined -wheelhouse/ \ No newline at end of file +wheelhouse/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f923dfce..a3d96cc4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -396,6 +396,7 @@ set(PUBLICHEADERS include/aare/FileInterface.hpp include/aare/FilePtr.hpp include/aare/Frame.hpp + include/aare/MultiThreadedFileReader.hpp include/aare/hist/PixelHistogram.hpp include/aare/hist/PixelHistogramImpl.hpp include/aare/hist/PedestalTrackingPixelHistogram.hpp @@ -414,7 +415,9 @@ set(PUBLICHEADERS include/aare/RawMasterFile.hpp include/aare/RawSubFile.hpp include/aare/VarClusterFinder.hpp - include/aare/utils/task.hpp) + include/aare/utils/task.hpp + include/aare/utils/ifstream_helpers.hpp + include/aare/utils/math_helpers.hpp) set(SourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/src/calibration.cpp @@ -431,6 +434,7 @@ set(SourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Interpolator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/MultiThreadedFileReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/PixelMap.cpp @@ -502,6 +506,7 @@ if(AARE_TESTS) ${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PixelHistogramImpl.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PixelHistogram.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/MultiThreadedFileReader.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp diff --git a/docs/src/MultiThreadedFileReader.rst b/docs/src/MultiThreadedFileReader.rst new file mode 100644 index 00000000..a2bdebf8 --- /dev/null +++ b/docs/src/MultiThreadedFileReader.rst @@ -0,0 +1,37 @@ +MultiThreadedFileReader +======================= + +``MultiThreadedFileReader`` reads one chunk per worker on each call. Each +worker owns an independent :cpp:class:`aare::File`, while all workers write +into non-overlapping regions of the destination buffer. The output is ordered +by frame index and successive calls advance through the file. + +.. code-block:: cpp + + #include "aare/MultiThreadedFileReader.hpp" + + // Four workers, chunks of 128 frames, and at most 10,000 frames. + aare::experimental::MultiThreadedFileReader reader(path, 4, 128, 10'000); + while (reader.remaining_frames() != 0) { + // Contains at most 4 * 128 frames. + auto batch = reader.read(); + process(batch); + } + +Omit the final argument to read every frame in the source. An explicit value +of zero requests an empty result. The low-level ``read_into`` overload avoids +an allocation when the caller already owns a buffer of at least +``reader.next_read_bytes()`` bytes. Use ``read_all()`` to read every frame +remaining from the current position, and ``seek()`` to reposition the reader. +Call ``close()`` to release all worker file handles early. + +.. note:: + + Multiple workers do not guarantee faster reads. Performance depends on the + storage device and file format, so the thread count and chunk size should be + benchmark-driven. + +.. doxygenclass:: aare::experimental::MultiThreadedFileReader + :members: + :undoc-members: + :private-members: diff --git a/docs/src/index.rst b/docs/src/index.rst index 8b9af286..d60fc78e 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -28,6 +28,7 @@ AARE pycalibration python/cluster/index python/file/index + python/experimental/index python/histogram/index pyFit @@ -42,6 +43,7 @@ AARE NDView Frame File + MultiThreadedFileReader Dtype Cluster ClusterFinder diff --git a/docs/src/python/experimental/MultiThreadedFileReader.rst b/docs/src/python/experimental/MultiThreadedFileReader.rst new file mode 100644 index 00000000..d8553e4f --- /dev/null +++ b/docs/src/python/experimental/MultiThreadedFileReader.rst @@ -0,0 +1,30 @@ +MultiThreadedFileReader +======================= + +.. py:currentmodule:: aare.experimental + +The reader returns a NumPy array with shape ``(frames, rows, cols)`` and +preserves the source pixel dtype. Each iteration reads at most +``n_threads * chunk_size`` frames—one chunk per worker. File I/O runs with the +Python GIL released. + +.. code-block:: python + + from aare.experimental import MultiThreadedFileReader + + with MultiThreadedFileReader( + "frames.npy", n_threads=4, chunk_size=128, total_frames=10_000 + ) as reader: + for frames in reader: + process(frames) + +Call ``read()`` directly for the next batch, or ``read_all()`` for all frames +remaining from the current position. ``tell()`` and ``seek()`` expose the +iteration position. The context manager closes all worker files on exit. +``close()`` is also available for explicit cleanup and may be called +repeatedly. + +.. autoclass:: MultiThreadedFileReader + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/src/python/experimental/index.rst b/docs/src/python/experimental/index.rst new file mode 100644 index 00000000..fc683bf5 --- /dev/null +++ b/docs/src/python/experimental/index.rst @@ -0,0 +1,10 @@ +Experimental +============ + +APIs in this module may change without notice. + +.. toctree:: + :caption: Experimental + :maxdepth: 1 + + MultiThreadedFileReader diff --git a/include/aare/File.hpp b/include/aare/File.hpp index 6e65fa26..a931f511 100644 --- a/include/aare/File.hpp +++ b/include/aare/File.hpp @@ -64,8 +64,9 @@ class File { size_t total_frames() const; size_t rows() const; size_t cols() const; + Dtype dtype() const; DetectorType detector_type() const; }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/FileInterface.hpp b/include/aare/FileInterface.hpp index 65ac0c2e..87ca3dbf 100644 --- a/include/aare/FileInterface.hpp +++ b/include/aare/FileInterface.hpp @@ -145,10 +145,15 @@ class FileInterface { */ virtual size_t bitdepth() const = 0; - virtual DetectorType detector_type() const = 0; + /** + * @brief get the data type of the pixels + * @return pixel data type + */ + virtual Dtype dtype() const { + return Dtype::from_bitdepth(static_cast(bitdepth())); + } - // function to query the data type of the file - /*virtual DataType dtype = 0; */ + virtual DetectorType detector_type() const = 0; virtual ~FileInterface() = default; @@ -168,4 +173,4 @@ class FileInterface { // size_t current_frame{}; }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/JungfrauDataFile.hpp b/include/aare/JungfrauDataFile.hpp index 0911c284..fe74d5e3 100644 --- a/include/aare/JungfrauDataFile.hpp +++ b/include/aare/JungfrauDataFile.hpp @@ -49,6 +49,7 @@ class JungfrauDataFile : public FileInterface { size_t pixels_per_frame() override; size_t bytes_per_pixel() const; size_t bitdepth() const override; + Dtype dtype() const override { return Dtype::UINT16; } void seek(size_t frame_index) override; //!< seek to the given frame index (note not byte offset) size_t tell() override; //!< get the frame index of the file pointer @@ -113,4 +114,4 @@ class JungfrauDataFile : public FileInterface { std::filesystem::path fpath(size_t frame_index) const; }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/MultiThreadedFileReader.hpp b/include/aare/MultiThreadedFileReader.hpp new file mode 100644 index 00000000..d7a930b8 --- /dev/null +++ b/include/aare/MultiThreadedFileReader.hpp @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MPL-2.0 +#pragma once + +#include "aare/Dtype.hpp" +#include "aare/File.hpp" + +#include +#include +#include +#include + +namespace aare::experimental { + +/** + * @brief Read independent chunks of a file in parallel. + * + * Each worker opens its own File instance, so seeking and reading do not share + * mutable file state. Chunks are written directly to their position in the + * destination buffer and the resulting frame order is the same as in the file. + */ +class MultiThreadedFileReader { + public: + /** + * @param fname path accepted by File + * @param n_threads maximum number of worker threads + * @param chunk_size number of frames claimed by a worker at a time + * @param total_frames number of frames to read, or all frames when omitted + */ + MultiThreadedFileReader(std::filesystem::path fname, size_t n_threads, + size_t chunk_size, + std::optional total_frames = std::nullopt); + + MultiThreadedFileReader(const MultiThreadedFileReader &) = delete; + MultiThreadedFileReader & + operator=(const MultiThreadedFileReader &) = delete; + MultiThreadedFileReader(MultiThreadedFileReader &&) noexcept = default; + MultiThreadedFileReader & + operator=(MultiThreadedFileReader &&) noexcept = default; + + /** + * @brief Read one chunk per active worker into a caller-owned buffer. + * + * The buffer must hold at least next_read_bytes() bytes. The reader's + * position advances by the returned number of frames. At the end of the + * configured range this function returns zero and does not access the + * destination. + */ + size_t read_into(std::byte *destination); + + /** @brief Read the next wave of chunks into an owned byte buffer. */ + std::vector read(); + + /** @brief Read every frame remaining from the current position. */ + std::vector read_all(); + + /** @brief Set the next frame index to read. The end position is valid. */ + void seek(size_t frame_index); + + /** @brief Return the next frame index to read. */ + size_t tell() const noexcept { return m_current_frame; } + + /** @brief Close all worker files. Safe to call more than once. */ + void close() noexcept { m_files.clear(); } + + /** @brief Return whether the worker files are open. */ + bool is_open() const noexcept { return !m_files.empty(); } + + size_t n_threads() const noexcept { return m_n_threads; } + size_t chunk_size() const noexcept { return m_chunk_size; } + size_t total_frames() const noexcept { return m_total_frames; } + size_t source_total_frames() const noexcept { + return m_source_total_frames; + } + size_t rows() const noexcept { return m_rows; } + size_t cols() const noexcept { return m_cols; } + size_t bitdepth() const noexcept { return m_bitdepth; } + Dtype dtype() const noexcept { return m_dtype; } + size_t bytes_per_frame() const noexcept { return m_bytes_per_frame; } + size_t total_bytes() const noexcept { return m_total_bytes; } + size_t remaining_frames() const noexcept; + size_t next_read_frames() const noexcept; + size_t next_read_bytes() const noexcept { + return next_read_frames() * m_bytes_per_frame; + } + + private: + std::filesystem::path m_fname; + size_t m_n_threads; + size_t m_chunk_size; + size_t m_total_frames; + size_t m_source_total_frames; + size_t m_rows; + size_t m_cols; + size_t m_bitdepth; + Dtype m_dtype; + size_t m_bytes_per_frame; + size_t m_total_bytes; + size_t m_current_frame; + std::vector m_files; + + void ensure_open() const; +}; + +} // namespace aare::experimental diff --git a/include/aare/NumpyFile.hpp b/include/aare/NumpyFile.hpp index 76d5321c..caa68531 100644 --- a/include/aare/NumpyFile.hpp +++ b/include/aare/NumpyFile.hpp @@ -61,7 +61,7 @@ class NumpyFile : public FileInterface { * @brief get the data type of the numpy file * @return DType */ - Dtype dtype() const { return m_header.dtype; } + Dtype dtype() const override { return m_header.dtype; } /** * @brief get the shape of the numpy file @@ -129,4 +129,4 @@ class NumpyFile : public FileInterface { void write_impl(void *data, uint64_t size); }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/RawFile.hpp b/include/aare/RawFile.hpp index 65758017..999b737f 100644 --- a/include/aare/RawFile.hpp +++ b/include/aare/RawFile.hpp @@ -118,6 +118,7 @@ class RawFile : public FileInterface { */ size_t cols(const size_t roi_index) const; size_t bitdepth() const override; + Dtype dtype() const override { return Dtype::from_bitdepth(bitdepth()); } size_t n_modules() const; /** @@ -170,4 +171,4 @@ class RawFile : public FileInterface { void open_subfiles(const size_t roi_index); }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/utils/math_helpers.hpp b/include/aare/utils/math_helpers.hpp new file mode 100644 index 00000000..9481bd8a --- /dev/null +++ b/include/aare/utils/math_helpers.hpp @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace aare { + +/** + * @brief Compute the ceiling of the integer division of n by d. + * @param n The numerator. + * @param d The denominator. + * @return The ceiling of the integer division. + */ +constexpr size_t ceil_div(size_t n, size_t d) { return n / d + (n % d != 0); } + +} // namespace aare \ No newline at end of file diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 03775481..5f877773 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -40,6 +40,7 @@ set(PYTHON_FILES aare/ClusterVector.py aare/Cluster.py aare/calibration.py + aare/experimental.py aare/func.py aare/RawFile.py aare/transform.py diff --git a/python/aare/__init__.py b/python/aare/__init__.py index 10872f89..c8db2e4e 100644 --- a/python/aare/__init__.py +++ b/python/aare/__init__.py @@ -3,8 +3,14 @@ from . import _aare from . import transform +from . import experimental -from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile +from ._aare import ( + File, + JungfrauDataFile, + RawMasterFile, + RawSubFile, +) from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder from ._aare import DetectorType, ReadoutMode from ._aare import hitmap diff --git a/python/aare/experimental.py b/python/aare/experimental.py new file mode 100644 index 00000000..2a5210d9 --- /dev/null +++ b/python/aare/experimental.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MPL-2.0 +"""Experimental APIs that may change without notice.""" + +from ._aare.experimental import MultiThreadedFileReader + +__all__ = ["MultiThreadedFileReader"] diff --git a/python/src/bind_MultiThreadedFileReader.hpp b/python/src/bind_MultiThreadedFileReader.hpp new file mode 100644 index 00000000..f63f79c8 --- /dev/null +++ b/python/src/bind_MultiThreadedFileReader.hpp @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: MPL-2.0 +#pragma once + +#include "aare/MultiThreadedFileReader.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace py = pybind11; + +inline py::dtype multi_threaded_reader_numpy_dtype(const aare::Dtype &dtype) { + using aare::Dtype; + if (dtype == Dtype::INT8) + return py::dtype::of(); + if (dtype == Dtype::UINT8) + return py::dtype::of(); + if (dtype == Dtype::INT16) + return py::dtype::of(); + if (dtype == Dtype::UINT16) + return py::dtype::of(); + if (dtype == Dtype::INT32) + return py::dtype::of(); + if (dtype == Dtype::UINT32) + return py::dtype::of(); + if (dtype == Dtype::INT64) + return py::dtype::of(); + if (dtype == Dtype::UINT64) + return py::dtype::of(); + if (dtype == Dtype::FLOAT) + return py::dtype::of(); + if (dtype == Dtype::DOUBLE) + return py::dtype::of(); + throw std::runtime_error("Unsupported pixel data type"); +} + +inline py::array +multi_threaded_reader_read(aare::experimental::MultiThreadedFileReader &reader, + bool read_all) { + const size_t n_frames = + read_all ? reader.remaining_frames() : reader.next_read_frames(); + const std::vector shape{ + static_cast(n_frames), + static_cast(reader.rows()), + static_cast(reader.cols())}; + + py::array image(multi_threaded_reader_numpy_dtype(reader.dtype()), shape); + auto *destination = reinterpret_cast(image.mutable_data()); + { + py::gil_scoped_release release; + if (read_all) { + size_t offset = 0; + while (reader.remaining_frames() != 0) { + const size_t frames_read = + reader.read_into(destination + offset); + offset += frames_read * reader.bytes_per_frame(); + } + } else { + reader.read_into(destination); + } + } + return image; +} + +inline void define_multi_threaded_file_reader_bindings(py::module_ &m) { + using aare::experimental::MultiThreadedFileReader; + + auto reader = + py::class_(m, "MultiThreadedFileReader"); + reader.attr("__module__") = "aare.experimental"; + reader + .def(py::init>(), + py::arg("fname"), py::arg("n_threads"), py::arg("chunk_size"), + py::arg("total_frames") = py::none(), + R"doc( + Read chunks of detector frames concurrently. + + Each worker opens an independent File. The returned array is + ordered by frame index even though chunks are read in parallel. + + Args: + fname: Path accepted by File. + n_threads: Maximum number of worker threads. + chunk_size: Number of frames read per claimed chunk. + total_frames: Optional frame limit. None reads all frames. + )doc") + .def( + "read", + [](MultiThreadedFileReader &self) { + return multi_threaded_reader_read(self, false); + }, + R"doc( + Read one chunk per active worker into a NumPy array. + + Returns: + An array containing at most n_threads * chunk_size frames. + An empty array is returned at the end of the configured + frame range. The GIL is released while file data is read. + )doc") + .def( + "read_all", + [](MultiThreadedFileReader &self) { + return multi_threaded_reader_read(self, true); + }, + R"doc( + Read all frames remaining from the current position. + )doc") + .def_property_readonly("n_threads", &MultiThreadedFileReader::n_threads) + .def_property_readonly("chunk_size", + &MultiThreadedFileReader::chunk_size) + .def_property_readonly("total_frames", + &MultiThreadedFileReader::total_frames) + .def_property_readonly("source_total_frames", + &MultiThreadedFileReader::source_total_frames) + .def_property_readonly("rows", &MultiThreadedFileReader::rows) + .def_property_readonly("cols", &MultiThreadedFileReader::cols) + .def_property_readonly("bitdepth", &MultiThreadedFileReader::bitdepth) + .def_property_readonly("dtype", + [](const MultiThreadedFileReader &self) { + return multi_threaded_reader_numpy_dtype( + self.dtype()); + }) + .def_property_readonly("bytes_per_frame", + &MultiThreadedFileReader::bytes_per_frame) + .def_property_readonly("total_bytes", + &MultiThreadedFileReader::total_bytes) + .def_property_readonly("remaining_frames", + &MultiThreadedFileReader::remaining_frames) + .def_property_readonly("next_read_frames", + &MultiThreadedFileReader::next_read_frames) + .def_property_readonly("next_read_bytes", + &MultiThreadedFileReader::next_read_bytes) + .def("seek", &MultiThreadedFileReader::seek, py::arg("frame_index")) + .def("tell", &MultiThreadedFileReader::tell) + .def("close", &MultiThreadedFileReader::close, + "Close all worker files. Safe to call more than once.") + .def_property_readonly( + "closed", + [](const MultiThreadedFileReader &self) { return !self.is_open(); }) + .def("__len__", &MultiThreadedFileReader::total_frames) + .def( + "__enter__", + [](MultiThreadedFileReader &self) -> MultiThreadedFileReader * { + if (!self.is_open()) { + throw std::runtime_error( + "Cannot enter a closed MultiThreadedFileReader"); + } + return &self; + }, + py::return_value_policy::reference_internal) + .def("__exit__", + [](MultiThreadedFileReader &self, const py::object &, + const py::object &, const py::object &) { + self.close(); + return false; + }) + .def( + "__iter__", [](MultiThreadedFileReader &self) { return &self; }, + py::return_value_policy::reference_internal) + .def("__next__", [](MultiThreadedFileReader &self) { + if (self.remaining_frames() == 0) { + throw py::stop_iteration(); + } + return multi_threaded_reader_read(self, false); + }); +} diff --git a/python/src/module.cpp b/python/src/module.cpp index 023a19ee..52c830b9 100644 --- a/python/src/module.cpp +++ b/python/src/module.cpp @@ -12,6 +12,7 @@ #include "bind_Defs.hpp" #include "bind_Eta.hpp" #include "bind_Interpolator.hpp" +#include "bind_MultiThreadedFileReader.hpp" #include "bind_PedestalTrackingPixelHistogram.hpp" #include "bind_PixelHistogram.hpp" #include "bind_PixelMap.hpp" @@ -59,7 +60,11 @@ double, 'f' for float) define_ClusterCollector(m, "Cluster" #N "x" #M #TYPE_CODE); PYBIND11_MODULE(_aare, m) { + auto experimental = m.def_submodule( + "experimental", "Experimental APIs that may change without notice"); + define_file_io_bindings(m); + define_multi_threaded_file_reader_bindings(experimental); define_raw_file_io_bindings(m); define_raw_sub_file_io_bindings(m); define_ctb_raw_file_io_bindings(m); diff --git a/python/tests/test_MultiThreadedFileReader.py b/python/tests/test_MultiThreadedFileReader.py new file mode 100644 index 00000000..0dc9c654 --- /dev/null +++ b/python/tests/test_MultiThreadedFileReader.py @@ -0,0 +1,147 @@ +# SPDX-License-Identifier: MPL-2.0 +import numpy as np +import pytest + +import aare +from aare.experimental import MultiThreadedFileReader + + +@pytest.fixture +def frame_file(tmp_path): + data = np.arange(10 * 2 * 3, dtype=np.uint16).reshape(10, 2, 3) + path = tmp_path / "frames.npy" + np.save(path, data) + return path, data + + +def test_experimental_import_path(): + assert aare.experimental.MultiThreadedFileReader is MultiThreadedFileReader + assert MultiThreadedFileReader.__module__ == "aare.experimental" + assert not hasattr(aare, "MultiThreadedFileReader") + + +def test_reads_all_frames_in_order(frame_file): + path, expected = frame_file + reader = MultiThreadedFileReader(path, n_threads=2, chunk_size=3) + + first = reader.read() + second = reader.read() + exhausted = reader.read() + + assert np.array_equal(first, expected[:6]) + assert np.array_equal(second, expected[6:]) + assert exhausted.shape == (0, 2, 3) + assert first.dtype == np.uint16 + assert reader.n_threads == 2 + assert reader.chunk_size == 3 + assert reader.total_frames == 10 + assert reader.source_total_frames == 10 + assert reader.rows == 2 + assert reader.cols == 3 + assert reader.bitdepth == 16 + assert reader.dtype == np.dtype(np.uint16) + assert reader.bytes_per_frame == 12 + assert reader.total_bytes == expected.nbytes + assert len(reader) == 10 + assert reader.tell() == 10 + assert reader.remaining_frames == 0 + assert reader.next_read_frames == 0 + assert reader.next_read_bytes == 0 + + +def test_total_frame_limit(frame_file): + path, expected = frame_file + reader = MultiThreadedFileReader( + path, n_threads=2, chunk_size=2, total_frames=7 + ) + + assert np.array_equal(reader.read(), expected[:4]) + assert np.array_equal(reader.read_all(), expected[4:7]) + + +def test_iteration_yields_one_chunk_per_thread(frame_file): + path, expected = frame_file + reader = MultiThreadedFileReader(path, n_threads=2, chunk_size=2) + + batches = list(reader) + + assert [len(batch) for batch in batches] == [4, 4, 2] + assert np.array_equal(np.concatenate(batches), expected) + + +def test_seek_resets_iteration_position(frame_file): + path, expected = frame_file + reader = MultiThreadedFileReader(path, n_threads=2, chunk_size=2) + + reader.read() + assert reader.tell() == 4 + + reader.seek(1) + assert reader.tell() == 1 + assert np.array_equal(reader.read(), expected[1:5]) + + with pytest.raises(IndexError): + reader.seek(11) + + +def test_context_manager_closes_worker_files(frame_file): + path, expected = frame_file + + with MultiThreadedFileReader(path, n_threads=2, chunk_size=2) as reader: + assert not reader.closed + assert np.array_equal(reader.read(), expected[:4]) + + assert reader.closed + reader.close() + with pytest.raises(RuntimeError): + reader.read() + with pytest.raises(RuntimeError): + reader.seek(0) + with pytest.raises(RuntimeError): + with reader: + pass + + +def test_explicit_zero_frame_limit(frame_file): + path, expected = frame_file + reader = MultiThreadedFileReader( + path, n_threads=8, chunk_size=3, total_frames=0 + ) + + actual = reader.read() + assert actual.shape == (0, *expected.shape[1:]) + assert actual.dtype == expected.dtype + + +@pytest.mark.parametrize( + "dtype", [np.int8, np.int32, np.uint64, np.float32, np.float64] +) +def test_preserves_numpy_dtype(tmp_path, dtype): + expected = np.arange(4 * 2 * 3, dtype=dtype).reshape(4, 2, 3) + path = tmp_path / "typed-frames.npy" + np.save(path, expected) + + reader = MultiThreadedFileReader(path, n_threads=2, chunk_size=3) + actual = reader.read() + + assert actual.dtype == expected.dtype + assert reader.dtype == expected.dtype + assert np.array_equal(actual, expected) + + +@pytest.mark.parametrize( + ("n_threads", "chunk_size", "total_frames"), + [(0, 1, None), (1, 0, None), (1, 1, 11)], +) +def test_invalid_configuration( + frame_file, n_threads, chunk_size, total_frames +): + path, _ = frame_file + + with pytest.raises(ValueError): + MultiThreadedFileReader( + path, + n_threads=n_threads, + chunk_size=chunk_size, + total_frames=total_frames, + ) diff --git a/python/tests/test_PixelHistogram.py b/python/tests/test_PixelHistogram.py new file mode 100644 index 00000000..d8563328 --- /dev/null +++ b/python/tests/test_PixelHistogram.py @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: MPL-2.0 +import numpy as np +import pytest + +from aare import PixelHistogram + + +def _random_frames(rows, cols, n, xmin, xmax, seed=0): + rng = np.random.default_rng(seed) + return [rng.uniform(xmin - 0.25, xmax + 0.25, size=(rows, cols)).astype(np.float64) + for _ in range(n)] + + +def _reference_hdata(frames, rows, cols, n_bins, xmin, xmax): + expected = np.zeros((rows, cols, n_bins), dtype=np.uint16) + inv_range = n_bins / (xmax - xmin) + for img in frames: + for r in range(rows): + for c in range(cols): + v = float(img[r, c]) + if not (xmin <= v < xmax): + continue + b = int((v - xmin) * inv_range) + if b >= n_bins: + b = n_bins - 1 + expected[r, c, b] += 1 + return expected + + +def test_async_fill_matches_reference(): + rows, cols, n_bins = 5, 7, 8 + xmin, xmax = 0.0, 2.0 + frames = _random_frames(rows, cols, n=3, xmin=xmin, xmax=xmax, seed=1) + + hist = PixelHistogram(rows=rows, cols=cols, n_bins=n_bins, xmin=xmin, xmax=xmax) + for img in frames: + hist.fill_async(img) + + np.testing.assert_array_equal( + hist.values(), + _reference_hdata(frames, rows, cols, n_bins, xmin, xmax), + ) + + + + +def test_fill_async_copies_buffer(): + # After fill_async returns, the caller should be free to mutate the + # numpy array without affecting the pending fill. + rows, cols, n_bins = 4, 4, 4 + xmin, xmax = 0.0, 1.0 + hist = PixelHistogram(rows=rows, cols=cols, n_bins=n_bins, xmin=xmin, xmax=xmax, n_threads=1, max_pending=8) + + img = np.full((rows, cols), 0.1, dtype=np.float64) # falls in bin 0 + hist.fill_async(img) + # Mutate the original array immediately; this must not affect the + # value that was already enqueued. + img[:] = 0.9 # would be bin 3 + hist.flush() + + h = hist.values() + assert h.shape == (rows, cols, n_bins) + # Every pixel saw one value in bin 0, none elsewhere. + assert (h[:, :, 0] == 1).all() + assert (h[:, :, 1:] == 0).all() + + + def test_fill_async_rejects_wrong_shape(): + hist = PixelHistogram(8, 8, 4, 0.0, 1.0) + bad = np.zeros((4, 4), dtype=np.float32) + with pytest.raises(ValueError): + hist.fill_async(bad) + + +def test_hdata_flushes_pending(): + # Submit several frames with a tiny queue and read hdata() without an + # explicit flush(); hdata() must drain everything first. + rows, cols, n_bins = 3, 3, 4 + xmin, xmax = 0.0, 1.0 + hist = PixelHistogram(rows=rows, cols=cols, n_bins=n_bins, xmin=xmin, xmax=xmax, + n_threads=1, max_pending=1) + frames = _random_frames(rows, cols, n=8, xmin=xmin, xmax=xmax, seed=3) + for img in frames: + hist.fill_async(img) + + h = hist.values() # no explicit flush() + np.testing.assert_array_equal( + h, _reference_hdata(frames, rows, cols, n_bins, xmin, xmax) + ) + + +def test_bin_centers_and_edges(): + n_bins = 5 + xmin, xmax = 0.0, 1.0 + hist = PixelHistogram(rows=2, cols=2, n_bins=n_bins, xmin=xmin, xmax=xmax) + edges = hist.bin_edges() + centers = hist.bin_centers() + assert edges.shape == (n_bins + 1,) + assert centers.shape == (n_bins,) + np.testing.assert_allclose(edges, np.linspace(xmin, xmax, n_bins + 1), atol=1e-6) + np.testing.assert_allclose(centers, 0.5 * (edges[:-1] + edges[1:]), atol=1e-6) diff --git a/src/Dtype.cpp b/src/Dtype.cpp index ad708ad9..08098874 100644 --- a/src/Dtype.cpp +++ b/src/Dtype.cpp @@ -92,6 +92,12 @@ Dtype::Dtype(Dtype::TypeIndex ti) : m_type(ti) {} */ Dtype::Dtype(std::string_view sv) { + // NumPy uses '|' for data types whose byte order is not applicable, + // notably one-byte integer types. + if (!sv.empty() && sv.front() == '|') { + sv.remove_prefix(1); + } + // Check if the file is using our native endianess if (auto pos = sv.find_first_of("<>"); pos != std::string_view::npos) { const auto endianess = [](const char c) { diff --git a/src/Dtype.test.cpp b/src/Dtype.test.cpp index 760a6c83..f87825e8 100644 --- a/src/Dtype.test.cpp +++ b/src/Dtype.test.cpp @@ -12,6 +12,9 @@ TEST_CASE("Construct from typeid") { } TEST_CASE("Construct from string") { + REQUIRE(Dtype("|i1") == typeid(int8_t)); + REQUIRE(Dtype("|u1") == typeid(uint8_t)); + if (endian::native == endian::little) { REQUIRE(Dtype("tell(); } size_t File::rows() const { return file_impl->rows(); } size_t File::cols() const { return file_impl->cols(); } size_t File::bitdepth() const { return file_impl->bitdepth(); } +Dtype File::dtype() const { return file_impl->dtype(); } size_t File::bytes_per_pixel() const { return file_impl->bitdepth() / bits_per_byte; } DetectorType File::detector_type() const { return file_impl->detector_type(); } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/src/MultiThreadedFileReader.cpp b/src/MultiThreadedFileReader.cpp new file mode 100644 index 00000000..6810e9bf --- /dev/null +++ b/src/MultiThreadedFileReader.cpp @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "aare/MultiThreadedFileReader.hpp" +#include "aare/File.hpp" +#include "aare/utils/math_helpers.hpp" + +#include +#include +#include +#include +#include + +namespace aare::experimental { +namespace { + +size_t checked_product(size_t lhs, size_t rhs) { + if (lhs != 0 && rhs > std::numeric_limits::max() / lhs) { + throw std::overflow_error( + "MultiThreadedFileReader buffer size overflow"); + } + return lhs * rhs; +} + +} // namespace + +MultiThreadedFileReader::MultiThreadedFileReader( + std::filesystem::path fname, size_t n_threads, size_t chunk_size, + std::optional total_frames) + : m_fname(std::move(fname)), m_n_threads(n_threads), + m_chunk_size(chunk_size), m_total_frames(0), m_source_total_frames(0), + m_rows(0), m_cols(0), m_bitdepth(0), m_dtype(Dtype::NONE), + m_bytes_per_frame(0), m_total_bytes(0), m_current_frame(0) { + if (m_n_threads == 0) { + throw std::invalid_argument( + "MultiThreadedFileReader requires at least one thread"); + } + if (m_chunk_size == 0) { + throw std::invalid_argument( + "MultiThreadedFileReader chunk size must be greater than zero"); + } + + File file(m_fname); + m_source_total_frames = file.total_frames(); + m_total_frames = total_frames.value_or(m_source_total_frames); + if (m_total_frames > m_source_total_frames) { + throw std::invalid_argument( + "Requested frame count exceeds the number of frames in the file"); + } + + m_rows = file.rows(); + m_cols = file.cols(); + m_bitdepth = file.bitdepth(); + m_dtype = file.dtype(); + m_bytes_per_frame = file.bytes_per_frame(); + m_total_bytes = checked_product(m_total_frames, m_bytes_per_frame); + + m_files.reserve(m_n_threads); + m_files.push_back(std::move(file)); + for (size_t i = 1; i < m_n_threads; ++i) { + m_files.emplace_back(m_fname); + } +} + +size_t MultiThreadedFileReader::remaining_frames() const noexcept { + return m_total_frames - m_current_frame; +} + +size_t MultiThreadedFileReader::next_read_frames() const noexcept { + const size_t remaining = remaining_frames(); + if (remaining == 0) { + return 0; + } + + const size_t chunks_remaining = ceil_div(remaining, m_chunk_size); + if (m_n_threads >= chunks_remaining) { + return remaining; + } + // This multiplication is safe: in this branch n_threads * chunk_size is + // strictly smaller than remaining. + return m_n_threads * m_chunk_size; +} + +void MultiThreadedFileReader::ensure_open() const { + if (!is_open()) { + throw std::runtime_error("MultiThreadedFileReader is closed"); + } +} + +size_t MultiThreadedFileReader::read_into(std::byte *destination) { + ensure_open(); + const size_t frames_to_read = next_read_frames(); + if (frames_to_read == 0) { + return 0; + } + if (destination == nullptr) { + throw std::invalid_argument( + "MultiThreadedFileReader destination must not be null"); + } + + const size_t first_frame = m_current_frame; + const size_t active_threads = ceil_div(frames_to_read, m_chunk_size); + auto worker = [&](size_t worker_index) { + File &file = m_files[worker_index]; + const size_t batch_offset = worker_index * m_chunk_size; + const size_t begin = first_frame + batch_offset; + const size_t count = + std::min(m_chunk_size, frames_to_read - batch_offset); + file.seek(begin); + file.read_into(destination + batch_offset * m_bytes_per_frame, count); + }; + + std::vector> workers; + workers.reserve(active_threads); + for (size_t i = 0; i < active_threads; ++i) { + workers.emplace_back(std::async(std::launch::async, worker, i)); + } + + for (auto &future : workers) { + future.get(); + } + m_current_frame += frames_to_read; + return frames_to_read; +} + +std::vector MultiThreadedFileReader::read() { + std::vector data(next_read_bytes()); + read_into(data.data()); + return data; +} + +std::vector MultiThreadedFileReader::read_all() { + ensure_open(); + std::vector data( + checked_product(remaining_frames(), m_bytes_per_frame)); + size_t offset = 0; + while (remaining_frames() != 0) { + const size_t frames_read = read_into(data.data() + offset); + offset += frames_read * m_bytes_per_frame; + } + return data; +} + +void MultiThreadedFileReader::seek(size_t frame_index) { + ensure_open(); + if (frame_index > m_total_frames) { + throw std::out_of_range( + "MultiThreadedFileReader frame index is out of range"); + } + m_current_frame = frame_index; +} + +} // namespace aare::experimental diff --git a/src/MultiThreadedFileReader.test.cpp b/src/MultiThreadedFileReader.test.cpp new file mode 100644 index 00000000..4896d788 --- /dev/null +++ b/src/MultiThreadedFileReader.test.cpp @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "aare/MultiThreadedFileReader.hpp" + +#include "aare/Dtype.hpp" +#include "aare/File.hpp" +#include "aare/FileInterface.hpp" +#include "aare/Frame.hpp" +#include "aare/NumpyFile.hpp" + +#include "test_config.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include + +using aare::File; +using aare::FileConfig; +using aare::Frame; +using aare::NumpyFile; +using aare::experimental::MultiThreadedFileReader; + +namespace { + +class TemporaryNumpyFile { + public: + TemporaryNumpyFile() { + const auto unique = + std::chrono::steady_clock::now().time_since_epoch().count(); + m_path = std::filesystem::temp_directory_path() / + ("aare-mt-reader-" + std::to_string(unique) + ".npy"); + + FileConfig cfg; + cfg.dtype = aare::Dtype::UINT16; + cfg.rows = 2; + cfg.cols = 3; + NumpyFile file(m_path, "w", cfg); + for (uint16_t frame_index = 0; frame_index < 10; ++frame_index) { + Frame frame(cfg.rows, cfg.cols, cfg.dtype); + auto image = frame.view(); + for (ssize_t row = 0; row < image.shape(0); ++row) { + for (ssize_t col = 0; col < image.shape(1); ++col) { + image(row, col) = static_cast(frame_index * 100 + + row * 10 + col); + } + } + file.write(frame); + } + } + + TemporaryNumpyFile(const TemporaryNumpyFile &) = delete; + TemporaryNumpyFile &operator=(const TemporaryNumpyFile &) = delete; + + ~TemporaryNumpyFile() { std::filesystem::remove(m_path); } + + const std::filesystem::path &path() const { return m_path; } + void truncate() { std::filesystem::resize_file(m_path, 0); } + + private: + std::filesystem::path m_path; +}; + +std::vector read_reference(const std::filesystem::path &fpath, + size_t n_frames) { + File file(fpath); + std::vector data(n_frames * file.bytes_per_frame()); + if (n_frames != 0) { + file.read_into(data.data(), n_frames); + } + return data; +} + +} // namespace + +TEST_CASE("Multi-threaded reader preserves numpy frame order", + "[MultiThreadedFileReader]") { + TemporaryNumpyFile file; + const auto &fpath = file.path(); + + MultiThreadedFileReader reader(fpath, 2, 3); + + CHECK(reader.n_threads() == 2); + CHECK(reader.chunk_size() == 3); + CHECK(reader.total_frames() == 10); + CHECK(reader.source_total_frames() == 10); + CHECK(reader.rows() == 2); + CHECK(reader.cols() == 3); + CHECK(reader.bitdepth() == 16); + CHECK(reader.total_bytes() == + reader.total_frames() * reader.bytes_per_frame()); + + const auto reference = read_reference(fpath, reader.total_frames()); + auto first = reader.read(); + auto second = reader.read(); + + CHECK(first == std::vector(reference.begin(), + reference.begin() + 6 * 12)); + CHECK(second == + std::vector(reference.begin() + 6 * 12, reference.end())); + CHECK(reader.read().empty()); + CHECK(reader.tell() == 10); + CHECK(reader.remaining_frames() == 0); +} + +TEST_CASE("Multi-threaded reader handles uneven raw chunks and frame limits", + "[.with-data][MultiThreadedFileReader]") { + const auto fpath = + test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json"; + REQUIRE(std::filesystem::exists(fpath)); + + MultiThreadedFileReader reader(fpath, 2, 3, 9); + + CHECK(reader.total_frames() == 9); + CHECK(reader.source_total_frames() == 10); + CHECK(reader.read_all() == read_reference(fpath, 9)); +} + +TEST_CASE("Multi-threaded reader can seek and reread", + "[MultiThreadedFileReader]") { + TemporaryNumpyFile file; + + MultiThreadedFileReader reader(file.path(), 2, 1); + const auto first = reader.read(); + CHECK(reader.tell() == 2); + CHECK(reader.next_read_frames() == 2); + + reader.seek(0); + CHECK(reader.tell() == 0); + CHECK(reader.read() == first); + CHECK_THROWS_AS(reader.seek(11), std::out_of_range); +} + +TEST_CASE("Multi-threaded reader validates its configuration", + "[MultiThreadedFileReader]") { + TemporaryNumpyFile file; + const auto &fpath = file.path(); + + CHECK_THROWS_AS(MultiThreadedFileReader(fpath, 0, 1), + std::invalid_argument); + CHECK_THROWS_AS(MultiThreadedFileReader(fpath, 1, 0), + std::invalid_argument); + CHECK_THROWS_AS(MultiThreadedFileReader(fpath, 1, 1, 11), + std::invalid_argument); + + MultiThreadedFileReader reader(fpath, 2, 2); + CHECK_THROWS_AS(reader.read_into(nullptr), std::invalid_argument); +} + +TEST_CASE("An explicit zero frame limit produces an empty read", + "[MultiThreadedFileReader]") { + TemporaryNumpyFile file; + + MultiThreadedFileReader reader(file.path(), 8, 3, 0); + CHECK(reader.total_frames() == 0); + CHECK(reader.total_bytes() == 0); + CHECK(reader.read().empty()); + CHECK_NOTHROW(reader.read_into(nullptr)); +} + +TEST_CASE("read_into reads at most one chunk per worker", + "[MultiThreadedFileReader]") { + TemporaryNumpyFile file; + MultiThreadedFileReader reader(file.path(), 3, 2); + std::vector data(reader.next_read_bytes()); + + CHECK(reader.next_read_frames() == 6); + CHECK(reader.read_into(data.data()) == 6); + CHECK(reader.tell() == 6); + CHECK(reader.next_read_frames() == 4); + CHECK(reader.next_read_bytes() == 4 * reader.bytes_per_frame()); +} + +TEST_CASE("Multi-threaded reader can close its worker files", + "[MultiThreadedFileReader]") { + TemporaryNumpyFile file; + MultiThreadedFileReader reader(file.path(), 2, 2); + + CHECK(reader.is_open()); + reader.close(); + CHECK_FALSE(reader.is_open()); + CHECK_NOTHROW(reader.close()); + CHECK_THROWS_AS(reader.read(), std::runtime_error); + CHECK_THROWS_AS(reader.read_all(), std::runtime_error); + CHECK_THROWS_AS(reader.seek(0), std::runtime_error); +} diff --git a/src/NumpyHelpers.test.cpp b/src/NumpyHelpers.test.cpp index da170831..cf2f9fce 100644 --- a/src/NumpyHelpers.test.cpp +++ b/src/NumpyHelpers.test.cpp @@ -34,6 +34,9 @@ TEST_CASE("trim whitespace") { } TEST_CASE("parse data type descriptions") { + REQUIRE(parse_descr("|i1") == aare::Dtype::INT8); + REQUIRE(parse_descr("|u1") == aare::Dtype::UINT8); + REQUIRE(parse_descr(" Date: Fri, 14 Aug 2026 10:31:07 +0200 Subject: [PATCH 02/10] Removing deprecated use of lmfit (#344) Removed the lmfit based fitting and the old API for calling functions closes #296 --- CMakeLists.txt | 64 +- RELEASE.md | 13 +- benchmarks/fit_benchmark.cpp | 25 +- docs/src/Requirements.rst | 4 +- docs/src/pyFit.rst | 29 +- include/aare/Fit.hpp | 100 --- patches/lmfit.patch | 13 - python/CMakeLists.txt | 1 - python/aare/__init__.py | 4 - python/aare/func.py | 2 - python/examples/fits.py | 112 +-- python/src/fit.hpp | 448 +---------- python/tests/test_Fit.py | 15 + python/tests/test_Minuit2_gauss.ipynb | 891 ++++++++++------------ python/tests/test_Minuit2_scurve_1d.ipynb | 160 +--- python/tests/test_Minuit2_scurve_3d.ipynb | 143 +--- src/Fit.cpp | 453 +---------- src/Fit.test.cpp | 81 ++ 18 files changed, 635 insertions(+), 1923 deletions(-) delete mode 100644 patches/lmfit.patch delete mode 100644 python/aare/func.py create mode 100644 python/tests/test_Fit.py create mode 100644 src/Fit.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a3d96cc4..995abd97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,6 @@ option(AARE_FETCH_PYBIND11 "Use FetchContent to download pybind11" ON) option(AARE_FETCH_CATCH "Use FetchContent to download catch2" ON) option(AARE_FETCH_JSON "Use FetchContent to download nlohmann::json" ON) option(AARE_FETCH_ZMQ "Use FetchContent to download libzmq" ON) -option(AARE_FETCH_LMFIT "Use FetchContent to download lmfit" ON) option(AARE_FETCH_MINUIT2 "Use FetchContent to download Minuit2" ON) option(AARE_WARNINGS_AS_ERRORS "Treat warnings as errors during compilation" @@ -87,8 +86,8 @@ if(AARE_SYSTEM_LIBRARIES) set(AARE_FETCH_ZMQ OFF CACHE BOOL "Disabled FetchContent for libzmq" FORCE) - # Still fetch lmfit and Minuit2 when setting AARE_SYSTEM_LIBRARIES since these - # are not available on conda-forge + # Still fetch Minuit2 when setting AARE_SYSTEM_LIBRARIES since it is not + # available on conda-forge endif() if(AARE_BENCHMARKS) @@ -97,61 +96,6 @@ endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -if(AARE_FETCH_LMFIT) - # TODO! Should we fetch lmfit from the web or inlcude a tar.gz in the repo? - set(LMFIT_PATCH_COMMAND git apply - ${CMAKE_CURRENT_SOURCE_DIR}/patches/lmfit.patch) - - # For cmake < 3.28 we can't supply EXCLUDE_FROM_ALL to FetchContent_Declare so - # we need this workaround - if(${CMAKE_VERSION} VERSION_LESS "3.28") - FetchContent_Declare( - lmfit - GIT_REPOSITORY https://jugit.fz-juelich.de/mlz/lmfit.git - GIT_TAG main - PATCH_COMMAND ${LMFIT_PATCH_COMMAND} - UPDATE_DISCONNECTED 1) - else() - FetchContent_Declare( - lmfit - GIT_REPOSITORY https://jugit.fz-juelich.de/mlz/lmfit.git - GIT_TAG main - PATCH_COMMAND ${LMFIT_PATCH_COMMAND} - UPDATE_DISCONNECTED 1 - EXCLUDE_FROM_ALL 1) - endif() - - # Disable what we don't need from lmfit - set(BUILD_TESTING - OFF - CACHE BOOL "") - set(LMFIT_CPPTEST - OFF - CACHE BOOL "") - set(LIB_MAN - OFF - CACHE BOOL "") - set(LMFIT_CPPTEST - OFF - CACHE BOOL "") - set(BUILD_SHARED_LIBS - OFF - CACHE BOOL "") - - if(${CMAKE_VERSION} VERSION_LESS "3.28") - if(NOT lmfit_POPULATED) - FetchContent_Populate(lmfit) - add_subdirectory(${lmfit_SOURCE_DIR} ${lmfit_BINARY_DIR} EXCLUDE_FROM_ALL) - endif() - else() - FetchContent_MakeAvailable(lmfit) - endif() - - set_property(TARGET lmfit PROPERTY POSITION_INDEPENDENT_CODE ON) -else() - find_package(lmfit REQUIRED) -endif() - if(AARE_FETCH_MINUIT2) # We are building Minuit2 from sources. @@ -457,8 +401,7 @@ target_link_libraries( aare_core PUBLIC fmt::fmt nlohmann_json::nlohmann_json ${STD_FS_LIB} # from # helpers.cmake - PRIVATE aare_compiler_flags Threads::Threads $ - $) + PRIVATE aare_compiler_flags Threads::Threads $) target_include_directories( aare_core SYSTEM @@ -490,6 +433,7 @@ if(AARE_TESTS) ${CMAKE_CURRENT_SOURCE_DIR}/src/defs.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Fit.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Interpolation.test.cpp diff --git a/RELEASE.md b/RELEASE.md index 1b51a49a..9739ab2e 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,17 @@ # Release notes +## Next + +### API Changes: + +- Removed the lmfit dependency and the legacy ``fit_gaus``, ``fit_pol1``, + ``fit_scurve``, and ``fit_scurve2`` APIs. Use ``Gaussian``, ``Pol1``, + ``RisingScurve``, or ``FallingScurve`` and call ``model.fit(...)`` (or + ``fit(model, ...)``) instead. +- Removed the legacy ``gaus``, ``pol1``, ``scurve``, and ``scurve2`` function + evaluators. Model objects are callable and provide the replacement, for + example ``Gaussian()(x, par)``. + ## 2026.7.2 @@ -151,4 +163,3 @@ dhanya.thattil@psi.ch - diff --git a/benchmarks/fit_benchmark.cpp b/benchmarks/fit_benchmark.cpp index 81d5451b..626bde8d 100644 --- a/benchmarks/fit_benchmark.cpp +++ b/benchmarks/fit_benchmark.cpp @@ -75,24 +75,7 @@ static void report_accuracy(benchmark::State &state, const TestCase &tc, // Benchmarks // ---------- -// 1. lmcurve -static void BM_FitGausLm(benchmark::State &state) { - const auto &tc = get_test_cases()[state.range(0)]; - auto data = generate_gaussian_data(tc); - auto xv = data.x.view(); - auto yv = data.y.view(); - - aare::NDArray result; - for (auto _ : state) { - result = aare::fit_gaus(xv, yv); - benchmark::DoNotOptimize(result.data()); - } - - report_accuracy(state, tc, result); - state.SetLabel(tc.name); -} - -// 2. Minuit2, analytic gradient (no Hesse) +// Minuit2, analytic gradient (no Hesse) static void BM_FitGausMinuitGrad(benchmark::State &state) { const auto &tc = get_test_cases()[state.range(0)]; auto data = generate_gaussian_data(tc); @@ -115,7 +98,7 @@ static void BM_FitGausMinuitGrad(benchmark::State &state) { state.SetLabel(tc.name); } -// 3. Minuit2, analytic gradient + Hesse +// Minuit2, analytic gradient + Hesse static void BM_FitGausMinuitGradHesse(benchmark::State &state) { const auto &tc = get_test_cases()[state.range(0)]; auto data = generate_gaussian_data(tc); @@ -145,8 +128,6 @@ static void BM_FitGausMinuitGradHesse(benchmark::State &state) { state.SetLabel(tc.name); } -BENCHMARK(BM_FitGausLm)->DenseRange(0, 5)->Unit(benchmark::kMicrosecond); - BENCHMARK(BM_FitGausMinuitGrad) ->DenseRange(0, 5) ->Unit(benchmark::kMicrosecond); @@ -155,4 +136,4 @@ BENCHMARK(BM_FitGausMinuitGradHesse) ->DenseRange(0, 5) ->Unit(benchmark::kMicrosecond); -BENCHMARK_MAIN(); \ No newline at end of file +BENCHMARK_MAIN(); diff --git a/docs/src/Requirements.rst b/docs/src/Requirements.rst index b0c370f8..7c44dbdc 100644 --- a/docs/src/Requirements.rst +++ b/docs/src/Requirements.rst @@ -14,7 +14,7 @@ Requirements To simplify deployment we build and statically link a few libraries. - fmt -- lmfit - https://jugit.fz-juelich.de/mlz/lmfit +- Minuit2 - nlohmann_json - pybind11 - ZeroMQ @@ -23,4 +23,4 @@ To simplify deployment we build and statically link a few libraries. - Sphinx - Breathe -- Doxygen \ No newline at end of file +- Doxygen diff --git a/docs/src/pyFit.rst b/docs/src/pyFit.rst index abaa3cf8..257f7414 100644 --- a/docs/src/pyFit.rst +++ b/docs/src/pyFit.rst @@ -1,19 +1,30 @@ - -Fit -======== +Fitting +------- .. py:currentmodule:: aare +Aare fits one-dimensional scans and three-dimensional pixel data with +Minuit2. Create a model object and call its :meth:`fit` method:: -**Functions** + model = Gaussian(compute_errors=True) + result = model.fit(x, y, y_err) -.. autofunction:: gaus +The model object is also callable, which evaluates it at the supplied points:: -.. autofunction:: pol1 + fitted_y = model(x, result["par"]) +The available models are ``Gaussian``, ``GaussianErfcPlateau``, +``GaussianChargeSharing``, ``GaussianChargeSharingKb``, ``Pol1``, ``Pol2``, +``RisingScurve``, and ``FallingScurve``. The module-level :func:`fit` function +accepts the same model objects when a functional interface is preferred. -**Fitting** +For three-dimensional data, pass an array with shape +``(rows, columns, scan_points)`` and select the worker count with +``n_threads``:: -.. autofunction:: fit_gaus + result = model.fit(x, image_data, image_errors, n_threads=8) -.. autofunction:: fit_pol1 \ No newline at end of file +The result dictionary contains ``par`` and ``chi2``. It also contains +``par_err`` when ``compute_errors`` is enabled. + +.. autofunction:: fit diff --git a/include/aare/Fit.hpp b/include/aare/Fit.hpp index a641e131..1a2fa55f 100644 --- a/include/aare/Fit.hpp +++ b/include/aare/Fit.hpp @@ -1,111 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 #pragma once -#include -#include - #include "aare/FitModel.hpp" #include "aare/NDArray.hpp" -#include "aare/utils/par.hpp" -#include "aare/utils/task.hpp" namespace aare { -namespace func { -double gaus(const double x, const double *par); -NDArray gaus(NDView x, NDView par); - -double pol1(const double x, const double *par); -NDArray pol1(NDView x, NDView par); - -double scurve(const double x, const double *par); -NDArray scurve(NDView x, NDView par); - -double scurve2(const double x, const double *par); -NDArray scurve2(NDView x, NDView par); - -} // namespace func - -static constexpr int DEFAULT_NUM_THREADS = 4; - -/** - * @brief Fit a 1D Gaussian to data. - * @param data data to fit - * @param x x values - */ -NDArray fit_gaus(NDView x, NDView y); - -/** - * @brief Fit a 1D Gaussian to each pixel. Data layout [row, col, values] - * @param x x values - * @param y y values, layout [row, col, values] - * @param n_threads number of threads to use - */ -NDArray fit_gaus(NDView x, NDView y, - int n_threads = DEFAULT_NUM_THREADS); - -/** - * @brief Fit a 1D Gaussian with error estimates - * @param x x values - * @param y y values, layout [row, col, values] - * @param y_err error in y, layout [row, col, values] - * @param par_out output parameters - * @param par_err_out output error parameters - */ -void fit_gaus(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - double &chi2); - -/** - * @brief Fit a 1D Gaussian to each pixel with error estimates. Data layout - * [row, col, values] - * @param x x values - * @param y y values, layout [row, col, values] - * @param y_err error in y, layout [row, col, values] - * @param par_out output parameters, layout [row, col, values] - * @param par_err_out output parameter errors, layout [row, col, values] - * @param n_threads number of threads to use - */ -void fit_gaus(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - NDView chi2_out, int n_threads = DEFAULT_NUM_THREADS); - -NDArray fit_pol1(NDView x, NDView y); - -NDArray fit_pol1(NDView x, NDView y, - int n_threads = DEFAULT_NUM_THREADS); - -void fit_pol1(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - double &chi2); - -// TODO! not sure we need to offer the different version in C++ -void fit_pol1(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - NDView chi2_out, int n_threads = DEFAULT_NUM_THREADS); - -NDArray fit_scurve(NDView x, NDView y); -NDArray fit_scurve(NDView x, NDView y, - int n_threads); -void fit_scurve(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, double &chi2); -void fit_scurve(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, NDView chi2_out, - int n_threads); - -NDArray fit_scurve2(NDView x, NDView y); -NDArray fit_scurve2(NDView x, NDView y, - int n_threads); -void fit_scurve2(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, double &chi2); -void fit_scurve2(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, NDView chi2_out, - int n_threads); - // --------------------------------------------------------------------------- // Minuit2-based pixel fitting. // Template bodies and explicit instantiations live in src/Fit.cpp. diff --git a/patches/lmfit.patch b/patches/lmfit.patch deleted file mode 100644 index 22063bf6..00000000 --- a/patches/lmfit.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt -index 4efb7ed..6533660 100644 ---- a/lib/CMakeLists.txt -+++ b/lib/CMakeLists.txt -@@ -11,7 +11,7 @@ target_compile_definitions(${lib} PRIVATE "LMFIT_EXPORT") # for Windows DLL expo - - target_include_directories(${lib} - PUBLIC -- $ -+ $ - $ - ) - diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 5f877773..401bf26b 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -41,7 +41,6 @@ set(PYTHON_FILES aare/Cluster.py aare/calibration.py aare/experimental.py - aare/func.py aare/RawFile.py aare/transform.py aare/ScanParameters.py diff --git a/python/aare/__init__.py b/python/aare/__init__.py index c8db2e4e..84224613 100644 --- a/python/aare/__init__.py +++ b/python/aare/__init__.py @@ -26,7 +26,6 @@ from .Cluster import Cluster from ._aare import Gaussian, RisingScurve, FallingScurve, Pol1, Pol2, GaussianErfcPlateau, GaussianChargeSharing, GaussianChargeSharingKb from ._aare import fit -from ._aare import fit_gaus, fit_pol1, fit_scurve, fit_scurve2 from ._aare import Interpolator from ._aare import calculate_eta2, calculate_eta3, calculate_cross_eta3, calculate_full_eta2 from ._aare import reduce_to_2x2, reduce_to_3x3 @@ -42,9 +41,6 @@ from .ScanParameters import ScanParameters from .utils import random_pixels, random_pixel, flat_list, add_colorbar, Timer -#make functions available in the top level API -from .func import * - from .calibration import * from ._aare import apply_calibration, count_switching_pixels from ._aare import calculate_pedestal, calculate_pedestal_float, calculate_pedestal_g0, calculate_pedestal_g0_float diff --git a/python/aare/func.py b/python/aare/func.py deleted file mode 100644 index ed2ef0c2..00000000 --- a/python/aare/func.py +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -from ._aare import gaus, pol1, scurve, scurve2 \ No newline at end of file diff --git a/python/examples/fits.py b/python/examples/fits.py index 40172b22..f4037de1 100644 --- a/python/examples/fits.py +++ b/python/examples/fits.py @@ -1,106 +1,78 @@ # SPDX-License-Identifier: MPL-2.0 import matplotlib.pyplot as plt import numpy as np -import sys -sys.path.insert(0, '/home/kferjaoui/sw/aare/build') -from aare import fit_gaus, fit_pol1 -from aare import Gaussian, fit -from aare import pol1 -textpm = f"±" # -textmu = f"μ" # -textsigma = f"σ" # +from aare import Gaussian, Pol1 +textpm = "±" +textmu = "μ" +textsigma = "σ" # ================================= Gauss fit ================================= -# Parameters -mu = np.random.uniform(1, 100) # Mean of Gaussian -sigma = np.random.uniform(4, 20) # Standard deviation -num_points = 10000 # Number of points for smooth distribution -# noise_sigma = 10 - -# Generate Gaussian distribution -data = np.random.normal(mu, sigma, num_points) +mu = np.random.uniform(1, 100) +sigma = np.random.uniform(4, 20) +data = np.random.normal(mu, sigma, 10000) counts, edges = np.histogram(data, bins=100) -x = 0.5 * (edges[:-1] + edges[1:]) # proper bin centers +x = 0.5 * (edges[:-1] + edges[1:]) y = counts.astype(np.float64) - -# Poisson noise yerr = np.sqrt(np.maximum(y, 1)) -# yerr = np.abs(np.random.normal(0, noise_sigma, len(x))) -# Create subplot fig0, ax0 = plt.subplots(1, 1, num=0, figsize=(12, 8)) - -# Add the errors as error bars in the step plot ax0.errorbar(x, y, yerr=yerr, fmt=". ", capsize=5) ax0.grid() -# Fit with lmfit -result_lm = fit_gaus(x, y, yerr) -par_lm = result_lm["par"] -err_lm = result_lm["par_err"] -chi2_lm = result_lm["chi2"] -print("[lmfit] fit_gaus: ", par_lm, err_lm, chi2_lm) +gaussian = Gaussian(compute_errors=True) +result = gaussian.fit(x, y, yerr) +par = result["par"] +err = result["par_err"] +chi2 = result["chi2"] +print(f"Gaussian.fit: par={par}, err={err}, chi2={chi2}") -# Fit with Minuit2 + analytic gradient + Hesse errors -gaussian = Gaussian() -gaussian.compute_errors = True -result_m2 = gaussian.fit(x, y, yerr) -par_m2 = result_m2['par'] -err_m2 = result_m2['par_err'] -chi2_m2 = result_m2['chi2'] -print(f"[minuit2] gaussian.fit: par={par_m2}, err={err_m2}, chi2={chi2_m2}") - -x = np.linspace(x[0], x[-1], 1000) -ax0.plot(x, gaussian(x, par_lm), marker="", label="fit_gaus") -ax0.plot(x, gaussian(x, par_m2), marker="", linestyle=":", label="fit_gaus_minuit_grad") +x_plot = np.linspace(x[0], x[-1], 1000) +ax0.plot(x_plot, gaussian(x_plot, par), marker="", label="Gaussian.fit") ax0.legend() -ax0.set(xlabel="x", ylabel="Counts", +ax0.set( + xlabel="x", + ylabel="Counts", title=( - f"fit_gaus: A={par_lm[0]:0.2f}{textpm}{err_lm[0]:0.2f} " - f"{textmu}={par_lm[1]:0.2f}{textpm}{err_lm[1]:0.2f} " - f"{textsigma}={par_lm[2]:0.2f}{textpm}{err_lm[2]:0.2f}\n" - f"minuit_grad: A={par_m2[0]:0.2f}{textpm}{err_m2[0]:0.2f} " - f"{textmu}={par_m2[1]:0.2f}{textpm}{err_m2[1]:0.2f} " - f"{textsigma}={par_m2[2]:0.2f}{textpm}{err_m2[2]:0.2f}\n" + f"A={par[0]:0.2f}{textpm}{err[0]:0.2f} " + f"{textmu}={par[1]:0.2f}{textpm}{err[1]:0.2f} " + f"{textsigma}={par[2]:0.2f}{textpm}{err[2]:0.2f}\n" f"(truth: {textmu}={mu:0.2f}, {textsigma}={sigma:0.2f})" ), ) fig0.tight_layout() - -# ================================= pol1 fit ================================= -# Parameters +# ================================= Pol1 fit ================================= n_points = 40 - -# Generate random slope and intercept (origin) -slope = np.random.uniform(-10, 10) # Random slope between 0.5 and 2.0 -intercept = np.random.uniform(-10, 10) # Random intercept between -10 and 10 - -# Generate random x values +slope = np.random.uniform(-10, 10) +intercept = np.random.uniform(-10, 10) x_values = np.random.uniform(-10, 10, n_points) - -# Calculate y values based on the linear function y = mx + b + error errors = np.abs(np.random.normal(0, np.random.uniform(1, 5), n_points)) -var_points = np.random.normal(0, np.random.uniform(0.1, 2), n_points) -y_values = slope * x_values + intercept + var_points +y_values = slope * x_values + intercept + np.random.normal(0, 1, n_points) fig1, ax1 = plt.subplots(1, 1, num=1, figsize=(12, 8)) ax1.errorbar(x_values, y_values, yerr=errors, fmt=". ", capsize=5) -result_pol = fit_pol1(x_values, y_values, errors) -par = result_pol["par"] -err = result_pol["par_err"] -x = np.linspace(np.min(x_values), np.max(x_values), 1000) -ax1.plot(x, pol1(x, par), marker="") -ax1.set(xlabel="x", ylabel="y", title=f"a = {par[0]:0.2f}{textpm}{err[0]:0.2f}\n" - f"b = {par[1]:0.2f}{textpm}{err[1]:0.2f}\n" - f"(init: {slope:0.2f}, {intercept:0.2f})") +pol1 = Pol1(compute_errors=True) +result = pol1.fit(x_values, y_values, errors) +par = result["par"] +err = result["par_err"] + +x_plot = np.linspace(np.min(x_values), np.max(x_values), 1000) +ax1.plot(x_plot, pol1(x_plot, par), marker="") +ax1.set( + xlabel="x", + ylabel="y", + title=( + f"intercept = {par[0]:0.2f}{textpm}{err[0]:0.2f}\n" + f"slope = {par[1]:0.2f}{textpm}{err[1]:0.2f}\n" + f"(truth: {intercept:0.2f}, {slope:0.2f})" + ), +) fig1.tight_layout() plt.show() - diff --git a/python/src/fit.hpp b/python/src/fit.hpp index b07dadfa..82bff325 100644 --- a/python/src/fit.hpp +++ b/python/src/fit.hpp @@ -227,452 +227,6 @@ fit_dispatch(const aare::FitModel &model, } void define_fit_bindings(py::module &m) { - - // TODO! Evaluate without converting to double - m.def( - "gaus", - [](py::array_t x, - py::array_t par) { - auto x_view = make_view_1d(x); - auto par_view = make_view_1d(par); - auto y = new NDArray{aare::func::gaus(x_view, par_view)}; - return return_image_data(y); - }, - R"( - Evaluate a 1D Gaussian function for all points in x using parameters par. - - Parameters - ---------- - x : array_like - The points at which to evaluate the Gaussian function. - par : array_like - The parameters of the Gaussian function. The first element is the amplitude, the second element is the mean, and the third element is the standard deviation. - )", - py::arg("x"), py::arg("par")); - - m.def( - "pol1", - [](py::array_t x, - py::array_t par) { - auto x_view = make_view_1d(x); - auto par_view = make_view_1d(par); - auto y = new NDArray{aare::func::pol1(x_view, par_view)}; - return return_image_data(y); - }, - R"( - Evaluate a 1D polynomial function for all points in x using parameters par. (p0+p1*x) - - Parameters - ---------- - x : array_like - The points at which to evaluate the polynomial function. - par : array_like - The parameters of the polynomial function. The first element is the intercept, and the second element is the slope. - )", - py::arg("x"), py::arg("par")); - - m.def( - "scurve", - [](py::array_t x, - py::array_t par) { - auto x_view = make_view_1d(x); - auto par_view = make_view_1d(par); - auto y = - new NDArray{aare::func::scurve(x_view, par_view)}; - return return_image_data(y); - }, - R"( - Evaluate a 1D scurve function for all points in x using parameters par. - - Parameters - ---------- - x : array_like - The points at which to evaluate the scurve function. - par : array_like - The parameters of the scurve function. The first element is the background slope, the second element is the background intercept, the third element is the mean, the fourth element is the standard deviation, the fifth element is inflexion point count number, and the sixth element is C. - )", - py::arg("x"), py::arg("par")); - - m.def( - "scurve2", - [](py::array_t x, - py::array_t par) { - auto x_view = make_view_1d(x); - auto par_view = make_view_1d(par); - auto y = - new NDArray{aare::func::scurve2(x_view, par_view)}; - return return_image_data(y); - }, - R"( - Evaluate a 1D scurve2 function for all points in x using parameters par. - - Parameters - ---------- - x : array_like - The points at which to evaluate the scurve function. - par : array_like - The parameters of the scurve2 function. The first element is the background slope, the second element is the background intercept, the third element is the mean, the fourth element is the standard deviation, the fifth element is inflexion point count number, and the sixth element is C. - )", - py::arg("x"), py::arg("par")); - - m.def( - "fit_gaus", - [](py::array_t x, - py::array_t y, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray{}; - auto y_view = make_view_3d(y); - auto x_view = make_view_1d(x); - *par = aare::fit_gaus(x_view, y_view, n_threads); - return return_image_data(par); - } else if (y.ndim() == 1) { - auto par = new NDArray{}; - auto y_view = make_view_1d(y); - auto x_view = make_view_1d(x); - *par = aare::fit_gaus(x_view, y_view); - return return_image_data(par); - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - R"( - Fit a 1D Gaussian to data. - - Parameters - ---------- - x : array_like - The x values. - y : array_like - The y values. - n_threads : int, optional - The number of threads to use. Default is 4. - )", - py::arg("x"), py::arg("y"), py::arg("n_threads") = 4); - - m.def( - "fit_gaus", - [](py::array_t x, - py::array_t y, - py::array_t y_err, - int n_threads) { - if (y.ndim() == 3) { - // Allocate memory for the output - // Need to have pointers to allow python to manage - // the memory - auto par = new NDArray({y.shape(0), y.shape(1), 3}); - auto par_err = - new NDArray({y.shape(0), y.shape(1), 3}); - auto chi2 = new NDArray({y.shape(0), y.shape(1)}); - - // Make views of the numpy arrays - auto y_view = make_view_3d(y); - auto y_view_err = make_view_3d(y_err); - auto x_view = make_view_1d(x); - - aare::fit_gaus(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2->view(), n_threads); - - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = return_image_data(chi2), - "Ndf"_a = y.shape(2) - 3); - } else if (y.ndim() == 1) { - // Allocate memory for the output - // Need to have pointers to allow python to manage - // the memory - auto par = new NDArray({3}); - auto par_err = new NDArray({3}); - - // Decode the numpy arrays - auto y_view = make_view_1d(y); - auto y_view_err = make_view_1d(y_err); - auto x_view = make_view_1d(x); - - double chi2 = 0; - aare::fit_gaus(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2); - - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = chi2, "Ndf"_a = y.size() - 3); - - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - R"( - Fit a 1D Gaussian to data with error estimates. - - Parameters - ---------- - x : array_like - The x values. - y : array_like - The y values. - y_err : array_like - The error in the y values. - n_threads : int, optional - The number of threads to use. Default is 4. - )", - py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4); - - m.def( - "fit_pol1", - [](py::array_t x, - py::array_t y, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray{}; - - auto x_view = make_view_1d(x); - auto y_view = make_view_3d(y); - *par = aare::fit_pol1(x_view, y_view, n_threads); - return return_image_data(par); - } else if (y.ndim() == 1) { - auto par = new NDArray{}; - auto x_view = make_view_1d(x); - auto y_view = make_view_1d(y); - *par = aare::fit_pol1(x_view, y_view); - return return_image_data(par); - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - py::arg("x"), py::arg("y"), py::arg("n_threads") = 4); - - m.def( - "fit_pol1", - [](py::array_t x, - py::array_t y, - py::array_t y_err, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray({y.shape(0), y.shape(1), 2}); - - auto par_err = - new NDArray({y.shape(0), y.shape(1), 2}); - - auto y_view = make_view_3d(y); - auto y_view_err = make_view_3d(y_err); - auto x_view = make_view_1d(x); - - auto chi2 = new NDArray({y.shape(0), y.shape(1)}); - - aare::fit_pol1(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2->view(), n_threads); - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = return_image_data(chi2), - "Ndf"_a = y.shape(2) - 2); - - } else if (y.ndim() == 1) { - auto par = new NDArray({2}); - auto par_err = new NDArray({2}); - - auto y_view = make_view_1d(y); - auto y_view_err = make_view_1d(y_err); - auto x_view = make_view_1d(x); - - double chi2 = 0; - - aare::fit_pol1(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2); - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = chi2, "Ndf"_a = y.size() - 2); - - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - R"( - Fit a 1D polynomial to data with error estimates. - - Parameters - ---------- - x : array_like - The x values. - y : array_like - The y values. - y_err : array_like - The error in the y values. - n_threads : int, optional - The number of threads to use. Default is 4. - )", - py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4); - - //========= - m.def( - "fit_scurve", - [](py::array_t x, - py::array_t y, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray{}; - - auto x_view = make_view_1d(x); - auto y_view = make_view_3d(y); - *par = aare::fit_scurve(x_view, y_view, n_threads); - return return_image_data(par); - } else if (y.ndim() == 1) { - auto par = new NDArray{}; - auto x_view = make_view_1d(x); - auto y_view = make_view_1d(y); - *par = aare::fit_scurve(x_view, y_view); - return return_image_data(par); - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - py::arg("x"), py::arg("y"), py::arg("n_threads") = 4); - - m.def( - "fit_scurve", - [](py::array_t x, - py::array_t y, - py::array_t y_err, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray({y.shape(0), y.shape(1), 6}); - - auto par_err = - new NDArray({y.shape(0), y.shape(1), 6}); - - auto y_view = make_view_3d(y); - auto y_view_err = make_view_3d(y_err); - auto x_view = make_view_1d(x); - - auto chi2 = new NDArray({y.shape(0), y.shape(1)}); - - aare::fit_scurve(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2->view(), n_threads); - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = return_image_data(chi2), - "Ndf"_a = y.shape(2) - 2); - - } else if (y.ndim() == 1) { - auto par = new NDArray({2}); - auto par_err = new NDArray({2}); - - auto y_view = make_view_1d(y); - auto y_view_err = make_view_1d(y_err); - auto x_view = make_view_1d(x); - - double chi2 = 0; - - aare::fit_scurve(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2); - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = chi2, "Ndf"_a = y.size() - 2); - - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - R"( - Fit a 1D polynomial to data with error estimates. - - Parameters - ---------- - x : array_like - The x values. - y : array_like - The y values. - y_err : array_like - The error in the y values. - n_threads : int, optional - The number of threads to use. Default is 4. - )", - py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4); - - m.def( - "fit_scurve2", - [](py::array_t x, - py::array_t y, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray{}; - - auto x_view = make_view_1d(x); - auto y_view = make_view_3d(y); - *par = aare::fit_scurve2(x_view, y_view, n_threads); - return return_image_data(par); - } else if (y.ndim() == 1) { - auto par = new NDArray{}; - auto x_view = make_view_1d(x); - auto y_view = make_view_1d(y); - *par = aare::fit_scurve2(x_view, y_view); - return return_image_data(par); - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - py::arg("x"), py::arg("y"), py::arg("n_threads") = 4); - - m.def( - "fit_scurve2", - [](py::array_t x, - py::array_t y, - py::array_t y_err, - int n_threads) { - if (y.ndim() == 3) { - auto par = new NDArray({y.shape(0), y.shape(1), 6}); - - auto par_err = - new NDArray({y.shape(0), y.shape(1), 6}); - - auto y_view = make_view_3d(y); - auto y_view_err = make_view_3d(y_err); - auto x_view = make_view_1d(x); - - auto chi2 = new NDArray({y.shape(0), y.shape(1)}); - - aare::fit_scurve2(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2->view(), n_threads); - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = return_image_data(chi2), - "Ndf"_a = y.shape(2) - 2); - - } else if (y.ndim() == 1) { - auto par = new NDArray({6}); - auto par_err = new NDArray({6}); - - auto y_view = make_view_1d(y); - auto y_view_err = make_view_1d(y_err); - auto x_view = make_view_1d(x); - - double chi2 = 0; - - aare::fit_scurve2(x_view, y_view, y_view_err, par->view(), - par_err->view(), chi2); - return py::dict("par"_a = return_image_data(par), - "par_err"_a = return_image_data(par_err), - "chi2"_a = chi2, "Ndf"_a = y.size() - 2); - - } else { - throw std::runtime_error("Data must be 1D or 3D"); - } - }, - R"( - Fit a 1D polynomial to data with error estimates. - - Parameters - ---------- - x : array_like - The x values. - y : array_like - The y values. - y_err : array_like - The error in the y values. - n_threads : int, optional - The number of threads to use. Default is 4. - )", - py::arg("x"), py::arg("y"), py::arg("y_err"), py::arg("n_threads") = 4); - // ── Bind model classes ────────────────────────────────────────── bind_fit_model(m, "Gaussian"); bind_fit_model(m, "GaussianErfcPlateau"); @@ -794,4 +348,4 @@ void define_fit_bindings(py::module &m) { )", py::arg("model"), py::arg("x"), py::arg("y"), py::arg("y_err") = py::none(), py::arg("n_threads") = 4); -} \ No newline at end of file +} diff --git a/python/tests/test_Fit.py b/python/tests/test_Fit.py new file mode 100644 index 00000000..3a477b27 --- /dev/null +++ b/python/tests/test_Fit.py @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MPL-2.0 +import numpy as np +import aare + + + +def test_gaussian_model_evaluates_and_fits_data(): + x = np.linspace(-5.0, 5.0, 51) + expected = np.array([20.0, 0.5, 1.2]) + model = aare.Gaussian() + + y = model(x, expected) + result = model.fit(x, y) + + np.testing.assert_allclose(result["par"], expected, atol=2e-3) diff --git a/python/tests/test_Minuit2_gauss.ipynb b/python/tests/test_Minuit2_gauss.ipynb index 0a6bdd2a..0a28ec3b 100644 --- a/python/tests/test_Minuit2_gauss.ipynb +++ b/python/tests/test_Minuit2_gauss.ipynb @@ -1,488 +1,413 @@ { - "cells": - [ - { - "cell_type": "code", - "execution_count": 2, - "id": "efef8e20-6571-4561-8f0b-6048b57907de", - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "import random\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from matplotlib.gridspec import GridSpec\n", - "import sys\n", - "sys.path.insert(0, '/home/ferjao_k/aare/build')\n", - "from aare import fit_gaus # lmfit\n", - "from aare import Gaussian, fit # minuit2" - ] + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "efef8e20-6571-4561-8f0b-6048b57907de", + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "import random\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.gridspec import GridSpec\n", + "from aare import Gaussian\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35772e2c-37c6-4986-a9c0-7dca4a034827", + "metadata": {}, + "outputs": [], + "source": [ + "ROWS = 100\n", + "COLS = 100\n", + "N_SCAN = 100\n", + "NOISE_FRAC = 0.05\n", + "SEED = 42\n", + "N_THREADS = 4\n", + "\n", + "N_REPEATS = 7\n", + "N_WARMUP = 3 # untimed iterations (icache + branch predictor warmup)\n", + "COOLDOWN = 2.0 # seconds between (method, thread_count) pairs" + ] + }, + { + "cell_type": "markdown", + "id": "be455445-7df8-47cc-9f39-d2a38856e0bd", + "metadata": {}, + "source": [ + "## Data generator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06c8fddb-56d9-4f84-8b21-4ffd8b7bd26f", + "metadata": {}, + "outputs": [], + "source": [ + "def generate_3d_data(rows, cols, n_scan, noise_frac, seed):\n", + " \"\"\"\n", + " Generate a synthetic detector image stack where each pixel has a\n", + " Gaussian response curve with per-pixel variation in A, mu, sigma.\n", + "\n", + " Returns x (n_scan,), y (rows, cols, n_scan), y_err (rows, cols, n_scan),\n", + " and the ground-truth parameter arrays.\n", + " \"\"\"\n", + " rng = np.random.default_rng(seed)\n", + "\n", + " # Per-pixel true params each of shape: [rows, cols, 1]\n", + " A_true = rng.uniform(200, 1000, size=(rows, cols))\n", + " mu_true = rng.uniform(20, 80, size=(rows, cols))\n", + " sig_true = rng.uniform(3, 12, size=(rows, cols))\n", + " \n", + " # One common binned energy array\n", + " x = np.linspace(0, 100, n_scan) # shape [1, 1, nscan]\n", + "\n", + " # Build ground truth signals per-pixel\n", + " exponent = -0.5 * ((x[None, None, :] - mu_true[:, :, None]) / sig_true[:,:, None])**2 # shape [rows, cols, nscan]\n", + " y_clean = A_true[:, :, None] * np.exp(exponent)\n", + "\n", + " # Perturb with noise\n", + " noise_sigma = noise_frac * A_true[:, :, None] * np.ones_like(y_clean) # shape [rows, cols, nscan]\n", + " noise = rng.normal(0, noise_sigma)\n", + " y = y_clean + noise\n", + "\n", + " y_err = noise_sigma.copy()\n", + "\n", + " return x, y, y_err, A_true, mu_true, sig_true " + ] + }, + { + "cell_type": "markdown", + "id": "30ed6bb8-4798-497f-9990-4c518f885855", + "metadata": {}, + "source": [ + "## Profiling function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc0b52b6-9fc3-453d-b6eb-e325a2b8342e", + "metadata": {}, + "outputs": [], + "source": [ + "def bench(fn, n_warmup=N_WARMUP, n_repeats=N_REPEATS):\n", + " \"\"\"\n", + " Warmup then time `fn` over `n_repeats` calls.\n", + " Returns (last_result, list_of_walltimes_in_seconds).\n", + " \"\"\"\n", + " # warmup: primes icache, branch predictor, and lets CPU ramp to boost clock\n", + " for _ in range(n_warmup):\n", + " res = fn()\n", + "\n", + " times = []\n", + " for _ in range(n_repeats):\n", + " t0 = time.perf_counter()\n", + " res = fn()\n", + " t1 = time.perf_counter()\n", + " times.append(t1 - t0)\n", + " return res, times" + ] + }, + { + "cell_type": "markdown", + "id": "377fc820-95b2-48aa-b104-a272c50e4103", + "metadata": {}, + "source": [ + "# Quick check on small (2x2) frame" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9cc999b9-e0d8-4deb-ae29-e8bd0141534b", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate 2 x 2 dataset of Gaussian-like profiles for each pixel\n", + "x2, y2, yerr2, true_A2, true_mu2, true_sig2 = generate_3d_data(\n", + " 2, 2, N_SCAN, NOISE_FRAC, SEED\n", + ")\n", + "model_g = Gaussian()\n", + "model_g.compute_errors = True\n", + "result = model_g.fit(x2, y2, yerr2)\n", + "\n", + "from pprint import pprint\n", + "print(\"== True Gaussian params == \")\n", + "print(\"A_true = \\n\", true_A2)\n", + "print(\"mu_true = \\n\", true_mu2)\n", + "print(\"sig_true = \\n\",true_sig2)\n", + "print(\"\\n\")\n", + "\n", + "print(\"== Fit results ==\")\n", + "par = result['par']\n", + "# print(par)\n", + "A_fit = par[:, :, 0]\n", + "mu_fit = par[:, :, 1]\n", + "sig_fit = par[:, :, 2]\n", + "print(\"A_fit = \\n\", A_fit)\n", + "print(\"mu_fit = \\n\", mu_fit)\n", + "print(\"sig_fit = \\n\", sig_fit)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a98a41f3-fd2e-4bfc-9ec6-0d23dd38e896", + "metadata": {}, + "outputs": [], + "source": [ + "fig, ax = plt.subplots(2, 2, figsize=(12,8))\n", + "\n", + "# Gaussians in 2x2 frame: True vs Fit\n", + "for row in range(2):\n", + " for col in range(2):\n", + " ax[row, col].plot(x2, y2[row, col,:], label=\"data\")\n", + " ax[row, col].plot(x2, model_g(x2, result['par'][row, col,:]), linewidth=1, color=\"green\", label=\"minuit\")\n", + " ax[row, col].set_title(f\"Gaussian Fit to data in pixel [{row}, {col}]\")\n", + " ax[row, col].legend()" + ] + }, + { + "cell_type": "markdown", + "id": "fcf52481-0278-4f95-8676-829a6d61eff8", + "metadata": {}, + "source": [ + "## Fit data with different backends" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f6bc651-80c1-41dd-8a05-7f15aba006aa", + "metadata": {}, + "outputs": [], + "source": [ + "# ===============\n", + "# DATA GENERATION\n", + "# ===============\n", + "print(f\"Generating synthetic data: {ROWS}x{COLS} pixels, \"\n", + " f\"{N_SCAN} scan points, noise_frac={NOISE_FRAC}\\n\")\n", + "\n", + "x, y, yerr, true_A, true_mu, true_sig = generate_3d_data(\n", + " ROWS, COLS, N_SCAN, NOISE_FRAC, SEED\n", + ")\n", + "\n", + "model = Gaussian()\n", + "print(f\"model.max_calls = {model.max_calls}\")\n", + "print(f\"model.tolerance = {model.tolerance}\")\n", + "print(\"model.compute_errors =\", model.compute_errors)\n", + "METHOD_DEFS = [\n", + " (\"Minuit2 (obj API)\",\n", + " lambda nt: lambda: model.fit(x, y, n_threads=nt),\n", + " \"#FF9800\", {\"linewidth\": 2.5, \"linestyle\": \":\"}),\n", + "]\n", + "\n", + "colors = {label: c for label, _, c, _ in METHOD_DEFS}\n", + "styles = {label: s for label, _, _, s in METHOD_DEFS}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a417145-ce42-4c3a-a7bf-05ff97ba0450", + "metadata": {}, + "outputs": [], + "source": [ + "# ====================================\n", + "# SINGLE-CALL BENCHMARK (at N_THREADS)\n", + "# ====================================\n", + "def extract_result(label, res):\n", + " \"\"\"Normalize return values across fitters into a common dict.\"\"\"\n", + " if isinstance(res, dict):\n", + " out = {\"par\": res[\"par\"]}\n", + " if \"par_err\" in res:\n", + " out[\"par_err\"] = res[\"par_err\"]\n", + " if \"chi2\" in res:\n", + " out[\"chi2\"] = res[\"chi2\"]\n", + " return out\n", + " \n", + "methods = {}\n", + "for label, factory, _, _ in METHOD_DEFS:\n", + " time.sleep(COOLDOWN)\n", + " res, times = bench(factory(N_THREADS))\n", + " entry = extract_result(label, res)\n", + " entry[\"times\"] = times\n", + " methods[label] = entry\n", + "\n", + "# ---- Print summary ----\n", + "ndf = N_SCAN - 3\n", + "print(f\"{'Method':24s} {'time (ms)':>10s} {'med|dA|':>10s} {'med|dMu|':>10s} {'med|dSig|':>10s}\")\n", + "print(\"-\" * 80)\n", + "for name, m in methods.items():\n", + " par = m[\"par\"]\n", + " med_t = np.median(m[\"times\"]) * 1e3\n", + " dA = np.median(np.abs(par[:,:,0] - true_A))\n", + " dMu = np.median(np.abs(par[:,:,1] - true_mu))\n", + " dSig = np.median(np.abs(par[:,:,2] - true_sig))\n", + "\n", + " chi2_str = \"\"\n", + " if \"chi2\" in m:\n", + " chi2_str = f\" chi2/ndf={np.median(m['chi2'] / ndf):.4f}\"\n", + "\n", + " print(f\"[{name:22s}] {med_t:8.2f} ms \"\n", + " f\"{dA:10.3f} {dMu:10.4f} {dSig:10.4f}{chi2_str}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "55ecc77c-0823-408d-a811-8e4f4900f332", + "metadata": { + "scrolled": true }, - { - "cell_type": "code", - "execution_count": 3, - "id": "35772e2c-37c6-4986-a9c0-7dca4a034827", - "metadata": {}, - "outputs": [], - "source": [ - "ROWS = 100\n", - "COLS = 100\n", - "N_SCAN = 100\n", - "NOISE_FRAC = 0.05\n", - "SEED = 42\n", - "N_THREADS = 4\n", - "\n", - "N_REPEATS = 7\n", - "N_WARMUP = 3 # untimed iterations (icache + branch predictor warmup)\n", - "COOLDOWN = 2.0 # seconds between (method, thread_count) pairs" - ] - }, - { - "cell_type": "markdown", - "id": "be455445-7df8-47cc-9f39-d2a38856e0bd", - "metadata": {}, - "source": [ - "## Data generator" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "06c8fddb-56d9-4f84-8b21-4ffd8b7bd26f", - "metadata": {}, - "outputs": [], - "source": [ - "def generate_3d_data(rows, cols, n_scan, noise_frac, seed):\n", - " \"\"\"\n", - " Generate a synthetic detector image stack where each pixel has a\n", - " Gaussian response curve with per-pixel variation in A, mu, sigma.\n", - "\n", - " Returns x (n_scan,), y (rows, cols, n_scan), y_err (rows, cols, n_scan),\n", - " and the ground-truth parameter arrays.\n", - " \"\"\"\n", - " rng = np.random.default_rng(seed)\n", - "\n", - " # Per-pixel true params each of shape: [rows, cols, 1]\n", - " A_true = rng.uniform(200, 1000, size=(rows, cols))\n", - " mu_true = rng.uniform(20, 80, size=(rows, cols))\n", - " sig_true = rng.uniform(3, 12, size=(rows, cols))\n", - " \n", - " # One common binned energy array\n", - " x = np.linspace(0, 100, n_scan) # shape [1, 1, nscan]\n", - "\n", - " # Build ground truth signals per-pixel\n", - " exponent = -0.5 * ((x[None, None, :] - mu_true[:, :, None]) / sig_true[:,:, None])**2 # shape [rows, cols, nscan]\n", - " y_clean = A_true[:, :, None] * np.exp(exponent)\n", - "\n", - " # Perturb with noise\n", - " noise_sigma = noise_frac * A_true[:, :, None] * np.ones_like(y_clean) # shape [rows, cols, nscan]\n", - " noise = rng.normal(0, noise_sigma)\n", - " y = y_clean + noise\n", - "\n", - " y_err = noise_sigma.copy()\n", - "\n", - " return x, y, y_err, A_true, mu_true, sig_true " - ] - }, - { - "cell_type": "markdown", - "id": "30ed6bb8-4798-497f-9990-4c518f885855", - "metadata": {}, - "source": [ - "## Profiling function" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "dc0b52b6-9fc3-453d-b6eb-e325a2b8342e", - "metadata": {}, - "outputs": [], - "source": [ - "def bench(fn, n_warmup=N_WARMUP, n_repeats=N_REPEATS):\n", - " \"\"\"\n", - " Warmup then time `fn` over `n_repeats` calls.\n", - " Returns (last_result, list_of_walltimes_in_seconds).\n", - " \"\"\"\n", - " # warmup: primes icache, branch predictor, and lets CPU ramp to boost clock\n", - " for _ in range(n_warmup):\n", - " res = fn()\n", - "\n", - " times = []\n", - " for _ in range(n_repeats):\n", - " t0 = time.perf_counter()\n", - " res = fn()\n", - " t1 = time.perf_counter()\n", - " times.append(t1 - t0)\n", - " return res, times" - ] - }, - { - "cell_type": "markdown", - "id": "377fc820-95b2-48aa-b104-a272c50e4103", - "metadata": {}, - "source": [ - "# Quick check on small (2x2) frame" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "9cc999b9-e0d8-4deb-ae29-e8bd0141534b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "== True Gaussian params == \n", - "A_true = \n", - " [[819.16483884 551.1027518 ]\n", - " [886.87833593 757.89442325]]\n", - "mu_true = \n", - " [[25.65064087 78.5373411 ]\n", - " [65.66838212 67.16385832]]\n", - "sig_true = \n", - " [[ 4.15302269 7.05347344]\n", - " [ 6.33718222 11.3408849 ]]\n", - "\n", - "\n", - "== Fit results ==\n", - "A_fit = \n", - " [[812.09277132 559.04069721]\n", - " [899.09335849 759.24481682]]\n", - "mu_fit = \n", - " [[25.6598209 78.40461782]\n", - " [65.52261318 66.84540995]]\n", - "sig_fit = \n", - " [[ 4.2778026 7.041045 ]\n", - " [ 6.29190225 11.34233504]]\n" - ] - } - ], - "source": [ - "# Generate 2 x 2 dataset of Gaussian-like profiles for each pixel\n", - "x2, y2, yerr2, true_A2, true_mu2, true_sig2 = generate_3d_data(\n", - " 2, 2, N_SCAN, NOISE_FRAC, SEED\n", - ")\n", - "model_g = Gaussian()\n", - "model_g.compute_errors = True\n", - "result = model_g.fit(x2, y2, yerr2)\n", - "\n", - "from pprint import pprint\n", - "print(\"== True Gaussian params == \")\n", - "print(\"A_true = \\n\", true_A2)\n", - "print(\"mu_true = \\n\", true_mu2)\n", - "print(\"sig_true = \\n\",true_sig2)\n", - "print(\"\\n\")\n", - "\n", - "print(\"== Fit results ==\")\n", - "par = result['par']\n", - "# print(par)\n", - "A_fit = par[:, :, 0]\n", - "mu_fit = par[:, :, 1]\n", - "sig_fit = par[:, :, 2]\n", - "print(\"A_fit = \\n\", A_fit)\n", - "print(\"mu_fit = \\n\", mu_fit)\n", - "print(\"sig_fit = \\n\", sig_fit)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "a98a41f3-fd2e-4bfc-9ec6-0d23dd38e896", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": - "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Nzc3dVm9ejXeffdddVt7X/F4nRUdHY2OHTtWer916NABx48f53usBo8//jieeuopXHfddejSpQtuvPFGPPzww6ryLT+XF1ab7y3ZRlKOs7KyatyG7APb+tpjW187bOvrh+193bCtd4y23mkDbUkVkIBo2bJllR6X+/3797faftkS6dGR3u2ffvoJf/31l1pOyJzclzep+TGUN6wEl854DEeMGIHdu3erEUbtIqO1N9xwg7otyzDxeFU2YMCAc5aMk7nHzZs3V7f5HjtXQUEBdLrKX93Saagt78Vjdn61OT7SNri7u1faJiUlBfHx8U753WbP2NZfGNv6umFbXz9s7+uGbb2DtPUGJ6Yt+TF79my1vNdDDz2klvc6evSotXfNJtx9992qOt+qVasMKSkppktBQYFpG6nmJ9v89NNPqnT+9ddfz+W9zJhXHefxqn5pFDc3N8N//vMfw8GDBw3ffPONwcfHxzB37lwesxrcfPPNaikKbXkv+eyFhYUZnnjiCR4zs2rA27dvVxdp5t588011W1u6sTbfW7IiRdOmTdVyc7Lkx/Dhw7m8l51iW39+bOsvHtv6C2N7Xzds6x2jrXfqQFt88MEHah1fDw8PQ8+ePU1LV5GxVH51F1lb27x8/v/93/+pEvqenp6GwYMHqzczVd/48nida8mSJYbOnTur948sr/fpp59Wep7HrDJpIOQ9JWvae3l5GVq1aqXWkSwuLuYxq7By5cpqv7vkxKW276nCwkLDfffdZwgJCTF4e3sbxo8fbzh+/Di/2uwU2/qasa2/eGzra4ftfe2xrXeMtt5F/rHM2DgREREREREROe0cbSIiIiIiIqKGwECbiIiIiIiIyIIYaBMRERERERFZEANtIiIiIiIiIgtioE1ERERERERkQQy0iYiIiIiIiCyIgTYRERERERGRBTHQJiIiIiIiIrIgBtpEREREREREFsRAm4iIiIiIiMiCGGgTERERERERWRADbSIiIiIiIiILYqBNREREREREZEEMtImIiIiIiIgsiIG2g9u1axdmzJiB1q1bw9vbW13i4uJw5513YsuWLbAHq1atgouLi7pubDNnzlS/u7rL+++/X+2+/fbbb+r/1da8efPw9ttvo7EMHTpUXerj5ZdfxqJFi2xqny7W9OnT0aJFC6u/f83fa35+fpWeW7t2LW677Tb06tULnp6e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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "fig, ax = plt.subplots(2, 2, figsize=(12,8))\n", - "\n", - "# Gaussians in 2x2 frame: True vs Fit\n", - "for row in range(2):\n", - " for col in range(2):\n", - " ax[row, col].plot(x2, y2[row, col,:], label=\"data\")\n", - " ax[row, col].plot(x2, model_g(x2, result['par'][row, col,:]), linewidth=1, color=\"green\", label=\"minuit\")\n", - " ax[row, col].set_title(f\"Gaussian Fit to data in pixel [{row}, {col}]\")\n", - " ax[row, col].legend()" - ] - }, - { - "cell_type": "markdown", - "id": "fcf52481-0278-4f95-8676-829a6d61eff8", - "metadata": {}, - "source": [ - "## Fit data with different backends" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "1f6bc651-80c1-41dd-8a05-7f15aba006aa", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generating synthetic data: 100x100 pixels, 100 scan points, noise_frac=0.05\n", - "\n", - "model.max_calls = 100\n", - "model.tolerance = 0.5\n", - "model.compute_errors = False\n" - ] - } - ], - "source": [ - "# ===============\n", - "# DATA GENERATION\n", - "# ===============\n", - "print(f\"Generating synthetic data: {ROWS}x{COLS} pixels, \"\n", - " f\"{N_SCAN} scan points, noise_frac={NOISE_FRAC}\\n\")\n", - "\n", - "x, y, yerr, true_A, true_mu, true_sig = generate_3d_data(\n", - " ROWS, COLS, N_SCAN, NOISE_FRAC, SEED\n", - ")\n", - "\n", - "model = Gaussian()\n", - "print(f\"model.max_calls = {model.max_calls}\")\n", - "print(f\"model.tolerance = {model.tolerance}\")\n", - "print(\"model.compute_errors =\", model.compute_errors)\n", - "METHOD_DEFS = [\n", - " (\"lmfit (LM)\",\n", - " lambda nt: lambda: fit_gaus(x, y, n_threads=nt),\n", - " \"#2196F3\", {\"linewidth\": 3.0, \"linestyle\": \"-\"}),\n", - "\n", - " (\"Minuit2 (obj API)\",\n", - " lambda nt: lambda: model.fit(x, y, n_threads=nt),\n", - " \"#FF9800\", {\"linewidth\": 2.5, \"linestyle\": \":\"}),\n", - "]\n", - "\n", - "colors = {label: c for label, _, c, _ in METHOD_DEFS}\n", - "styles = {label: s for label, _, _, s in METHOD_DEFS}" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "5a417145-ce42-4c3a-a7bf-05ff97ba0450", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Method time (ms) med|dA| med|dMu| med|dSig|\n", - "--------------------------------------------------------------------------------\n", - "[lmfit (LM) ] 90.30 ms 6.272 0.0940 0.0949\n", - "[Minuit2 (obj API) ] 59.60 ms 6.272 0.0940 0.0949 chi2/ndf=880.9946\n" - ] - } - ], - "source": - [ - "# ====================================\n", - "# SINGLE-CALL BENCHMARK (at N_THREADS)\n", - "# ====================================\n", - "def extract_result(label, res):\n", - " \"\"\"Normalize return values across fitters into a common dict.\"\"\"\n", - " if isinstance(res, dict):\n", - " out = {\"par\": res[\"par\"]}\n", - " if \"par_err\" in res:\n", - " out[\"par_err\"] = res[\"par_err\"]\n", - " if \"chi2\" in res:\n", - " out[\"chi2\"] = res[\"chi2\"]\n", - " return out\n", - " # fit_gaus without y_err returns a raw array\n", - " return {\"par\": res}\n", - " \n", - "methods = {}\n", - "for label, factory, _, _ in METHOD_DEFS:\n", - " time.sleep(COOLDOWN)\n", - " res, times = bench(factory(N_THREADS))\n", - " entry = extract_result(label, res)\n", - " entry[\"times\"] = times\n", - " methods[label] = entry\n", - "\n", - "# ---- Print summary ----\n", - "ndf = N_SCAN - 3\n", - "print(f\"{'Method':24s} {'time (ms)':>10s} {'med|dA|':>10s} {'med|dMu|':>10s} {'med|dSig|':>10s}\")\n", - "print(\"-\" * 80)\n", - "for name, m in methods.items():\n", - " par = m[\"par\"]\n", - " med_t = np.median(m[\"times\"]) * 1e3\n", - " dA = np.median(np.abs(par[:,:,0] - true_A))\n", - " dMu = np.median(np.abs(par[:,:,1] - true_mu))\n", - " dSig = np.median(np.abs(par[:,:,2] - true_sig))\n", - "\n", - " chi2_str = \"\"\n", - " if \"chi2\" in m:\n", - " chi2_str = f\" chi2/ndf={np.median(m['chi2'] / ndf):.4f}\"\n", - "\n", - " print(f\"[{name:22s}] {med_t:8.2f} ms \"\n", - " f\"{dA:10.3f} {dMu:10.4f} {dSig:10.4f}{chi2_str}\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55ecc77c-0823-408d-a811-8e4f4900f332", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "# ===============\n", - "# THREAD SCALING\n", - "# ===============\n", - "thread_counts = [1, 2, 4, 8, 16]\n", - "\n", - "thread_times = {label: [] for label, _, _, _ in METHOD_DEFS}\n", - "ttimes_stddev = {label: [] for label, _, _, _ in METHOD_DEFS}\n", - "\n", - "for nt in thread_counts:\n", - " # shuffle method order per thread count to decorrelate thermal bias\n", - " run_order = list(METHOD_DEFS)\n", - " random.shuffle(run_order)\n", - "\n", - " for label, factory, _, _ in run_order:\n", - " time.sleep(COOLDOWN)\n", - " _, times = bench(factory(nt))\n", - "\n", - " med = np.median(times) * 1e3\n", - " std = np.std(times) * 1e3\n", - " thread_times[label].append(med)\n", - " ttimes_stddev[label].append(std)\n", - "\n", - " per_px = med / (ROWS * COLS) * 1e3\n", - " per_px_std = std / (ROWS * COLS) * 1e3\n", - " print(f\" {label:22s} n_threads={nt:2d} \"\n", - " f\"{med:8.2f} ± {std:6.2f} ms \"\n", - " f\"({per_px:.4f} ± {per_px_std:.4f} μs/pixel)\")\n", - " print(\"\\n\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5f3152d-be84-420e-8045-9c42ac5c24cb", - "metadata": {}, - "outputs": [], - "source": [ - "# =============================\n", - "# FIGURE 1: Residual histograms\n", - "# =============================\n", - "param_names = [\"A\", \"μ\", \"σ\"]\n", - "param_truths = [true_A, true_mu, true_sig]\n", - "\n", - "fig1, axes1 = plt.subplots(1, 3, figsize=(15, 5))\n", - "fig1.suptitle(f\"Parameter Residuals — {ROWS}×{COLS} pixels, {N_SCAN} scan points\",\n", - " fontsize=14, fontweight=\"bold\")\n", - "\n", - "for col, (pname, truth) in enumerate(zip(param_names, param_truths)):\n", - " ax = axes1[col]\n", - "\n", - " # collect residuals across all methods for shared bin edges\n", - " res_by_method = {}\n", - " all_res = []\n", - " for mname, m in methods.items():\n", - " residual = (m[\"par\"][:, :, col] - truth).ravel()\n", - " res_by_method[mname] = residual\n", - " all_res.append(residual)\n", - " all_res = np.concatenate(all_res)\n", - "\n", - " lo, hi = np.percentile(all_res, [0.5, 99.5])\n", - " edges = np.linspace(lo, hi, 101)\n", - "\n", - " for mname, r in res_by_method.items():\n", - " ax.hist(r, bins=edges, histtype=\"step\", label=mname,\n", - " color=colors[mname],\n", - " linewidth=styles[mname][\"linewidth\"],\n", - " linestyle=styles[mname][\"linestyle\"])\n", - "\n", - " ax.axvline(0, color=\"k\", linestyle=\"--\", linewidth=1, alpha=0.7)\n", - " ax.set_xlabel(f\"Fitted {pname} − True {pname}\")\n", - " ax.set_ylabel(\"Pixel count\")\n", - " ax.set_title(f\"Δ{pname}\")\n", - " ax.legend(fontsize=8)\n", - " ax.grid(alpha=0.3)\n", - "\n", - "fig1.tight_layout()\n", - "# fig1.savefig(\"fig1_residual_histograms.png\", dpi=150, bbox_inches=\"tight\")\n", - "# print(\"\\nSaved fig1_residual_histograms.png\")\n", - "\n", - "# ====================================================\n", - "# FIGURE 2: Performance — bar chart + thread scaling\n", - "# ====================================================\n", - "fig2 = plt.figure(figsize=(14, 5))\n", - "gs = GridSpec(1, 2, figure=fig2, width_ratios=[1, 1.3])\n", - "\n", - "# -- Left: bar chart at N_THREADS --\n", - "ax2a = fig2.add_subplot(gs[0])\n", - "names = list(methods.keys())\n", - "medians = [np.median(methods[n][\"times\"]) * 1e3 for n in names]\n", - "bars = ax2a.barh(names, medians,\n", - " color=[colors[n] for n in names],\n", - " edgecolor=\"white\", height=0.5)\n", - "ax2a.set_xlabel(\"Median wall time (ms)\")\n", - "ax2a.set_title(f\"Single call — {ROWS}×{COLS} px, {N_THREADS} threads\")\n", - "for bar, val in zip(bars, medians):\n", - " ax2a.text(bar.get_width() + max(medians) * 0.02,\n", - " bar.get_y() + bar.get_height() / 2,\n", - " f\"{val:.1f} ms\", va=\"center\", fontsize=10)\n", - "ax2a.grid(axis=\"x\", alpha=0.3)\n", - "ax2a.set_xlim(0, max(medians) * 1.25)\n", - "\n", - "# -- Right: thread scaling with error bars --\n", - "ax2b = fig2.add_subplot(gs[1])\n", - "for label, _, _, _ in METHOD_DEFS:\n", - " tt = thread_times[label]\n", - " sd = ttimes_stddev[label]\n", - " speedup = [tt[0] / t for t in tt]\n", - " # propagate uncertainty: S = t0/t → δS/S = sqrt((δt0/t0)² + (δt/t)²)\n", - " speedup_err = [\n", - " s * np.sqrt((sd[0] / tt[0])**2 + (sd[i] / tt[i])**2)\n", - " for i, s in enumerate(speedup)\n", - " ]\n", - " ax2b.errorbar(thread_counts, speedup, yerr=speedup_err,\n", - " fmt=\"o-\", label=label, color=colors[label],\n", - " linewidth=2, markersize=7, capsize=4)\n", - "\n", - "ax2b.plot(thread_counts, thread_counts, \"k--\", alpha=0.4, label=\"Ideal linear\")\n", - "ax2b.set_xlabel(\"Number of threads\")\n", - "ax2b.set_ylabel(\"Speedup vs 1 thread\")\n", - "ax2b.set_title(\"Thread scaling\")\n", - "ax2b.set_xticks(thread_counts)\n", - "ax2b.legend(fontsize=9)\n", - "ax2b.grid(alpha=0.3)\n", - "\n", - "fig2.tight_layout()\n", - "# fig2.savefig(\"fig2_performance.png\", dpi=150, bbox_inches=\"tight\")\n", - "# print(\"Saved fig2_performance.png\")\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "92a8c724-e139-45d4-a354-1b8408637ead", - "metadata": {}, - "outputs": [], - "source": [] - } - ], + "outputs": [], + "source": [ + "# ===============\n", + "# THREAD SCALING\n", + "# ===============\n", + "thread_counts = [1, 2, 4, 8, 16]\n", + "\n", + "thread_times = {label: [] for label, _, _, _ in METHOD_DEFS}\n", + "ttimes_stddev = {label: [] for label, _, _, _ in METHOD_DEFS}\n", + "\n", + "for nt in thread_counts:\n", + " # shuffle method order per thread count to decorrelate thermal bias\n", + " run_order = list(METHOD_DEFS)\n", + " random.shuffle(run_order)\n", + "\n", + " for label, factory, _, _ in run_order:\n", + " time.sleep(COOLDOWN)\n", + " _, times = bench(factory(nt))\n", + "\n", + " med = np.median(times) * 1e3\n", + " std = np.std(times) * 1e3\n", + " thread_times[label].append(med)\n", + " ttimes_stddev[label].append(std)\n", + "\n", + " per_px = med / (ROWS * COLS) * 1e3\n", + " per_px_std = std / (ROWS * COLS) * 1e3\n", + " print(f\" {label:22s} n_threads={nt:2d} \"\n", + " f\"{med:8.2f} \u00b1 {std:6.2f} ms \"\n", + " f\"({per_px:.4f} \u00b1 {per_px_std:.4f} \u03bcs/pixel)\")\n", + " print(\"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5f3152d-be84-420e-8045-9c42ac5c24cb", + "metadata": {}, + "outputs": [], + "source": [ + "# =============================\n", + "# FIGURE 1: Residual histograms\n", + "# =============================\n", + "param_names = [\"A\", \"\u03bc\", \"\u03c3\"]\n", + "param_truths = [true_A, true_mu, true_sig]\n", + "\n", + "fig1, axes1 = plt.subplots(1, 3, figsize=(15, 5))\n", + "fig1.suptitle(f\"Parameter Residuals \u2014 {ROWS}\u00d7{COLS} pixels, {N_SCAN} scan points\",\n", + " fontsize=14, fontweight=\"bold\")\n", + "\n", + "for col, (pname, truth) in enumerate(zip(param_names, param_truths)):\n", + " ax = axes1[col]\n", + "\n", + " # collect residuals across all methods for shared bin edges\n", + " res_by_method = {}\n", + " all_res = []\n", + " for mname, m in methods.items():\n", + " residual = (m[\"par\"][:, :, col] - truth).ravel()\n", + " res_by_method[mname] = residual\n", + " all_res.append(residual)\n", + " all_res = np.concatenate(all_res)\n", + "\n", + " lo, hi = np.percentile(all_res, [0.5, 99.5])\n", + " edges = np.linspace(lo, hi, 101)\n", + "\n", + " for mname, r in res_by_method.items():\n", + " ax.hist(r, bins=edges, histtype=\"step\", label=mname,\n", + " color=colors[mname],\n", + " linewidth=styles[mname][\"linewidth\"],\n", + " linestyle=styles[mname][\"linestyle\"])\n", + "\n", + " ax.axvline(0, color=\"k\", linestyle=\"--\", linewidth=1, alpha=0.7)\n", + " ax.set_xlabel(f\"Fitted {pname} \u2212 True {pname}\")\n", + " ax.set_ylabel(\"Pixel count\")\n", + " ax.set_title(f\"\u0394{pname}\")\n", + " ax.legend(fontsize=8)\n", + " ax.grid(alpha=0.3)\n", + "\n", + "fig1.tight_layout()\n", + "# fig1.savefig(\"fig1_residual_histograms.png\", dpi=150, bbox_inches=\"tight\")\n", + "# print(\"\\nSaved fig1_residual_histograms.png\")\n", + "\n", + "# ====================================================\n", + "# FIGURE 2: Performance \u2014 bar chart + thread scaling\n", + "# ====================================================\n", + "fig2 = plt.figure(figsize=(14, 5))\n", + "gs = GridSpec(1, 2, figure=fig2, width_ratios=[1, 1.3])\n", + "\n", + "# -- Left: bar chart at N_THREADS --\n", + "ax2a = fig2.add_subplot(gs[0])\n", + "names = list(methods.keys())\n", + "medians = [np.median(methods[n][\"times\"]) * 1e3 for n in names]\n", + "bars = ax2a.barh(names, medians,\n", + " color=[colors[n] for n in names],\n", + " edgecolor=\"white\", height=0.5)\n", + "ax2a.set_xlabel(\"Median wall time (ms)\")\n", + "ax2a.set_title(f\"Single call \u2014 {ROWS}\u00d7{COLS} px, {N_THREADS} threads\")\n", + "for bar, val in zip(bars, medians):\n", + " ax2a.text(bar.get_width() + max(medians) * 0.02,\n", + " bar.get_y() + bar.get_height() / 2,\n", + " f\"{val:.1f} ms\", va=\"center\", fontsize=10)\n", + "ax2a.grid(axis=\"x\", alpha=0.3)\n", + "ax2a.set_xlim(0, max(medians) * 1.25)\n", + "\n", + "# -- Right: thread scaling with error bars --\n", + "ax2b = fig2.add_subplot(gs[1])\n", + "for label, _, _, _ in METHOD_DEFS:\n", + " tt = thread_times[label]\n", + " sd = ttimes_stddev[label]\n", + " speedup = [tt[0] / t for t in tt]\n", + " # propagate uncertainty: S = t0/t \u2192 \u03b4S/S = sqrt((\u03b4t0/t0)\u00b2 + (\u03b4t/t)\u00b2)\n", + " speedup_err = [\n", + " s * np.sqrt((sd[0] / tt[0])**2 + (sd[i] / tt[i])**2)\n", + " for i, s in enumerate(speedup)\n", + " ]\n", + " ax2b.errorbar(thread_counts, speedup, yerr=speedup_err,\n", + " fmt=\"o-\", label=label, color=colors[label],\n", + " linewidth=2, markersize=7, capsize=4)\n", + "\n", + "ax2b.plot(thread_counts, thread_counts, \"k--\", alpha=0.4, label=\"Ideal linear\")\n", + "ax2b.set_xlabel(\"Number of threads\")\n", + "ax2b.set_ylabel(\"Speedup vs 1 thread\")\n", + "ax2b.set_title(\"Thread scaling\")\n", + "ax2b.set_xticks(thread_counts)\n", + "ax2b.legend(fontsize=9)\n", + "ax2b.grid(alpha=0.3)\n", + "\n", + "fig2.tight_layout()\n", + "# fig2.savefig(\"fig2_performance.png\", dpi=150, bbox_inches=\"tight\")\n", + "# print(\"Saved fig2_performance.png\")\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92a8c724-e139-45d4-a354-1b8408637ead", + "metadata": {}, + "outputs": [], + "source": [] + } + ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", diff --git a/python/tests/test_Minuit2_scurve_1d.ipynb b/python/tests/test_Minuit2_scurve_1d.ipynb index 62c5f230..9f033002 100644 --- a/python/tests/test_Minuit2_scurve_1d.ipynb +++ b/python/tests/test_Minuit2_scurve_1d.ipynb @@ -2,26 +2,19 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "efef8e20-6571-4561-8f0b-6048b57907de", "metadata": {}, "outputs": [], "source": [ "import time\n", - "import random\n", "import numpy as np\n", "np.set_printoptions(suppress=True, precision=6)\n", "\n", "import matplotlib.pyplot as plt\n", "from scipy.special import erf\n", - "\n", - "import sys\n", - "sys.path.insert(0, '/home/ferjao_k/sw/aare/build')\n", - "\n", - "from aare import fit_scurve, fit_scurve2 # lmfit\n", - "from aare import RisingScurve, FallingScurve, fit # minuit2 (object based API)\n", - "\n", - "from pprint import pprint" + "from aare import RisingScurve, FallingScurve\n", + "from pprint import pprint\n" ] }, { @@ -34,7 +27,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "35772e2c-37c6-4986-a9c0-7dca4a034827", "metadata": {}, "outputs": [], @@ -58,7 +51,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "06c8fddb-56d9-4f84-8b21-4ffd8b7bd26f", "metadata": {}, "outputs": [], @@ -92,22 +85,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "9a0b3292-7c57-4483-aecc-7ef683ff0b62", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": - "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d71hN9oxMTGw2+1wu926cbfbjbVr14bprP4cEUFJSQnq6+vx4MEDpKam6o6npqbCarXq1mN0dBTNzc3Tfj02bdqEzs5OeDwe7ZGVlYVdu3bB4/EgLS1N2ewB69atC7rFS1dXF1JSUgCo/fkHgOHhYURG6i93UVFR2i0/VM8/3mSy2u12REdH6+b09vbixYsXSqxHoHi7u7vR2NiI+Ph43XHV8xsJu944XQ+w79n17PoAdv1f0vUheUu1aSpwy4/Lly/Lq1evpLS0VGbNmiXv3r0L96mF3P79+8ViscjDhw+lt7dXewwPD2tzqqqqxGKxSH19vXR2dkphYeG0veXBvxn/LqQi6mdvbW0Vk8kklZWV0t3dLVevXpWZM2dKTU2NNkflNSguLpbk5GTtlh/19fWSkJAgR48e1eaolH9oaEg6Ojqko6NDAMjp06elo6NDe6fNyWR1OBxis9mksbFR2tvbZePGjdPmlh+/y+/z+WTr1q1is9nE4/Horoffv3/XnmM65yc9dr1xu17EWH3PrmfXs+v/rq439EZbROTcuXOSkpIiMTExsmrVKu0WGKoBMOHjypUr2hy/3y/Hjx8Xq9UqZrNZ1q9fL52dneE76T/o5+I1QvZbt25JRkaGmM1mSU9Pl4sXL+qOq7wGX79+FafTKQsXLpTY2FhJS0uTiooK3cVWpfxNTU0Tfr8XFxeLyOSyfvv2TUpKSiQuLk5mzJghW7ZskZ6enjCk+e9+l9/r9f7yetjU1KQ9x3TOT8HY9Ve0OSpd6ybDaH3PrmfXs+v/nq6PEBEJze/GiYiIiIiIiMiw/6NNRERERERE9Cdwo01EREREREQUQtxoExEREREREYUQN9pEREREREREIcSNNhEREREREVEIcaNNREREREREFELcaBMRERERERGFEDfaRERERERERCHEjTYRERERERFRCHGjTURERERERBRC3GgTERERERERhRA32kREREREREQh9D9BSl3fJLcb/QAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig, ax = plt.subplots(1, 2, figsize=(12,4), sharex=True)\n", "\n", @@ -134,38 +115,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "37d2fda4-40b8-4ea2-8f2c-6c3f2384d158", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Paramter list of the Scurve:\n", - "['p0', 'p1', 'mu', 'sigma', 'A', 'C']\n", - "6 \n", - "\n", - "== Tuned fit settings ==\n", - "max_calls : 500\n", - "tolerance : 0.01\n", - "compute_erros: True\n", - "\n", - "\n", - "== Results ==\n", - "True rising params: [100. 0.25 60. 6. 120. 1. ]\n", - "lmfit rising result: [ 99.467831 0.287017 60.078405 5.715461 117.257939 0.967234]\n", - "minuit_grad (obj api ) rising result:\n", - "{'par': array([100. , 0.269137, 60.050193, 5.729932, 117.777635,\n", - " 0.984073]),\n", - " 'par_err': array([1. , 0.054406, 0.747753, 0.909864, 7.887499, 0.174058]),\n", - " 'chi2': array([7.351743])}\n" - ] - } - ], + "outputs": [], "source": [ - "res_r_lmfit = fit_scurve(x, y_rising)\n", - "\n", "model_r = RisingScurve()\n", "print(\"Paramter list of the Scurve:\")\n", "# print(model_r.GetParNames())\n", @@ -187,7 +141,6 @@ "res_r_munuit_obj = model_r.fit(x, y_rising, np.sqrt(y_rising))\n", "\n", "print(\"True rising params: \", p_true_rising)\n", - "print(\"lmfit rising result: \", res_r_lmfit)\n", "print(\"minuit_grad (obj api ) rising result:\")\n", "pprint(res_r_munuit_obj, sort_dicts=False)" ] @@ -202,26 +155,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "5ce22d2d-1adb-47d9-bf36-6cb535fa8b77", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True falling params: [100. 0.25 60. 6. 120. -1. ]\n", - "lmfit falling result: [101.73448 0.225786 60.251971 6.092915 118.476735 -0.998677]\n", - "minuit_grad (obj api ) falling result\n", - "{'par': array([101.741754, 0.225723, 60.251604, 6.092887, 118.473268,\n", - " -0.998565]),\n", - " 'chi2': array([1865.728797])}\n" - ] - } - ], + "outputs": [], "source": [ - "res_f_lmfit = fit_scurve2(x, y_falling)\n", - "\n", "model_f = FallingScurve()\n", "model_f.SetParameter(2, 20) # set start p2 = 20\n", "model_f.SetParameter(4, 90) # set start p4 = 90\n", @@ -230,36 +168,22 @@ "res_f_munuit_obj = model_f.fit(x, y_falling)\n", "\n", "print(\"True falling params: \", p_true_falling)\n", - "print(\"lmfit falling result: \", res_f_lmfit)\n", "print(\"minuit_grad (obj api ) falling result\")\n", "pprint(res_f_munuit_obj, sort_dicts=False)" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "fe72503a-fc77-4b17-9b17-b98055e2fc87", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": - "iVBORw0KGgoAAAANSUhEUgAAA9oAAAF0CAYAAADcnkHwAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAADV1UlEQVR4nOzdeZxN5R/A8c+562x39hVjLMmuiEIKJSJCREkIqSwltIhsJSU/8lO0iCGJUpKSfUk/KaFkJ8Y6YzD7cuduz++Pa64ZZqzDDL7v1+u+uOeee85z7tyZc77neZ7vV1NKKYQQQgghhBBCCFEkdMXdACGEEEIIIYQQ4mYigbYQQgghhBBCCFGEJNAWQgghhBBCCCGKkATaQgghhBBCCCFEEZJAWwghhBBCCCGEKEISaAshhBBCCCGEEEVIAm0hhBBCCCGEEKIISaAthBBCCCGEEEIUIQm0hRBCCCGEEEKIIiSBtrhpxcbGomma52EwGIiKiuKJJ55g3759563fpEkTmjRpcln7WLt2LZqmsXbt2qJp9GXatWsXTz/9NBUqVMDLy4vQ0FDq1KlD//79SUtLK5Y2Fbfhw4dTtmxZDAYDgYGBwPk/26ysLEaNGlVsPzchhBBX79zzfN7HkCFDLnk7cXFxaJpGbGzseduOi4vzLOvRowflypUrugO4DEop5s2bx3333Ud4eDheXl6UKVOGFi1aMH369GJpU3GLi4vjkUceITg4GE3TGDhwYIE/yw0bNjBq1ChSUlKKra3i1mQo7gYIca3NnDmTKlWqYLVa+d///sfYsWNZs2YNu3fvJigoyLPe1KlTL3vbderU4bfffqNatWpF2eRLsnXrVu69916qVq3KiBEjKFeuHKdOneLvv/9m3rx5DBkyBH9//+veruK0aNEixo4dy7Bhw2jZsiVmsxk4/2eblZXF6NGjAS775ooQQoiSJfc8n1epUqWKfD9vvvkmL730UpFv91IMHTqU9957j2effZZXXnkFi8XCoUOHWL16NYsWLaJ3797F0q7i9PLLL/P7778zY8YMIiMjiYqKIjIykt9++42KFSt61tuwYQOjR4+mR48enhvwQlwPEmiLm16NGjWoW7cu4A6qnE4nI0eO5Pvvv+eZZ57xrHclwbK/vz/169cvsrZejg8++ACdTsfatWuxWCye5R07duStt95CKVUs7cpLKYXVasXb2/u67G/79u0AvPjii4SHh3uWF8eNECGEENdH3vP8tZQ3eLuesrOz+eCDD+jWrRuffvppvtd69OiBy+UqlnadKzs7Gy8vLzRNuy772759O3fffTft2rXLt7y4rsuEOJcMHRe3nNyT8YkTJ/ItL2jo+LRp07jjjjvw8/PDYrFQpUoV3njjDc/rBQ0d79GjB35+fuzfv59WrVrh5+dHdHQ0gwcPJicnJ9/2jx49SseOHbFYLAQGBvLUU0+xadOm84Y9FeT06dP4+/vj5+dX4OuXeqL7/fffadOmDSEhIXh5eVGxYkUGDhyY73gKGio3atSo8/ahaRr9+/fn448/pmrVqpjNZqZPn054eDhPP/30edtISUnB29ubQYMGeZalpaUxZMgQypcvj8lkonTp0gwcOJDMzMwLHke5cuUYPnw4ABEREWiaxqhRo4D8P9u4uDjCwsIAGD16tGeYYY8ePQA4efIkffr0ITo6GrPZTFhYGPfeey8rV6684P6FEEKULPv37+eZZ56hUqVK+Pj4ULp0adq0acM///xzRdsr6HyYe9774osvqFq1Kj4+Ptxxxx38+OOP571/0aJF1KpVC7PZTIUKFZg8eXKB59JzZWZmkpOTQ1RUVIGv63SXdjnvcrmYMmUKd955J97e3gQGBlK/fn1++OGHfMeTe+7Mq1y5cp7zJJwdWr98+XJ69uxJWFgYPj4+zJ8/H03TWLVq1XnbmDZtGpqmsW3bNs+yP//8k0cffZTg4GC8vLyoXbs2X3/99QWPI/faa//+/fz888+e83hcXNx5Q8dHjRrFK6+8AkD58uU96+Zet61evZomTZoQEhKCt7c3ZcuWpUOHDmRlZV3SZyrEhUiPtrjlHDx4EIDbb7/9guvNmzePvn37MmDAACZMmIBOp2P//v3s3Lnzovuw2+08+uij9OrVi8GDB/PLL7/w1ltvERAQwIgRIwD3ibNp06YkJSXx3nvvcdttt7F06VI6d+58ScfRoEEDfvrpJ5566imee+457r777svuOV62bBlt2rShatWqTJw4kbJlyxIXF8fy5csvazt5ff/996xfv54RI0YQGRlJeHg4Bw8e5OOPP+ajjz7KN5z9q6++wmq1ekYWZGVl0bhxY44ePcobb7xBrVq12LFjByNGjOCff/5h5cqVhV6QLFy4kI8++ojPP/+cpUuXEhAQQJkyZc5bLyoqiqVLl/Lwww/Tq1cvz3C73OD76aefZsuWLYwdO5bbb7+dlJQUtmzZwunTp6/4MxFCCHHtOJ1OHA5HvmUGg4Hjx48TEhLCu+++S1hYGElJScyaNYt77rmHrVu3Urly5SLZ/08//cSmTZsYM2YMfn5+jB8/nvbt27Nnzx4qVKgAwNKlS3nssce4//77mT9/Pg6HgwkTJpx3078goaGh3HbbbUydOpXw8HBatWpF5cqVL7vnuEePHsyZM4devXoxZswYTCYTW7ZsyTcP/XL17NmTRx55hC+++ILMzExat25NeHg4M2fO5MEHH8y3bmxsLHXq1KFWrVoArFmzhocffph77rmHjz/+mICAAObNm0fnzp3JysrKF9jnlTttr3379lSsWJEJEyYA7vN7fHx8vnV79+5NUlISU6ZM4bvvvvPcrKhWrZpnjvd9993HjBkzCAwM5NixYyxduhSbzYaPj88Vfy5CAKCEuEnNnDlTAWrjxo3Kbrer9PR0tXTpUhUZGanuv/9+Zbfb863fuHFj1bhxY8/z/v37q8DAwAvuY82aNQpQa9as8Szr3r27AtTXX3+db91WrVqpypUre55/9NFHClA///xzvvWee+45BaiZM2decN9Wq1W1a9dOAQpQer1e1a5dWw0bNkwlJiZe8L25KlasqCpWrKiys7MLXad79+4qJibmvOUjR45U5/4JAVRAQIBKSkrKt3zbtm0KUJ9++mm+5Xfffbe66667PM/HjRundDqd2rRpU771FixYoAC1ZMmSCx5PbptOnjyZb/m5P9uTJ08qQI0cOfK8bfj5+amBAwdecD9CCCGKX+55vqDHued4pZRyOBzKZrOpSpUqqZdfftmz/ODBg+edd3O3ffDgQc+ygs6HgIqIiFBpaWmeZQkJCUqn06lx48Z5ltWrV09FR0ernJwcz7L09HQVEhJy3rm0IH/88YcqW7as5/gsFotq3bq1mj17tnK5XBd9/y+//KIANWzYsAuuV9i5MSYmRnXv3t3zPPfz6dat23nrDho0SHl7e6uUlBTPsp07dypATZkyxbOsSpUqqnbt2uf9rFq3bq2ioqKU0+m8YFtjYmLUI488km9ZQT/L999//7yfpVJnry3++uuvC+5HiCslQ8fFTa9+/foYjUYsFgsPP/wwQUFBLFq0CIPhwgM67r77blJSUnjyySdZtGgRp06duuR9appGmzZt8i2rVasWhw4d8jxft26dp015Pfnkk5e0D7PZzMKFC9m5cyeTJk3iiSee4OTJk4wdO5aqVauyZ88ewD1P2uFw5HsA7N27l3///ZdevXrh5eV1ycd2MQ888EC+JHMANWvW5K677mLmzJmeZbt27eKPP/6gZ8+enmU//vgjNWrU4M4778zX3hYtWly37O533303sbGxvP3222zcuBG73X7N9ymEEOLKzZ49m02bNuV7GAwGHA4H77zzDtWqVcNkMmEwGDCZTOzbt49du3YV2f6bNm2aL1dKREQE4eHhnnN+ZmYmf/75J+3atcNkMnnW8/PzO+9aoTD16tVj//79LF26lDfeeIMGDRqwatUqunXrxqOPPurJy5Lbu5/7yJ2//fPPPwPQr1+/IjnmXB06dDhvWc+ePcnOzmb+/PmeZTNnzsRsNtOlSxfAPax/9+7dPPXUUwD52tyqVSvi4+M91zHXyp133onJZKJPnz7MmjWLAwcOXNP9iVuPBNrippd7Al69ejXPPfccu3btuqRg9umnn2bGjBkcOnSIDh06EB4ezj333MOKFSsu+l4fH5/zglez2YzVavU8P336NBEREee9t6BlF1K1alUGDhzInDlzOHz4MBMnTuT06dO8+eabAMyaNQuj0ZjvAe65yECBw6uvRmFzyHr27Mlvv/3G7t27gbMn3bw/ixMnTrBt27bz2muxWFBKXdbNjis1f/58unfvzvTp02nQoAHBwcF069aNhISEa75vIYQQl69q1arUrVs33wNg0KBBvPnmm7Rr147Fixfz+++/s2nTJu644w6ys7OLbP8hISHnLTObzZ59JCcno5S66nO+0WikRYsWjB07lmXLlnHkyBGaNGnCjz/+6AmkH3zwwXznz9yb2SdPnkSv1xMZGXklh1iogs751atXp169ep6b606nkzlz5tC2bVuCg4OBs3lyhgwZct45v2/fvgDX/JxfsWJFVq5cSXh4OP369aNixYpUrFiRyZMnX9P9iluHzNEWN73cEzC47zo7nU6mT5/OggUL6Nix4wXf+8wzz/DMM8+QmZnJL7/8wsiRI2ndujV79+4lJibmqtoVEhLCH3/8cd7yqwnoNE3j5ZdfZsyYMZ4M3G3atGHTpk3nrZs7J/no0aMX3KaXl9d5Sdyg8BNgYXPGnnzySQYNGkRsbCxjx47liy++oF27dvl6v0NDQ/H29mbGjBkFbiM0NPSCbS0KoaGhfPDBB3zwwQccPnyYH374gddff53ExESWLl16zfcvhBCiaMyZM4du3brxzjvv5Ft+6tSp61rmKSgoCE3TCpyPfTXn/JCQEAYOHMjatWvZvn07rVq14pNPPiE9Pd2zTu55MywsDKfTSUJCQqE3xMF9g6Cgc35heUoKO+c/88wz9O3bl127dnHgwAHi4+PzVXrJbdfQoUN57LHHCtxGUc2hv5D77ruP++67D6fTyZ9//smUKVMYOHAgERERPPHEE9d8/+LmJj3a4pYzfvx4goKCGDFixCWXxPD19aVly5YMGzYMm83Gjh07rrodjRs3Jj093XMXOte8efMu6f3nJvzIdfz4cdLS0jw1RENCQgq803/77bdTsWJFZsyYUeBJNVe5cuVITEzMd4Fgs9lYtmzZJbUzV1BQEO3atWP27Nn8+OOPJCQk5Bs2DtC6dWv+/fffAttct27dArOfX4nc+toX69EoW7Ys/fv356GHHmLLli1Fsm8hhBDXh6Zpnr/3uX766SeOHTt2Xdvh6+tL3bp1+f7777HZbJ7lGRkZBWYnP5fdbi800M0dAp97zq9cuXKB582WLVsC7szfF1KuXLl8WcHBnZk7IyPjou3M68knn8TLy4vY2FhiY2MpXbo0zZs397xeuXJlKlWqxN9//13g+b5u3br5huNfjUs55+v1eu655x4++ugjADnniyIhPdrilhMUFMTQoUN59dVXmTt3Ll27di1wvWeffRZvb2/uvfdeoqKiSEhIYNy4cQQEBFCvXr2rbkf37t2ZNGkSXbt25e233+a2227j559/9gSwFyvX0adPH1JSUujQoQM1atRAr9eze/duJk2ahE6n47XXXrtoGz766CPatGlD/fr1efnllylbtiyHDx9m2bJlfPnllwB07tyZESNG8MQTT/DKK69gtVr573//i9PpvOxj7tmzJ/Pnz6d///6UKVOGZs2a5Xt94MCBfPvtt9x///28/PLL1KpVC5fLxeHDh1m+fDmDBw/mnnvuuez9nstisRATE8OiRYt48MEHCQ4OJjQ0lKCgIJo2bUqXLl2oUqUKFouFTZs2ebLFCiGEuHG0bt2a2NhYqlSpQq1atdi8eTPvv/9+kU+ZuhRjxozhkUceoUWLFrz00ks4nU7ef/99/Pz8SEpK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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig, ax = plt.subplots(1, 2, figsize=(12,4))\n", "\n", "# rising\n", "ax[0].plot(x, y_true_rising, label=\"true\")\n", "ax[0].scatter(x, y_rising, s=12, label=\"data\")\n", - "ax[0].scatter(x, scurve(x, res_r_lmfit[:6]), s=20, marker=\"x\", label=\"lmfit\")\n", "ax[0].plot(x, model_r(x, res_r_munuit_obj['par']), linewidth=1, color=\"green\", label=\"minuit\")\n", "ax[0].set_title(\"Rising S-curve fits\")\n", "ax[0].legend()\n", @@ -267,7 +191,6 @@ "# falling\n", "ax[1].plot(x, y_true_falling, label=\"true\")\n", "ax[1].scatter(x, y_falling, s=12, label=\"data\")\n", - "ax[1].scatter(x, scurve2(x, res_f_lmfit[:6]), s=20, marker=\"x\", label=\"lmfit\")\n", "ax[1].plot(x, model_f(x, res_f_munuit_obj['par']), linewidth=1, color=\"green\", label=\"minuit\")\n", "ax[1].set_title(\"Falling S-curve fits\")\n", "ax[1].legend()\n", @@ -285,29 +208,13 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "3ba40e9a-0c64-4ef6-ba90-b1116f99b862", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rising abs error lmfit : [0.532169 0.037017 0.078405 0.284539 2.742061 0.032766]\n", - "Rising abs error minuit_grad: [0. 0.019137 0.050193 0.270068 2.222365 0.015927]\n", - "\n", - "\n", - "Falling abs error lmfit : [1.73448 0.024214 0.251971 0.092915 1.523265 0.001323]\n", - "Falling abs error minuit_grad: [1.741754 0.024277 0.251604 0.092887 1.526732 0.001435]\n" - ] - } - ], + "outputs": [], "source": [ - "print(\"Rising abs error lmfit : \", np.abs(res_r_lmfit[:6] - p_true_rising))\n", - "print(\"Rising abs error minuit_grad: \", np.abs(res_r_munuit_obj['par'] - p_true_rising))\n", - "print(\"\\n\")\n", - "print(\"Falling abs error lmfit : \", np.abs(res_f_lmfit[:6] - p_true_falling))\n", - "print(\"Falling abs error minuit_grad: \", np.abs(res_f_munuit_obj['par'] - p_true_falling))" + "print(\"Rising absolute parameter error:\", np.abs(res_r_munuit_obj['par'] - p_true_rising))\n", + "print(\"Falling absolute parameter error:\", np.abs(res_f_munuit_obj['par'] - p_true_falling))\n" ] }, { @@ -320,25 +227,12 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "dc0b52b6-9fc3-453d-b6eb-e325a2b8342e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rising lmfit : 0.334 ms\n", - "Rising minuit2 : 0.239 ms\n", - "\n", - "Falling lmfit : 0.294 ms\n", - "Falling minuit2 : 0.214 ms\n" - ] - } - ], + "outputs": [], "source": [ "def bench(fn, n_repeats=200):\n", - " # warmup\n", " for _ in range(3):\n", " fn()\n", "\n", @@ -348,20 +242,14 @@ " t1 = time.perf_counter()\n", " return res, (t1 - t0) / n_repeats\n", "\n", - "model_rising = RisingScurve()\n", + "model_rising = RisingScurve()\n", "model_falling = FallingScurve()\n", "\n", - "res_r_lmfit, t_r_lmfit = bench(lambda: fit_scurve(x, y_rising))\n", - "res_r_minuit, t_r_minuit_grad = bench(lambda: model_rising.fit(x, y_rising))\n", + "_, t_rising = bench(lambda: model_rising.fit(x, y_rising))\n", + "_, t_falling = bench(lambda: model_falling.fit(x, y_falling))\n", "\n", - "res_f_lmfit, t_f_lmfit = bench(lambda: fit_scurve2(x, y_falling))\n", - "res_f_minuit, t_f_minuit_grad = bench(lambda: model_falling.fit(x, y_falling))\n", - "\n", - "print(f\"Rising lmfit : {1e3*t_r_lmfit:.3f} ms\")\n", - "print(f\"Rising minuit2 : {1e3*t_r_minuit_grad:.3f} ms\")\n", - "print()\n", - "print(f\"Falling lmfit : {1e3*t_f_lmfit:.3f} ms\")\n", - "print(f\"Falling minuit2 : {1e3*t_f_minuit_grad:.3f} ms\")" + "print(f\"Rising Minuit2: {1e3*t_rising:.3f} ms\")\n", + "print(f\"Falling Minuit2: {1e3*t_falling:.3f} ms\")\n" ] }, { diff --git a/python/tests/test_Minuit2_scurve_3d.ipynb b/python/tests/test_Minuit2_scurve_3d.ipynb index c7a4409d..eada2267 100644 --- a/python/tests/test_Minuit2_scurve_3d.ipynb +++ b/python/tests/test_Minuit2_scurve_3d.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "efef8e20-6571-4561-8f0b-6048b57907de", "metadata": {}, "outputs": [], @@ -13,30 +13,15 @@ "from scipy.special import erf\n", "import matplotlib.pyplot as plt\n", "from matplotlib.gridspec import GridSpec\n", - "import sys\n", - "sys.path.insert(0, '/home/ferjao_k/aare/build')\n", - "\n", - "from aare import fit_scurve2\n", - "from aare import FallingScurve, fit" + "from aare import FallingScurve\n" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "2536939d-8f34-438e-850a-6f3bdf48f658", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'/home/ferjao_k/aare/build/aare/__init__.py'" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import aare\n", "aare.__file__" @@ -44,7 +29,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "35772e2c-37c6-4986-a9c0-7dca4a034827", "metadata": {}, "outputs": [], @@ -71,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "06c8fddb-56d9-4f84-8b21-4ffd8b7bd26f", "metadata": {}, "outputs": [], @@ -127,7 +112,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "dc0b52b6-9fc3-453d-b6eb-e325a2b8342e", "metadata": {}, "outputs": [], @@ -154,19 +139,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "13bcd5a6-8f95-4300-a002-04023d0f9bab", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Generating synthetic data: 100x100 pixels, 100 scan points, noise_frac=0.05\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "print(f\"Generating synthetic data: {ROWS}x{COLS} pixels, \"\n", " f\"{N_SCAN} scan points, noise_frac={NOISE_FRAC}\\n\")\n", @@ -186,17 +162,13 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "1f6bc651-80c1-41dd-8a05-7f15aba006aa", "metadata": {}, "outputs": [], "source": [ "model = FallingScurve()\n", "METHOD_DEFS = [\n", - " (\"lmfit (LM)\",\n", - " lambda nt: lambda: fit_scurve2(x, y, n_threads=nt),\n", - " \"#FF9800\", {\"linewidth\": 3.0, \"linestyle\": \"-\"}),\n", - "\n", " (\"Minuit2 (analytic)\",\n", " lambda nt: lambda: model.fit(x, y, n_threads=nt),\n", " \"#4CAF50\", {\"linewidth\": 4.0, \"linestyle\": \":\"}),\n", @@ -216,27 +188,13 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "5a417145-ce42-4c3a-a7bf-05ff97ba0450", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Method time (ms) med|dp0| med|dp1| med|dp2| med|dp3| med|dp4| med|dp5|\n", - "--------------------------------------------------------------------------------------------------------------\n", - "[lmfit (LM) ] 359.31 ms 18.6946 0.2249 0.2177 0.2663 11.9347 0.3635\n", - "[Minuit2 (analytic) ] 257.18 ms 17.1035 0.2147 0.2293 0.2668 11.9449 0.3518 chi2/ndf=596.8385\n" - ] - } - ], + "outputs": [], "source": [ "PARAM_NAMES = [\"p0\", \"p1\", \"p2\", \"p3\", \"p4\", \"p5\"]\n", "ndf = N_SCAN - len(PARAM_NAMES)\n", - "# NOTE: fit_scurve returns Ndf = n_scan - 2 in its dict, which looks wrong\n", - "# for a 6-parameter model. We use our own ndf = n_scan - 6 everywhere.\n", - "\n", "def extract_result(label, res):\n", " if isinstance(res, dict):\n", " out = {\"par\": res[\"par\"]}\n", @@ -245,8 +203,6 @@ " if \"chi2\" in res:\n", " out[\"chi2\"] = res[\"chi2\"]\n", " return out\n", - " # fallback: raw array, assume shape (rows, cols, 6)\n", - " return {\"par\": res}\n", "\n", "methods = {}\n", "for label, factory, _, _ in METHOD_DEFS:\n", @@ -288,35 +244,12 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "55ecc77c-0823-408d-a811-8e4f4900f332", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " lmfit (LM) n_threads= 1 1404.39 ± 11.48 ms (140.4392 ± 1.1478 µs/pixel)\n", - " Minuit2 (analytic) n_threads= 1 1022.42 ± 1.69 ms (102.2417 ± 0.1690 µs/pixel)\n", - "\n", - "\n", - " lmfit (LM) n_threads= 2 708.78 ± 6.14 ms (70.8775 ± 0.6142 µs/pixel)\n", - " Minuit2 (analytic) n_threads= 2 521.15 ± 0.96 ms (52.1151 ± 0.0962 µs/pixel)\n", - "\n", - "\n", - " Minuit2 (analytic) n_threads= 4 256.73 ± 2.40 ms (25.6728 ± 0.2399 µs/pixel)\n", - " lmfit (LM) n_threads= 4 352.83 ± 0.77 ms (35.2833 ± 0.0767 µs/pixel)\n", - "\n", - "\n", - " lmfit (LM) n_threads= 8 226.62 ± 1.48 ms (22.6616 ± 0.1479 µs/pixel)\n", - " Minuit2 (analytic) n_threads= 8 164.39 ± 0.51 ms (16.4386 ± 0.0509 µs/pixel)\n", - "\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "thread_counts = [1, 2, 4, 8]\n", " \n", @@ -339,8 +272,8 @@ " per_px = med / (ROWS * COLS) * 1e3\n", " per_px_std = std / (ROWS * COLS) * 1e3\n", " print(f\" {label:22s} n_threads={nt:2d} \"\n", - " f\"{med:8.2f} ± {std:6.2f} ms \"\n", - " f\"({per_px:.4f} ± {per_px_std:.4f} µs/pixel)\")\n", + " f\"{med:8.2f} \u00b1 {std:6.2f} ms \"\n", + " f\"({per_px:.4f} \u00b1 {per_px_std:.4f} \u00b5s/pixel)\")\n", " \n", " print(\"\\n\")" ] @@ -355,49 +288,17 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "d5f3152d-be84-420e-8045-9c42ac5c24cb", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": - "iVBORw0KGgoAAAANSUhEUgAABjUAAAN2CAYAAACxdwtsAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Qd0VNXWwPGdQioBQkjo3RCQoogIogKKFBXQh4oVbE95gigKYsGCigqIgB9gQyyAgKgPlfcUxQYqVREVxYCCCNIC0tOT+dY+vDvMnUxCyiRT8v+tdTOZO3fu3DYzZ84+Z58Qh8PhEAAAAAAAAAAAAD8X6usNAAAAAAAAAAAAKA6CGgAAAAAAAAAAICAQ1AAAAAAAAAAAAAGBoAYAAAAAAAAAAAgIBDUAAAAAAAAAAEBAIKgBAAAAAAAAAAACAkENAAAAAAAAAAAQEAhqAAAAAAAAAACAgEBQAwAAAAAAAAAABASCGgAAACjUjTfeKCEhIWbq3r17sY+ULms9T9fhC19++aVzG3T6448/fLIdQGFef/112zXqC66vr9sDBNr3DQAAqHwIagAA4KfcK2Rdp6pVq8qpp54qw4cPly1btvh6UwOCv1bcFXaew8LCpEaNGnLGGWfIfffdJ7t37/b1psLHli5dKnfddZd06dJFYmJiShSwWbhwofTs2VMSEhIkMjJSmjRpIrfccots3ry50Ofs2rXLvF5ycrJER0dLfHy8nHvuufLyyy9LXl5eOeyhyMyZM+Xmm2+Wtm3bSnh4uHP/dHuLkpmZKRMnTpQOHTpItWrVJDY2Vtq0aSMPPfSQHDp0qNDnffvtt3L11VdLvXr1zHGpXbu29O/fXz799NNy2Dt4cvDgQZkwYYJceeWV5jy7XtcnC4iW9hotzfsBgcn1mho7dqyvNwcAAHhJuLdWBAAAKs6xY8dk48aNZnr11Vfl/ffflwsvvJBTEETy8/NNZez3339vptmzZ8uaNWukYcOGFbodWuGrlcOqol8bdjNmzDDv9ZJwOBxy0003yRtvvGGbv23bNvPZMW/ePHn33Xfl4osvLlDZ37t3b/n7779tgYNvvvnGTP/+97/lvffek6ioKK+epnvvvbfIIIQn+/fvNxXU+j5x9fPPP5vpzTfflM8//1yaNm1qe/yVV16RIUOGmPeaZe/evbJ48WIzPfLII/LYY49JeerYsaM888wzUplpQO7+++8v8fNKc42W9v2AisH3DQAAKC6CGgAABIirrrpKzjzzTMnOzpaVK1fKf/7zHzM/PT1dBg0aZCqGtMWpt+Xm5kpOTo5pBYuS0Qo27XFRpUqVEp/nw4cPmwq5n376yczXnhpTpkyRyZMnV+hp6NOnj5nge9rSuEGDBub60FboWvFenECIawWuVhpqL68FCxbIL7/8Yq7Ra6+91vyvvRWsoOkVV1zhrCzWYJZWBOs1OGvWLPPaH3/8sTz88MMFKuR1ff/85z9lzJgx0qpVK4/bpM9r3769DBgwoMBj+n7R5+k+6rW/fv36k+6jBiasgIZ+Tul9rcjWXh8a8NDPxmuuuUZWrFghoaHHO6rrem+//XZnQKNz587St29fUxn+0UcfmXmPP/64nHXWWXLJJZdIeWndurWZKjs9X+3atTPnff78+XLgwIEily/tNVqa9wMqDt83AACg2BwAAMAvffHFFw79qram1157zfb4ddddZ3v8s88+M/OXLl3quOmmmxynn366o3bt2o6IiAhHdHS045RTTjHzf/zxxwKvdcMNNzjX061bN8fvv//uGDhwoCMhIcEREhJitkW9+OKLjiuuuMKRkpJiHgsPD3fExcWZ17rvvvscaWlpBdbduHFj57offfRRx4cffujo3Lmz2ab69es7xowZ48jOzjbLvvDCC45WrVo5IiMjHU2bNnU8+eSTjvz8/ALrzM3Ndbz++uuOHj16OGrVqmW2IzEx0dG/f3/H559/bltW98f1OLlPun2udu7cafalbdu2jqpVq5ptSU5Odtx9992OXbt2FdgW1/XrcVy3bp3joosuctSoUcPM27p1a6nP88GDB835sx7r3bt3oeu48sorHQ0aNDDLV6tWzXHuuec6XnnlFUdeXl6B5ZcvX+647LLLHPXq1XNUqVLFERsba45Dnz59zDnS1y3s2nD373//29GxY0dHVFSUIykpyXHzzTc79uzZU+C4FLa/7sensGOh6xw1apTj/PPPdzRq1MicG912fc2ePXs65syZU+BaKeq1jh496njssccc7du3N+uyrqHTTjvN8c9//tPx0UcfOfxNenq68389NkUdR+t9oufYWubaa691PrZ//37z3rUe02ve8vzzzzvn6/v/119/dT724IMPOh/Tc/7333/bXvOqq64yj9WpU8eRmppaYJse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feYNGTLE+b9+xrs62WDsnmhPE6t3o7r++utNz0jX7wXtbem6zXqdu/aO0B4x2ovI6m1S0u9q63WtHqT6/tLjZvVCKel+6feenms9//odqr2ZdPB4PV76nanXun7HAQAA/0JQAwCAAKQpHLTSVSsP7r77bmflkqZLuOyyy0yF98loag1PNG1SWFhYgfnvvfeejBw5stTrVa4pMtzTerg+5p6WRtNbKNdUTCejFYCaHkWPVXGUZN1Wqgp37sGY0tDUWBoc0Yqe//znP2aepuPSChutqLQq2rTixlMl7sm2WVNGaYBD08/oudKUJ65pT7TyUgNjVjqzwlhpRZSnylmtBCpOUMN1H4q6djTFyskCGidbh0UDgFpJrGlGNPin6YisdGdKU7KMHz/evLdOxjWtWWnVqlWrXIIaGrhxdeTIEdt9rYi1aAW4FcjTwKamhznZc6xlC6OVuK7nQ1O56GeUe0DTW/vovq3u22tta0mOi+vzSsL9PeFeKarv3+IENdQdd9xhrlfX61ffD+XxGeaefkqDMRoA0QDghx9+aHtcH9M0YoUFT8pTaa7R0r4fiqKfo5rqSNM56rX9/vvv29Zx1113mcYA7udHGxR4+p51pd/lVuq00n7muQZn3b9zre9Vb1/XrreenuP+vNIEV9yDzq77VtL90kC+nqOHH37YNADQFIyuaRj181mDsd27dy/xdgIAgPJDUAMAgACh4wVoy/qi6NgCVkBDK78133e/fv1Mq1CtdClOxWlMTIzH+a69ROrVq2fyUGvrdq1M0DE0hg0bdtJ1F9VCuzj59bVlrHuFclEVgyXJ2e+67kaNGsnw4cMLXda9xevJjl1J9OnTx3medXwI7UmhNNCgwQ1toarcK9x0nIKzzz77pNusx2T27Nny7LPPmsCJ5o7XSfOQa+XShg0b5P777zf5+4vi+vquOeIte/bs8fg89wpQ17z8Op5LYQEqK8CjNFe6jkug16GuTyulrDELiksDghp00cor7fHy22+/meOh+dN13BltRa2tkjXIF6i014krzTd/+umn2+67BuSsikF9nlVh7LqMp/vur+Hag+W+++4z/2urbD3OGkDSikEda6C4PWFORl9fK9aV9gjRyner8lorN10Da9a26jnVVt5Wz4fS7mNR9D3h+jnh/n4oToW50nFj3Htp6byhQ4fKggULCnyGWcEKbV1+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qJHjt99+K/BaL774oiMkJKTQ5z344IPFPjfu19oll1zicZ0dO3a0HRe9xj3t1+7dux2JiYnO+aNHjzbz9b3v+vnXqVMn23X6f//3f46wsLAij50eY1eFvedSUlKKXI9+Vrp+FupnbmGvURT3z/vifI6V9hot7fuhMLp8Udus1/nBgwedy69atcqRlJRU6PKun1//+te/PC7To0cPR/369T1+1xV2PXk6zq6fE0VxfU/qa3va5+joaMfXX39d4L1evXr1QvdVyw6zZs2yPaeo75vCrtOTfQ8/99xzHl9f36Nq/vz5J7327rnnnmIdKwAAUHEqflQ3AABQbrS3gqam0Bah2qtBW0hra92XX37ZtKovC22FqS0atUWsjh2hre27detm0g5deOGFUlE0ncqnn35qehbotmgaJd1vbY2sPRV037UFtpXP36ItZO+8807TarOwPObaUlNbVGsrbu3BoWORaAtNbU2uqUN0IFHt9eLNgY5PRvdPe0BYdPt0/ywTJ040qXy0Fa9up55zbYWqPSI0TYy2GHdNx6IDDOux6dq1q0lDpa1WdR/1f219q+uyenWcjPay0BQumo5MX1fPgfYc0ZRS2tq1MNojRnvZaJoVPXfaYlbPj+u4Ge50m7XVuua61+doa3Nt8a7bq9diSWgvm+nTp5tUXtoyWlty6zWhx01b9GsLY72uSzImi7/S1t7aMlpbomsrat1363zre0VblWsqH3fag0XHV9FBqvX8WNeVjsWgA0hr7wNPLZc1hZleb5oGSa8td7oN2tJfexZ5I/2U0utOe2w9/fTT5n2rnxG6bXpu9b1cWBoxTVOlz9PrXq8n63NE02RpaiPX3gMlNW3aNHON6Tbo8db3g/Yi0evK03FxpfW3em6sFEPao0jfL0p7Qmlrc6tXjr7XXD/btdW79m7Q97l+Zutr6fFITk424/U899xz5nO8OPR46rg++v62jo9+9utni47p4f5Z6Dp2gqYKLG+luUZL+34ojL7OTTfdZHrk6Ge1fmbo55HeHz16tDk/2nPO9bjoNj/yyCPmuOr2Wp9n2pNIew64XkOun5P6ftHeHZouzVefTfodqb0h9VrS70fdV/0e1l4m+h3pSo+vjjWiPWO0B4SeCz1Hes3osdbrVHtvlDf9ntL3iKZg9HTcdJ/0va7ve72OdL90OT2f2qNR329WzzAAAOA/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mjE1AKCyGviLSNTxAnypZe4TWXiqVISpU6fKnDlzpGPHjub+F1984Xzssccek1GjRklERITH57799tuSkpIijRs3PunrdOjQQWbNmiWXXXaZuf/ZZ5+ZHzpHjhwx9/XH0N13321e8/XXX/fS3gEAENxlCI8D8FZQOYIyBCqdEr5Hffn+9Lf36J133imPP/445XwA8HP01ACAykp/6OiA3mWZyhgU0QGnHnnkEenSpYs0atRI5s6dawagOuuss6R58+by5ZdfmuWuuOIK+f3332XQoEHmf+32aLWo+te//mVudR3aumvv3r0eW39dd911xdqmbt26yebNm2XXrl3m/quvvio333yzbZl+/frJhx9+6PwBBADwnWnfz5BRy+5zThPWPiObj/xmbq15ugz8rAxRxnIEZQignN+jlbSc37dvX8r5AConR75IRlrBKfNvkfzs47e6jJ8gqAEAlZW2vvL0hVWSSddRRhkZGbJixQp599135bbbbpMqVarImjVr5Omnn5YHH3zQLPPOO+9IvXr1zK1Orl588UVzq+tYv369JCUl2R7Pyckxj5199tnF2h5toXX99dfL7Nmz5eDBg7J27Vrp1auXbRndxjZt2sg333xTxr1HedIWhw899JBJKxAdHS3NmjUzLe/y8/NtLRM1HYFeX7pM9+7d5eeff7atJysrS4YPH25+YMfGxkr//v1lx44dnDzAT2w7/KekHtjknLYc+kPSc9PNrTVPl4GflSG8UI6gDAGU43uUcj6XF4DKJXO/yOykAlPo+50lcu8yc2uW8ROkn0LQ0YqpBx54wNwCKEIFdSc/mauuusrcnnHGGaZyYuDAgc7u4Vu2bCnz+vft22e6q8fExBT7OZqLVwMZVatWNdsTFhZWYJk6depQse3nJkyYYIJeb7zxhrRu3Vq+/fZbuemmm6R69epmoDM1ceJEmTx5skkx0KJFCxk3bpz07NlTUlNTJS4uziwzYsQIWbx4sSxYsEASEhJk5MiRphXfd9995/HaAOBbUTWjpOX1p5pb+E8ZQhPaVPHyZlCGALzzHi2P96fiPQoAga9edZGnLjt+608IaiDoaEvbVq1amVsA/i8q6nilk1U57HpfW9qXlQYzMjMzS/ScBg0amG7ymsNXe3l4ouvkc8a/rVy5Ui699FK55JJLnGkQ5s+fb4IbVi8NzeE8ZswYGTBg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SmmKla+++qrI5y1evNikXQKAYM0d/8iKx5zTCz+8JDvSd8jcjfNk+vrnzfT2pnd9vZk4ScXppc37malrg3MDogzRsWNHU36gDIFgRjnfOyjnA0AlCmpoWiktKOpg3q42bdokjRs3Nv936NDBpPhYunSp8/Fdu3aZSqEuXbqY+zoguOaXX7NmjXOZ1atXm3nWMgBQGTWs2kBS4lvYpqbVmxTrua7POZx9WBb99n6JXlsHbtZKB8urr74qZ555pvN+//795ZlnnpGyWr9+vURHR3uskGjQoIF89NFH5jvj66+/lueee06++eYbj+v566+/zLJXXXWV+JKnoMYrr7wi55133kkHzF67dq38/vvv5byFAFDxdGyG7/eud04IvLJEQlRCsRtH+EsZQhsM/PTTT5Qh4Jco51POB4Bg59Ogho6ZoYVFndTWrVvN/1ZPDG1ho61gZs6cKb/99ptMnz7dFB51ME+lg33fcsstMnLkSPnss8/MYOLXX3+9aS1z4YUXOnt29OnTR2699VZZtWqVmfT/vn37FjpIOABUBre2u0UmdZtgm+4988RYQkVxf97AlCtK9No333yzqYRQGmTWz2b9rLboIMxXXHF8nV9++aVpLamf/aeddpq0bt1avv32W/PYH3/8YXr2uX6vuKcf1HmPP/647Ny506xT16XfNeecc47pzWd9n7Rs2dJ8D3mi26rPtdat3zkaNLd6erz22mvOZfX757777jOBhubNm8u//vUv52Pz5s0zA1Hr83Q7PvzwwwKvpcEH/e7SVFYWfS0Nqui6NLChz7UqcLp37y7/+c9/nMdSe27o96AeKz3OFh3k2jrmAAD4U1miJOWIYC5D6Hc6ZQh4A+X80r9HtfeXNoDlPQoA/s2nQQ0tUOoXhU5Wag/9/5FHHjH3//GPf5jxM7RVqlbQaGvUd999V84990SX5ClTpshll11mKmu0cBkTE2MCH2FhYc5l3nzzTfP8Xr16maldu3YyZ84cH+wxKoIWRvT8B0o+e6Ay6tq1q2zZssX0gJg/f75ceeWVts9tdz///LOpxPjhhx9k+PDhMmbMmBK9nn6v1KtXT9555x1TGaGVEq5++eUXWblypVxwwQUen6+VIq69+8444wzTMlOD6cuXL5fHHnvM9BS06L7pc7QXyMcff2zWrXr37m0qX/R57733nglAuKbQUNpLsWbN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So0aJwY4mzhxokyePFmmT58ua9eulTp16kjPnj3lyJEjzmVGjBghixYtkgULFsjXX39tBknr27cvgxADfioqKkqSk5PNLQAAAIDgK1sT1EDQST+UIX8u/UOyD2f5elMAAD40YcIEadiwobz22mty1llnSZMmTaRHjx7SvHlzZy+NqVOnypgxY2TAgAHSpk0beeONNyQ9PV3mzZtnljl06JDMmjXLBEMuvPBCad++vcydO1d++ukn+fTTTzm/gB/at2+fvPnmm+YWAAAAgDfK1vvFnzCmBoLOLY2ukN+/flnG1ftFmiYdj9uFd9/g680CAFSwDz74QHr37i1XXnmlLFu2zHSbHTp0qNx6663m8a1bt8ru3bulV69ezudERkZKt27dZMWKFTJkyBCTqionJ8e2jKaq0gCILqPrd6fpqnSyHD582Nzm5+ebqTT0eRqEKe3z/Qn74p8C+bwczDokufm5zvuPf/mkvL9wkfxS51eJb1DTzGsU10juOP12CTSBfF7csS+V59wEw/UKAADE2dDv448/lv69zpUk8R8+DWosX75cnnnmGVNhsGvXLpPa4bLLLjOPaQXCQw89JB9++KFs2bJFqlevblpIjh8/3lQmWLTSYNSoUTJ//nzJyMgwLTCff/55adCggS39xJ133mkqN5Tmwp42bZot/QSCR7UqVSXCkS81HdlSyypPR1bz8VYBACqalh9eeOEFueeee+TBBx+UNWvWmPKABi4GDx5sAhqqdu3atufp/W3btpn/dZmIiAiJj48vsIz1fHdPP/20PPbYYwXmp6WlSWZmZqkriLQwqZVOoaGB3dGWffFPgXxent4wUbYc3eq8f2z/McnOz5ath7fJ3gP7nL8t9u7dK4EmkM+LO/al8pwb1xSOAAAAQRfUOHbsmJx22mly0003yeWXX257TFM/rFu3zgxEostoYEJzWmtA4ttvv3Uup/MWL15s8lzrIKAjR440ea41UBIWdnxMhWuvvdYM6LlkyRJz/7bbbpNBgwaZ5yHwLNu1Rg7EHE8dYonftUa6NbvEZ9sEAPDPSpozzzxTnnrqKXNfU0fpIOAa6NCghiUkJMT2PK3UcZ/nrqhlHnjgARNIce2poWmwEhMTpVq1aqXeF309XUcwVGyyL/4nkM+LjptTnGWSkvypbVnwnxd37EvlOTf+lnMbAAAEH58GNS666CIz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iIiInJNHBDVkMLqePXti/fr1muUy379/f4dtFxERETk/5iOIiIiIeQgiIiLn4THdT0mflBMmTECvXr3Qr18/vPnmm/jnn39w99132/x/SVPRp556qlKTUWoYTH/HYvoz/T0Zz3/31ZD5CHc/t7gvzonHxTnxuDgndzou7rg/zsbReQhr8BxgOvBc4PeBvwv8bXS1a4SXjBYOD7F48WLMmzcPp06dQufOnbFgwQIMGDDA0ZtFRERELoD5CCIiImIegoiIyPE8KqhBRERERERERERERESuyyPG1CAiIiIiIiIiIiIiItfHoAYREREREREREREREbkEBjWIiIiIiIiIiIiIiMglMKhBREREREREREREREQugUGNejh69CgmTZqE1q1bIygoCG3btsVTTz2F4uJizXr//PMPRo8ejUaNGiE6Ohr33XdfpXV+//13DBw4UH1Os2bN8PTTT4NjuNfN4sWL1TEJDAxEz5498fPPP9fjKJOYO3cuevfujdDQUMTGxmLs2LFITk7WJI6cr7Nnz0bTpk3VeTxo0CDs379fs05RURGmTp2qvgfyfRgzZgyOHz/ORK7lsfDy8sK0adOY9g3kxIkTuPnmmxEVFYXg4GB0794dO3fuZPqTw/MX5qz5HXaE5557Dv3791ffn/DwcKvec+utt6rfOtOpb9++cMV9cdbjIjIyMjBhwgQ0btxYTfI8MzPTJY5NbfN7GzZsUOvJ+m3atMF///tfOIva7MtPP/1UKf1l+vPPP+FoGzduVPc8cq7LNn366ac1vsdZj0tt98WZj4s1+XhXOjZkH19++SWSkpLUdUru1a655ppal2m4slatWlX6/j7yyCMelQbm9+1yzyPpsGfPHo9KBymjaNmypfrta9KkicobnTx50qPSwJZlna7Mmny/u6eBs5exMqhRD5JJ1el0eOONN9TN6YIFC1Rm79FHHzWuU1ZWhiuvvBJ5eXn45ZdfsGrVKnz88ceYMWOGcZ3s7GwMHTpUZZq3b9+OhQsXYv78+XjllVfqd3Q90AcffKAKex977DHs3r0bl156KUaOHKl+aKju5KZm8uTJ2LJlC9avX4/S0lIMGzZMndcG8+bNU+fsokWL1HkcHx+vzuucnBzjOnJs1qxZo74H8n3Izc3FqFGj1PeEaibp+uabb6Jr166a5Ux7+xb4XXzxxfDz88PXX3+NP/74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QrZb1jOf5P325gr5CPkfltLnxx9/tFk6EBGR+3O3PIT8f0vXR7mWNgRnz0PI9lhKHxYbkzPzkj+ODqwQkes7evQoWrdujd27d6N79+6O3hynJP1VrlmzRtU4ISIiogrMRzAfQUREVBfMQzAPQZ6JHaQREREREREREREREZFLYFCDiIiIiIiIiIiIiIhcArufIiIiIiIiIiIiIiIil8CWGkRERERERERERERE5BIY1CAiIiIiIiIiIiIiIpfAoAYREREREREREREREbkEBjWIiIiIiIiIiIiIiMglMKhBREREREREREREREQugUENIiIiIiIiIiIiIiJyCQxqEBERERERERERERGRS2BQg4iIiIiIiIiIiIiIXAKDGkRERERERERERERE5BIY1CAiIiIiIiIiIiIiIpfAoAYREREREREREREREbkEBjWIiIiIiIiIiIiIiMglMKhBREREREREREREREQugUENIiIiIiIiIiIiIiJyCQxqEFGD+7//+z94eXmhc+fO9fqcPXv24Morr0TLli0RFBSEyMhI9OvXDytXrrTZthIREZH75SHMvf322+pzQ0JCbPq5RERE5H75iJ9++kl9jqVpy5YtNtteIqqabzWvERHZxf/+9z91sd+/fz+2bt2KpKSkOn1OZmYmWrRogRtvvBHNmjVDXl4e3n33XUyYMAFHjx7F448/bvNtJyIiItfPQ5g6ceIEZs6ciaZNmyIrK8sm20lERETun494/vnncdlll2mW2briBRFZxpYaRNSgduzYgd9++w0PPvgg/P39sWTJkjp/1qBBg/Df//4XN998s8pIjBo1Cu+//77KmLz55ps23W4iIiJynzyEqbvvvhsDBgzA0KFDbfJ5RERE5Bn5iHbt2qFv376aia0+iRoGgxpE1KAk4+Dj44MHHnhABSFWrVqF/Px84+vSwkJqTsybNw/PPfec6loqMDAQvXr1wvfff2/V/4iOjoavLxuiERERuRN75CGky8oNGzZg8eLFDbgnRERE5I5lEUTUcBjUIKIGU1BQoFpSjBw5EvHx8bjtttuQk5ODDz/8sNK6ixYtwrp16/Dqq6+qAgdvb2/1vs2bN1daV6fTobS0FGlpaapQ4ptvvsHDDz/cQHtFRERErpiHSE1NxbRp0/DCCy+gefPmPIhERERuyl5lEZMnT1YVKsPCwjB8+HD88ssv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RxAREbnHr6m7kBmmHR46OnUXuiT29dldzHIGkfcLDQjRPNYEL4iIXOSWvqsyvtzrr7+Ov/zlLzh48KA73pKIiIh8BMsRREREFffZkW+QHNlBk5Z05BufDmo4w3IGkec8eO0DyC7I1qRFmIuBU596LE960C42CXFnf9SkNWg2wGP5IfJGbhuQMyAgAKdOnXLX2xEREXndBNdz587lRNfVhOUIIiLydfN3LkRK1v9KEkxFSKrdFo9e/E09TKwDzHvzFZZFKoHlDCLPaB/fzjHRdpJwi6H7gdC40t8oNBa+dO03vPldwC+jtInN/+2RvBAZJqjx5Zdfah6bzWacPn0aCxYswE033eTOvBEREXmNwMBAxMbGqluqPJYjiIiInJOARnLG75q08MDaJWWRACC2TgzLIixnEBmPBDTC4uEteO1H5P0qXDNz5513ah77+fkhPj4et956K9566y135g1FRUWYNm0aPv74Y5w5cwb169fHQw89hOeffx7+/v7WoMr06dPx3nvvqfEzu3TpgoULF6J169bW98nPz8ekSZOwYsUK5Obmonfv3li0aBEaNWrk1vwSEZFxye+Q/L6MGTMGDRo08HR2dKsmyxFERES6Yioq8+kzF4AFiz7A6KcmsyxSCpYziMgdeO1HZMCghslkQk1544038M477+Cjjz5SQYpt27bh4YcfRlRUFJ566im1zqxZszBnzhwsWbIELVq0wIwZM9C3b18kJycjIiJCrTN+/HisWbMGK1euVK1sJ06ciIEDB2L79u2qGyoREfmm5zuMRtFnbTRpgT33Ol330qVL2Lp1q7qlyqvJcgQREZHe7Qypa71/qQDYsn0XRrAsUiqWM4jIHXjtR+T9qjSGhvSSsLSyrA6//PIL7rjjDtx+++3qcZMmTVRvCwluWD5/3rx5mDp1KoYMGaLSJABSt25dfPLJJxg1ahQuXLiAxYsXY9myZejTp49aZ/ny5WjcuDHWr1+P/v37V0veiYjI+0WHRAKmPG2ipFGNqO5yBBERkZFNajcS+au7atJCbnnVY/nxNixnEBERGVelghpLly7Fm2++iUOHDqnH0kPimWeewYMPPujWzN18882qp8bvv/+uPmP37t346aefVCBDHD16VHUJ69evn/U1ISEh6NGjBzZt2qSCGtIbo7CwULOODBvSpk0btU5pQQ0ZskoWi6ysLGvL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loUyTrJYu3atuqDzxrknXN32nJwcdStjQNuSx5ZWh97M1e2UYyTHKjk5WXWFE3KxKeMYJyYmQg9c3db/+7//UxUOtoV5Gbf5008/RZcuXaAHFfnflTHM5WLF0irW/rusRzJkhWyPVPzddddd1nR5LK2cjURafEoBaPXq1fjhhx/QtGlTT2eJdFTGkN/m5557To1HLP83lt9mGdvectEt66xZs0bzOllHKmCDgoJ0Ub7w1HbWRBnDXduvh3KGUbbVlXKGUbbVlXKGt29rZflSWYT0w9evEfRWjqPy8VxLemf2tet5T89UTq5LSUkx79y50zx9+nRz7dq11X1ZsrOz1fN79+41x8fHmx944AHz6dOnrYvMdm+0bS0qKjK3adPG3Lt3b/OOHTvM69evNzdq1Mg8duxYs56lpaWZY2NjzUOGDDHv2rXLnJycbJ40aZI5KChIPTaSp556ytywYUPzt99+az548KB55MiR5oSEBPP58+fNRnb06FEZ30B9n43m5MmT5muuucZ86623mk+cOKE5D+ndypUr1f/h4sWLzfv37zePHz/eXKtWLfOxY8fMRvLEE0+Yo6KizD/88IPm+OXk5Hg6a6SDMkZmZqa5bt265vvvv9/822+/mf/5z3+aIyMjzbNnz7auc+TIEXN4eLj56aefVv9L8j8l/1uff/65bsoXetjOypYx3LX9eihnGGlbyytnGGVbXSlnGGVbfbksQsZj5GsEb1Fe+YZcx3Nt9ZLvpOX7KeWVOXPmqPvyHaaqe8LHrucZ1NCRESNGqH96++W///2vev6ll15y+nxiYqLZaNsq5KR3++23m8PCwsx16tRRFyt5eXlmvdu6dau5X79+apsiIiLMXbt2NX/99ddmoykoKDBPnDhRVTDIdvbp00dVmhmdkYMaH374odP/W6PEzxcuXKjOp8HBwebrr7/evGHDBrPRlHb85NiSsbmrjLFnzx7zLbfcYg4JCTHXq1fPPG3aNLPJZNKsI4XsDh06qP+lJk2amN9++22v2U5Xyxfevp1VKWO4a/v1UM4wyra6Us4wwra6Ws4wwrb6clmEjMfo1wjewJXyDbmO59rqI99JZ99V+Q5T1cHHruf95I+ne4sQERERERERERERERGVhwMZEhERERERERERERGRLjCoQUREREREREREREREusCgBhERERERERERERER6QKDGkREREREREREREREpAsMahARERERERERERERkS4wqEFERERERERERERERLrAoAYREREREREREREREekCgxpERERERERERERERKQLDGoQ6VTPnj0xfvz4Gvu8JUuWIDo6usY+j4iIiKoPyxFERETEMgQR6RWDGkRe7KGHHoKfn5/DcvjwYfzzn//EK6+8Yl23SZMmmDdvnlcHItLT03HbbbehQYMGCAkJQePGjTF27FhkZWVVy+f98MMPTvef7SL7yJsMHjwYV111FUJDQ1G/fn08+OCDOHXqlKezRUREOmS0coR9maJRo0ZqezIzM6vlM/RYjrDIz8/Hddddp/K4a9cuT2eHiIh0xohlCGfb884771TLZ+mxDCHH0T6Pzz77rKezRVSqwNKfIiJvIEGADz/8UJMWHx+PgIAA6I2/vz/uuOMOzJgxQ22DFIjGjBmD8+fP45NPPnH753Xr1g2nT5+2Pn7qqadUAMV2f0ZFRVnvFxcXqx9uyaen9OrVC88995wKaJw8eRKTJk3Cn//8Z2zatMljeSIiIv0yUjnC1siRI9GuXTv1W1ld9FiOsJg8ebJqRLJ7925PZ4WIiHTKiGUI2R7ZLme/4+6k1zLEyy+/jEcffdT6uHbt2h7ND1FZPF/iJqIySY+GevXqaRYpRNgOGyH3U1JS8PTTT1sj6tIy4OGHH8aFCxesadOmTVPrFxQUqIvdhg0bolatWujSpYta35a0GpAeA+Hh4bjrrrtUi8iyHDt2TH3GypUr1Q+49DRo3bq15n1jYmLwxBNPoFOnTkhMTETv3r0xevRo/Pjjj9XyLQgODtbst7CwMM3+/Oabb1Tw4KuvvsK1116rnpP96GxIjjvvvFO1VrFwZR/ak/3z9ttvY8CAASovTZs2xWeffaZZR45h165d1f6R/SgtIzZv3ozCwkI37x0iIvIFRipHWMhvqfTOkMB/ddJjOUL85z//wdq1azF79mw37g0iIvI1RixDSO8R+9/26qDXMkRERIQm3wxqkDdjUIPIAKT7pwzBIFF1aQ0gi/yYSxfQyMhIa5rl4l8KGD///LP60d+zZw/uuece1Vrh0KFD6vlff/0Vf/3rX1XAQYYskN4D0rvCFc888wwmTpyInTt3qjzIcEqlFUJkWCXJe48ePeApOTk5mDlzJj744APs27cPCQkJLr2uvH1YmhdeeAF33323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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": - "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HTt2HLSAsAUXt27dWqUGd6sIKtO+tfY1adJEsbGxgW4KAACoAAjaAgAAALthAb+lS5e6DMhGjRopPDy8zIN/3ize0NDQCh9YrGwqy761dqanp2vVqlVq3bo1GbcAAICgLQAAALCnLFsL3CYmJio6OrpaBxYro8q0b+vWratly5a5cg6USQAAABW7sBMAAABQAVT0eqio/Cp6UBkAAJQvzj4BAAAAAAAAoAIhaAsAAABUcaeccoruu+++MnmtQYMGacSIESXOGzlypBISEnzPjzvuOL300ktlsl4AAIDqhIHIAAAAgErOAqkWmL3hhhtUkfz444+BbgIAAEClRKYtAAAAKj0bbOquu+5SUlKSoqKi1KJFCz3wwANuEDFUzwHI8vPzA90MAACA/UbQFgAAAJXe448/rldeeUUvvPCC5s6dqyeeeEJPPvmknn/++YMWJN7do2iwcE/L2mN3yx6Izz//XK1atVKNGjV02WWXFXu9KVOm6LDDDlOtWrXccq+//rpv3tSpU5WcnOzm1a1bV2effbY2btx4wKUUvKUT3njjDSUmJqp27dq65ZZbCi3/66+/qk+fPm65jh076ptvvvHN++WXX9SrVy+3TQ0bNtTVV1+tnTt3+uY3b95cjz76qPr166fo6GjNmTNnv9oMAABQEVAeAQAAAJXeuHHjdPLJJ2vw4MG+AN6HH36oSZMmHZT1vfnmm7ud17RpUx177LG+5+++++5ug7AWfDzhhBN8zz/44ANlZWW5ny+//PL9atvChQt1zjnn6LPPPnM1ZS1Ieu2117qAp1m7dq2OOuoovfzyyzr99NNdkPvoo4922clHHHGEgoOD9dhjj6lv377KyMjQkCFDdNtttxUK7O6vrVu3aubMma6NS5cudW06/vjjXXB3xowZbl0WcLbnY8eOdcdzwoQJatu2rcugtjZ06dJFy5cvd/OeeeYZ3Xnnnb7Xf/vtt12g1wLRZNoCAIDKjExbAAAAVHqWGfrbb79pwYIF7vn06dOVmprqAoLVzUcffeSCryeeeKJCQ0N15ZVXqnXr1r75//3vf3XIIYdo6NChCgkJUadOnXTRRRe5gLHp2rWr259hYWGqX7++hg8f7rJky6psgWXDRkZGqn379howYIAmT57s5r366qu68MILdfjhh7vAsbXBAtqffPKJm5+SkqLu3bu7NluA+YorrijW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qAwKCgpcGYSff/7ZNy0iIkKnnXYaAdtyQtAWAAAAOAhGrRqtG0bepJGrRhWabs9tus0vS23bttVbb71VLNDYrl07BYqNJP3EE09o3bp1LsC7Zs0aXX311btdPi8vT6+99pouuugiVTVnnHGGfvvtN9+gLQAAVFSrV6/WZ599pgkTJmj58uVauXJloJtULRG0BQAAAMrY0i1L9czkESrwFCjfU1Bonj236TbflisrZ511ln788Udt2bLFPbfyAh6PR3379vUts2zZMpd56812vfDCC3XZZZe5342Li3OBXysZ4NW8eXN99dVXvuf2s00rOt9q3F155ZWuVICNIG0PC05agNaygCMjI1WrVi23TGpq6m63wS4OrZRAp06dfNN++eUX9erVy5eta6+5c+fOQm2wwHC/fv3cNhx66KGFLi5vu+02t4zNs9IQn376aYnr/vrrr9WiRQu3z7zGjRunmjVruv9L2r777rvPlVfwWrt2rc477zw1atTI1fg75JBDfG2NiYlR79699f333+/lSAIAEBg7duzQ77//ru+++86dK0RFRbnv8cTERA5JABC0BQAAAMrY14u+VdBelglSkL5e/F2ZrdOChMcee6yvu/6bb75ZqozVjz76SJdffrm7ODv//PNdIHdf2ajRr7zyiqt1Z+UX7GGlGIoaNWqUunTpstvXsW6YRTOD7YLx9ddfV0ZGhsaMGaM//vhDzzzzTKFl3n33XX3wwQdKT093wdG7777bN8/WZ8Fg27577rnHbePSpcWD5YMHD3YBVmujf6byOeeco/79++91+6wb6UknneRGz7as4g0bNriatzaytpcFjW0bAQCoSOw7zG5Mfvzxx1q0aJG7wWt1688880y1adMm0M2rthiIDAAAANgHN468SZuydl+X1TI1M7Iz9vo6+Z58/bbid01dN1UKshCuKRzqrRmZoGcHPVXqtlmQ9q677tKwYcP0+eefa9asWS7TdE8sWHn44Yf7ft8Cnhs3blTt2rVVliwL+I033thjpu2mTZsUHx9faFpKSorvZ8uEveKKK1y26p133umbfu2117p55txzz9Vjjz3mm2dBVwuk2gWoZRTbvLFjxyopKanQemyZCy64wAVqLasoKyvLDRz2v//9r1TbN3HiRM2ZM8cFfS3QbJKTkwstY9u2cOHCUr0eAADlae7cucrNzVW9evXc91edOnU4AAFG0BYAAADYBxaw3Zi1scz2WUb2pjJ7rSOOOEKXXnqpHnzwQZcd2qBBg73+jv8ylqVqtm7dWqZBW+tqaWUDvvjiC5etujtWiiAzc9egbf7B0Ntvv91lAFkmrNW9tTIOe9oGa7/Xc88952r9rlq1ygVuLUvWsmBLcvHFF7tSDC+88IK+/fZbNWnSxD0vDav517hxY1/AtiS2bbaNAAAEmt2cDA8Pdz1C7GE3Sa3Ekn3H2vclAo/yCAAAAMA+sOzX2pG1d/uoFVFrn/ZnrYiaqhVZS7Xdo/Br2br2hV10WbaoZZOWxWBeVrvV6tt52UBie1p3SaycgQ3CZeULLKi8J926ddP8+fMLTTv77LN12GGHacmSJS7oaSUH/OvO7oll9VoA+5133nFZvFYiwerl7u737UK1a9eubvAVy7i1IO7ets+rWbNmSktLK1RvtyjLxLVtBAAgUOw70L6PrDyS3RD1srrxVqKIgG3FQaYtAAAAsA9KU65gxOR/a+SqUcUGIfMXEhSiQYmH6vru17rsUW8X/gN14403usG47HGgevTo4WrknnbaaW4k6RdffHG3y9avX98FdS1o6c02tUHN7Hffe+89HXPMMXtdX58+fdz/VhPWaukZC9RavV7LoLWumy+//PIes1n92e/afq1bt66r12eBWCsZsSeXXHKJnn76aVfTz9q9p+3zZ4OMWdD3mmuucTV3LeA9fvx4Nz0iIsIFvy1r2GoNAwAQCFb73W5o2v/eXiJ2sxIVE5m2AAAAQBk7udWJ2lsuqEcendzyhDLf97Vq1dKRRx6psLCwA36thx56yGWnWtDTasNaFu/uWF3cfv36uRIBFmRdsWKF7r//fhc4tYFMLIjpfeyOBVitZq2VM/B69dVX9dRTT7nfu/LKK11d2tKygdlOPfVUNxhZo0aNXDB44MCBe/ydoUOHuotY+13b7j1tnz/LxLWSChacteCt1QK0+sIWLDZWY9gyhi0jFwCA8pSdna3Ro0fryy+/dAFbK4tg34cnnngiB6ICC/KUtm8R8Dc78a5Ro4au+uYarcxPq5L7xYYCSVRjrVSau6CqiqrDNlaX7awO21ia7WxZo4VGHPa0KjO7sF+/fr0r/r+3briVVXXYxuqwnd5zAat7VnTQqKq2TVbvbenSpW7QqsjIyH16zVGrRuuZySPc55cNOuafYWufY8N73qBDm6S4boplmWlbFY5F9+7dXZaqf9B0f+zvvm3ZsqWeffZZnXTSSSqrzwQri2BdUTt06FDiMgfyXkPVUtW/QwCUL6vpbrXl7XvGtG7d2t2ELG2vlaqsIECft6U9l6Y8AgAAAFCGMrIylJG1SY1jG7nA7J+rRmvC2om++T3r99AhTVLc/EWbF6tmRILiQ6tG8Lss2MXL4sWLA7Z+C6xaoHfw4MFl9pp2IThjxowyez0AAErLeqrk5OS4gTCTk5Nd7VpUDgRtAQAAgDL009Jf9OH8j3c73wK4/kHcs9oO1dBWZ3AMKoD27dsrIyPDDVwWEhIS6OYAALDPLEBr2bUtWrRwz62kzwknnED2fiVE0BYAAAAoQ8cmHa0+DXuXennLtEXFYAOdAQBQWdkgmlZeyOqr20CgVl/dNGjQINBNw34gaAsAAACUoVqRtdxjX+uuAgAA7I9NmzZpzJgxWr16tXtu9VLz8/+pp4/KiaAtAAAAAAAAUMnk5uZqypQpmjlzphtUy0r79OjRQ126dKHMTxVA0BYAAADYC7sQAg4my7gGAGBfvje++eYbbdy40T1v1qyZBgwYoLi4OHZiFUHQFgAAAJVe8+bNtXz58mLTr776ar344ov7/brh4eEKDg523Q3r1q3rngcFBaksecsjhIaGlvlrV3eVZd9aO9PT010bw8LCAt0cAEAlYN8ZNoDm9OnTNXDgQDVt2jTQTUIZI2gLAACASm/ixImFarfNmjVLRx11lIYMGXJAr2sB26SkJK1Zs8ZXJ+5gBOwsk9fWVZEDi5VRZdq31r4mTZrQnRUAUCK7CTlt2jR3E9myao0Fbdu0aeNuTqLq4agCAACg0rMLGH+PPfaYWrZsqUMPPfSAX9uyay17xS6WDsagHhZUtK6NtWvXdsFFVM99axm2VosQAICiVqxY4QYa27p1q2JjY303+eyGHwHbqougLQAAAKqUnJwcvffeexo+fPhusyuzs7PdwyszM9MX5Ntd/Vq7ODoYQTVbn11weUsxoPruW2onw/s+8GaJA6jeLEg7duxYXwmomJgY9e3b153f8BlReT9vS7s+grYAAACoUr766itt3rxZF1544W6XefTRR3X//fcXm251RbOyslTeJ+5btmxxFw2VIbBYmbBvURnxvgVgPXvmzJnjyj1ZTx87P7BSCJ07d3Y9M9avX89OqsSftxaMLw2CtgAAAKhS/vOf/+i4445To0aNdrvM7bff7jJx/TNtExMTXZmF+Ph4lfcFg2XM2LoJ2rJvAT4TAKSlpWnx4sWKiopy5zM20FjNmjXZMVXk8zYyMrJUyxG0BQAAQJVh3Qd//fVXffHFF3tcLiIiwj2KshP2QARO7YIhUOuu6ti3qIx43wLVj3fgTGM3kjt16qQGDRqoVatWgW5alRYUgHOw0q6Ls0IAQLViXaJ79+6tuLg41atXT6eccormz59faBnrUm1f3v6Pfv36+eYvW7as2Hzv49NPPz2gdQM4MG+99Zb7+xo8eDC7EgAAVIpg7YwZM/Thhx9qx44dvunJyckEbKu5gxq0HTRokG644YYyfc23335bCQkJOlhsdFk70bcL8vI0cuRId7Fv9dcOhAUaLAhQGlYDxVLALe0eAKqLUaNG6ZprrtH48eP1v//9z9WIOvroo7V9+/ZCyx177LFas2aN7/HDDz/45tmdb/959rDamDYwgHXJPtB1A9j/ix4L2g4bNoyRlAEAQIVn1xGff/65uz6wa4J58+YFukmorEFbb+bRlVdeWWze1Vdf7eb5D/hg3dIefPBBlaUzzzxTCxYs8D2/77771K1bt0LLZGRk6F//+pfatm2r6OhoNW3aVNddd50rLrw3lgV14oknqnnz5qrIvFle06ZNKzT9ueeec4Ht0rDg9Pnnn6977733ILUSACqen376yX1XdezYUV27dnUBnhUrVmjy5MmFlrNu09YdyfuoVauWb56NHu8/zx5ffvml+46KjY094HX7837Pvfnmm+77zF7/qquucoMTPPHEE27d9nn+8MMPF/s9W962w+pg2fcgUNVZWQT7m7r44osD3RQAAIDdsozaP/74Q99++602bdrkapweeuih6t69O3sN+1/T1rKLPvroIz377LOuILKxEXYtjdsuDv35X+CWFVund727s3r1avd46qmn1KFDB1fbzALNNu2zzz7b7e/t3LnTDVzhn01V2dSoUWOflr/ooovUp08fPfnkkxS1BlAteW/oFf3Osh4QFgy13h12AmVBUXteEgu62k20F198sUzWXZQNQvDjjz+6oK/9fMYZZ2jp0qVq06aNy94dO3asC1IdccQRroyDfdfZ97R9X1uAeO3atZo+ffo+tQ2ojCxz3Ub/BQAAqKjmzJmjCRMmKCcnxz1v3769i8uUVGsf1ds+l0fo0aOHC876D+5gP1swt+gdgaLlESx79ZFHHnEXllbPz17ntdde22OJALsItmnecgX+5RHsZ+uOahei3lqCNs2KNVt6uWXMtmzZUocffri72LY7GNYVdXfsgjg0NFT9+/f3TbNMpksuuURJSUkuWGzZu5bNWlJJAgsSN2zYULVr13bdX3Nzc33LvPfee+rVq5fbbsuKOuecc1x5gpJYSryNWlw0wGztt663W7dude0xts9tu21f+7fFv5vg448/7uqg2AeA7XP/bKzOnTv7MsQAoLqx4I6NHm/1ouy7w8tKHLz//vv6/fff9fTTT2vixInuuyQ7O7vE17EbfnayNWDAgANed0nss9wybe1GpH23HXbYYa4W7ogRI9z3kt2As//te9RYpqF9th955JHuc99OAi+77LJStw0AAADAwWGJGxawrVOnjk499VSlpKQQsEXZ1bS1i0Pr0ullF5Kl7YZmF78WvJw6daorqWBdPPe3Zod1Q/2///s/l0XkrSlo03b3R2GBUAvK7s6ff/7p2lb0QrlJkyb65JNP3N2Qe+65R3fccYd77s/S2i37yf5/5513XPDYv0yB/UFaqQgLMH/11VcuQ8q/lIQ/C8yeddZZhfaxseeWXWWBX7sr4+0GaNu9uxGSb7/9dhe0vfvuu137P/jgA9WvX7/QMnYxP3r06N3uFwtSZGZmFnoAQFVw7bXX+or++7PvEhvEyIKpFiS1m3pWmuf7778vsZeGfbbaDb6yWHdJ7KanffZ72ee4BXD9Rx21ad6bgUOGDHHtatGihQvW2o25Pd20BAAAAHBwWO90S77z6tmzpw455BAXsLVxhoAyDdpaHdTU1FSX/WqlB8aMGaPzzjuvVL97/PHHu2CtZX7eeuut7s6CNzNoX1nmq9X2s0Cst6ZgSaUTbHAxC5heccUVe3w92x6r++cvLCzMZfPaaN+W3Xruuee6YGvRoG3NmjX1wgsvqF27djrhhBPcxf5vv/3mm29Bbcvcsgto67r673//2wUBtm3bVmJbLr30Uv3888+upIPZsGGDvvvuO19w3PuHbVm9RWstetmHgmUFW81DG5DDso4to8te21/jxo33OPCa1fm1sgveh2VVA0BlZ7XPv/nmG3ezzW7O7Yn1omjWrJkWLlxYbJ71irCaVBdccMFBWbf3u8if9bAoaZrdaDT2OW2ZuFauwb4X7XvXTgz9e4AAAAAAOHisZ50lKX788ceF4l7h4eEudmTn70CZB20t0GpBScsotexP+9mmlUaXLl18P9sb1AKOuysTUBYsK9TaZxlJextwy7KSrPhzUa+88orLwLVAqQWJX3/9ddf11J9l+9rANP4X+P7bZZnFJ598srvot2wpbzmDoq/jn/1qr/nuu++65//9739dF1e76C6tuXPnuixZq3G4J3ZBbwGH3bFsXctU9j5WrlxZ6jYAQEU8ebIsV+uhYOUPvOVm9sRu/tlnn322l1Qa4aSTTirVXfL9Wff+ss92a5fdJLSTxHHjxmnmzJkHbX0AAAAA5Eu8+/rrr12PbovL2MNiTsBBHYjMyzI+7cLT7MvAK3vKDPJ28/QfQOJAsoIs0/TYY491gVbrGlp03UVZ4NlG7fNnGbU33nijK+tgtW4t4GqDdv3111+l3i6rUWsDY9jDatvahb0Fa4855hhf4emSWEasZe/edtttLjhuZSn25U7M3gZs88rIyNhjsMFq4VIQG0BVYTXHrZyBnUTZZ7oN0mWsJ4F9bloPiPvuu0+nn366C9JaTwQri+OtOeVv0aJF7kRsdwNY2k0z+x3v9+Xe1l1WrDyP1WTv27evoqOj3Y0/e327cQgAAADg4LAYj42HYeUpLbZlsSJLArSkPP/SZkBp7Pc7xoKh9ma0hwUfy4I3cGg1Wv0HItsTSyu3C9OSMmwtSGrzrQtqSRm0RdmgXvaH5c9qvdrAMta11OZbWQerXbsvLB3e7rI89thjrsC0pcGXJrvYSk5YcNeypGbPnu1KHHjZdpmStt2rdevW7iLdv0xDSWbNmlVsEDkAqKpefvll12vAejxYUNb7sG5LxnpNWEaq9Y5o06aN++y1/y1T1b+urLemu5WYse+bktj3hX3+l3bdZcUG7LReIQMHDnQ9XOx7wAaztJI6AAAAAMre5s2b3Xm9xW8sYGslKm2sDBsAnoAtyjXT1i5qrfu99+eyYAFRq8NnGU4PPfSQqx1oGa57G5zFBvWy4K7VBbQLagsk2wW0dfm3zFb/wbMsMLy79lrw2UoBWLat1aj1tslKFFh9WevGatlKdtdkX7q0WlkDC7I+//zzuvLKK12Q1Grs7o214bTTTtPNN9/stse/7mG9evVcQPann35y0y0obZla/mya1Q2+5ZZb3Prt4j09Pd19gHgHzLF9NHnyZD3yyCOl3h4AqMz8e3OUxD5b7TO/NOyzc0+fn0Xrhe9t3SWx70R7+PMf6NLLv07WKaec4h4AAAAAykd8fLy7lrCeyhZ/seQO4EAEH+gb0h5lxdLGbRRty0zt2rWrHn/8cRe83RPrvmpZv4cddpgLyNrvWxDSyhdYppQFXf2zmfZUj9Xufljauv8gYxZktcCp3R2xbqZW19CybveFtcsusD/99FNXW9cybp966qlS/a4FVy0I7R2AzMsGX7MM3FdffdUNnmYZYSW5++679X//93+655571L59e7cd/lm+1kXXgsqWAQwAAAAAAIC9s3KeNn6Rtwe0ZdNafOqMM84gYIsyEeTZn7SfKszqEt50000uG7YipK+///77uv7667V69WpfSYSyZAOe3XDDDTrnnHNK/TuWtWxZvVd9c41W5qepKgpSkBLVWCuVJo+q5p9IddjG6rKd1WEbS7OdLWu00IjD9tw7o6KzWuh2Y816U1SE76CDoTpsY3XYTu+5gJX7KMsb+NV1m6r6+yWQ2LeojHjfAhXfkiVLNHbsWNd72ZL/evToEegmoRJ93pb2vHO/yyNUVccff7wry5CWluZKNQSK/eFb2YdHH31UV1xxxUEJ2Nob0+4AnX322WX+2gAAAAAAAFWJ1a0dM2aMixkZC7hZwA84GAjalsAyWwPtiSee0MMPP6xDDjnE1dk9GOyDxerdAgAAAAAAoGR5eXmaMmWKZsyY4bIzbaykbt26uUdZjfMEFEXQtoIqaeAZAAAAAAAAlC/Lrp0/f7772cYFGjBgQJUpEYWKi6AtAAAAAAAAsBvdu3fX2rVr3QD1zZs3Zz+hXBC0BQAAAAAAACTl5+dr+vTpysrKchm1xrJqhw4dqqCgIPYRyg1BWwAAAAAAAFR7K1eudKUQMjMz3b5o166datWq5X4mYIvyRtAWAAAAAAAA1da2bds0btw4LV261D2Pjo5W//79fQFbIBAI2gIAAAAAAKDaKSgo0MyZMzV58mTl5eW5bNpOnTqpZ8+eCg8PD3TzUM0RtAUAAAAAAEC1k5ub6+rXWsC2QYMGSk5OJrsWFQZBWwAAAAAAAFQLNsBYZGSk+zkiIkIDBw50g4+1adMm0E0DCiFoCwAAAAAAgCpfCmHOnDmaOHGiBg0apKSkJDe9ZcuWgW4aUCKCtgAAAAAAAKiy1q1bp9TUVG3cuNE9X7RokS9oC1RUBG0BAAAAAABQJUsh/PXXX5o/f76vHEKfPn3Url27QDcN2CuCtgAAAAAAAKhSlixZotGjRys7O9s9t0CtBWy99WyBio6gLQAAAAAAAKoUy6q1gG3t2rWVnJys+vXrB7pJwD4haAsAAAAAAIBKzQK0GzZsUOPGjd1z+//YY49VkyZNFBwcHOjmAfuMoC0AAAAAAAAqJY/HowULFrjatfn5+Ro6dKhiYmLcvKZNmwa6ecB+I2gLAAAAAACASmfjxo1KTU3VunXr3POaNWu6wce8QVugMiNoCwAAAAAAgEojJydHkyZN0uzZs12mbWhoqHr16qVOnTpRCgFVBkFbAAAAAAAAVAp5eXn67LPPtG3bNve8RYsW6t+/P9m1qHII2gIAAAAAAKBSsKzali1batmyZRo4cKAbaAzYHwWeAmXnZ7v/g1XxBqsjaAsAAAAAAIAKm1k7ZcoUF6itXbu2m9azZ09XDiEkJCTQzUMltHTLUn296Fv9mZaq3IJchQWH6ZDGyTq51YlKqpGkioKgLQAAAAAAACqcpUuXaty4ca4Uwpo1a3TyySf7sm2BfbJ9jbRjjUalz9IzC75SkAqUryA3ywK3I1f+rj9WjtTwNqfo0LqdpOiGUkxDBRLvcgAAAAAAAFQYmZmZGjNmjFauXOmex8XFqVu3boFuFiqxjFkvaObct/VMjd4qsAlBuwK2Xi6A6ynQM/O/kCbcrs7tL1Stvg8rkAjaAgAAAAAAoEKUQpg2bZqmT5+u/Px8BQcHu2CtPciuxYH4Kaq5vom3wL9HCtpN/dqgIAV5CvRyfDedFNVc5yiwCNoCAAAAAAAg4BYtWuTq1xobYMwGGqtRo0agm4UqoGujAfp4yQ+7smz3ID8oWNlB4Tqm9WkKNIK2AAAAAAAACAiPx6Ogv7uqt23bVsuXL1fr1q3VokULjggOyNrt65SaNkaj08ZoyZYlpf69PEk5+dkKNIK2AAAAAAAAKFdW/mDGjBlavHixTjnlFFf+wIK3xxxzDEcC+y19x4a/A7WpWrh50X69RpgnX6OW/qKzOl+oQCJoCwAAAAAAgHKTlpam1NRUbdmyxVcWoV27dhwB7JeNOzM0ZvWujNp5GfNLXKZ17iarZqsloTVUsLuatpJCPAU6JGu1jm51YsCPBkFbAAAAAAAAHHTbt2/XuHHjtGTJrq7qUVFR6t+/v1q1asXexz7ZlLVZY1ePcxm1czbOlceFZAtrkbtZyVlpSslOU4OQMC2NqK8bQmtYTQ436Fgxnl2vcnJ8I9UqyAn4ESFoCwAAAAAAgINat3bmzJmaPHmycnNzXRmEjh07qlevXgoPD2fPo1S2ZGdq3JrxGr0qVbM2zFJBCYHaZrlbXJDWgrWNQ0Kl5qdILYdKa8cpaepDGp67Xc/E91SQx+MGHfPPsLVXG545WUlZadLcHlKv+wJ6ZAjaAgAAAAAA4KCxIO2aNWtcwLZ+/fpKTk5W7dq12ePYq605WzV+zV+u9MH09Bkq8BQUW6ZJ3lalZO0K1DYNCdoVqG0xVGpypBQSsWuh2t2lpFN16Mqf1HTak/o6qoX+jGys3KAQV8P2kKw0nbxziZK63SwlHitFNwz40SFoCwAAAAAAgDK1c+dOF6yNjIx0zwcMGKDmzZurTZs2bjqwO9tzt+uvNRNcoHba+mnK8+QXW6Zh3rZdgdrsNDUP8iio+clSiyekxKP/CdT6i2m461G3h5KaDdYNM0boX4s+VG5BnsKDQxXc6mypy5tS7a4V5sAQtAUAAECVGdTk1ltv1Y8//uguFO2i8D//+Y969uwZ6KYBAFCtSiHMmTNHEydOdEHaQYMGuelxcXFq27ZtoJuHCmpH7k5NWDtRqWmpmrx+qvIK8ootUy9/uy+jtmVQvoKanSS1fFRqcowUuuvmQKlYYPawtxR0yOvasmaF6jZsJoWEqKIhaAsAAIBKb9OmTRo4cKAOO+wwF7StV6+eFi9erISEhEA3DQCAamP9+vVKTU3Vhg0bfN/PeXl5Cg0l/ITisvKyNHHdZFejdvK6ycopyC22TJ38HUrOWq2UrFVqrVwFNTtRavnQrhIGoVEHtluDguUJjS55ULIKgL8aAAAAVHqPP/64EhMT9dZbb/mmWXYPAAA4+LKysjRhwgTNmzfPPbfBxfr06aP27dtTCgGFZOdna/K6KUpNG+sya+15UbXyd2pgtgVq09TWk6XgpidILe+TEo+TwqKrzR4laAsAAIBK75tvvtExxxyjIUOGaNSoUWrcuLGuvvpqXXbZZSUun52d7R5emZmZ7v+CggL3KE+2PutKWt7rrQ7Yt6iMeN+islm3bp1+/vlnF7g1Vp6ob9++ioqKct9v9kD1lpufqynp0zQmbaz+WjtBWfm73iv+EvKzNODvQG37gh0Kbnq8PC3ulBKPV0FYzD8LluH5UqA+b0u7PoK2AAAAqPSWLFmil19+WcOHD9cdd9zhsn2uu+46RURE6IILLii2/KOPPqr777+/2PT09HTfRWd5nrhv2bLFXTQEBweX67qrOvYtKiPet6hscnNztXXrVhektexaK1Fkz+2B6stq0s7dMk8TN07WtE3TtTN/Z7Fl4gqyNcBKH2SnqWPeduXVP1xZba/RhvpHyBP6d6B203YbmqxKfd6W9m+DoC0AAAAqPTvp7tWrlx555BH3vHv37po9e7YL5JYUtL399ttdgNc/09bKK9StW1fx8fHl3nYbRdvWTdCWfQvwmYCKLicnRwsWLFCnTp1808455xzVqFGD77FqLr8gXzM3zNLo1WM0fs14bcstHmyNKcjRgOw1bjCxzvmZCk08Vp4Wt0hNT1B4WKzCq8HnbWRk6QZNI2gLAACASq9hw4bq0KFDoWlWR+/zzz8vcXnLwLVHUXbCHojAqV0wBGrdVR37FpUR71tUVAsXLtT48eO1c+dORUdHq1WrVm567dq1A900BEi+J1+zN8zR6LRUjV09Xpk5u0pO+YsqyFW/7DWu9EG3vM0Ka3qs1OImqdkJUni8gqrZ521wKddF0BYAAACV3sCBAzV//vxC0ywLqFmzZgFrEwAAVUVGRobGjBmjNWvWuOcJCQmKifGrM4pqpcBToLkb57lA7ZjV47Q5e3OxZSIL8tTHArXZaeqRu1nhiUdJLW6Ump0oRdQISLsrG4K2AAAAqPRuvPFGDRgwwJVHGDp0qKtp+9prr7kHAADY/3q1kydP1syZM13dz9DQUPXo0UOdO3dWSEgIu7UaseM/f9N8jU4bqzFpY7QxK6PYMuGePPXOXucyanvlblREkyOlltdJzU6SIhIC0u7KjKAtAAAAKr3evXvryy+/dLVqH3jgASUlJWnEiBE699xzA900AAAqrf/9739atWqV+7l58+buBmlsbGygm4VyDNQu3LxIqWlj3CN954Ziy4R58tXLF6jdoKgmR0jdr5aanyxF1ORYHQCCtgAAAKgSTjjhBPcAAABlo1u3bm6wTgvWNm3alN1aTQK1S7csc6UPLFC7dse6YsuEegrUI2dXoLZP7gZFNxokdbtyV6A2slZA2l0VEbQFAAAAAACo5vLy8jR16lQ3UGeXLl3ctEaNGrmyQwyUWfUtz1yu0at2ZdSmbV9dbH6Ip0DdctYrOWu1+uWsU2yjQ6Wul0nNT5EiGYjuYCBoCwAAAAAAUI0tX77cDTS2bds2V7e2devWioqKcvMI2FZdK7euckHa0WljtHLrymLzgz0F6pKzQcnZaeqfvVbxDVOkLndJzU+VouoEpM3VCUFbAAAAAACAashKH4wdO1YrVqxwz61erZVC8AZsUfWs3rbGV/pgWebyYvODPB51yt3gSh8MyF6rGg0GSJ1vl5IsUFsvIG2urgjaAgAAoNzUrFlTQUFBpVo2I6P4qMQAAODA5efna/r06a4cgv1s2bRWEqFHjx4u0xZVy9rt63yDiS3esqTEZTrkeAO1a1SrQX+p4y1S0mlSdP1yby924S8RAAAA5WbEiBG+nzdu3KiHHnpIxxxzjPr37++mjRs3Tj///LPuvvtujgoAAAeJlUGYMmWKCgoKXN3a5ORkJSQksL+rkPQdGzRm9a7SBws2LSxxmbY5GUrJTtPArNWqU7+P1PGmvwO1Dcq9vSiOoC0AAADKzbBhw3w/n3766XrggQd07bXX+qZdd911euGFF/Trr7/qxhtv5MgAAFBGcnJyFB4e7n6uUaOG+vTpo5iYGLVs2ZJ9XEVs3JmhMavHuozauRnzSlymVe4ml1FrA4rVq9dT6nCDlHS6FNOw3NuLPSNoCwAAgICwjNrHH3+82HTLvL3tttsC0iYAAKoay6adOXOmK4UwePBg1a1b1023cgio/DZlbdbY1eNcndo5G+fKI0+xZVrkblayBWqz09Swbnep3b+kFhaobRyQNqN0CNoCAAAgIGrXrq0vv/xSN998c6HpX331lZsHAAAOzOrVqzVmzBht2rTJPZ8/f74vaIvKa0t2psatGa/Rq1I1a8MsFZQQqG2Wu0XJ2atdsLZJnc5S26ulFmdIsU0C0mbsO4K2AAAACIj7779fl1xyiUaOHOmraTt+/Hj99NNPeuONNzgqAADspx07drjv1EWLFrnnkZGR6tevn1q3bs0+raS25WxzgdrUtLGalj5dBZ6CYss0ztv6d+mDNDWr3UFqe7mUdIYU1zQgbcaBIWgLAACAgLjwwgvVvn17/fvf/9YXX3whj8ejDh06uIygvn37clQAANgP8+bNcwFbq2Fr7Lu1d+/eioiIYH9WMttzt+uvNRPcYGLT1k9Tnie/2DIN87a5IK0NKNa8VjsFdb50V0ZtXLOAtBllh6At9luj2MYKV9X80A/ySLXyaik8NFyeIFVJ1WEbq8t2VodtLM12Nomjmw9QGVlw9v333w90MwAAqDLsJqgFbK0MQnJyMuUQKpkduTs1Ye1EN5jY5HWTSwzU1svfoZSsVS5Y27JmawV1umhXoDY+KSBtxsFB0Bb77druVykhIaHKFmpfv3696tWrp+DgYFVF1WEbq8t2VodtLO12Wheh4KCquw+Aqmznzp3Kzc0tNC0+Pj5g7QEAoLLIysrS1q1bfcHZdu3auazapKQkBQVV4ayOKiQrL1uT1k1yGbWT1k5STkHhcyJTJ3+HkrN21ahtk5CkoI4XSC2GSPEtAtJmHHwEbQEAVQYBW6Dy1du75ZZb9Mknn2jjxo3F5ufnF88sAQAA/2TUWimECRMmKCwsTEOHDlVoaKgL1LZoQSCvosvJz9HkdVNcoHbCmgnKLthVzsJfrfydGvj3YGLt4psquMM5uwK1NVoFpM0oXwRtAQAAEBA333yz/vjjD7300ku64IIL9OKLLyotLU2vvvqqHnvsMY4KAAC7kZ6ertTUVPe/iY2NdTdD6aVSseXm52rq+mkanZbqatXuzM8qtkyNgmwNsBq1WWnqENdYIe3O3BWoTWgTkDYjcAjaAgAAICC+/fZbvfvuuxo0aJAuvvhipaSkqFWrVmrWrJmrc3vuuedyZAAA8JOdna2JEydqzpw57nl4eLh69erlBhuryqXSKrO8gjxNS5+u0avG6K8147U9b2exZeIKcjQga7VSslepU2xDhbQb+negtm1A2oyKgaAtAAAAAiIjI8PV2zOWGWTPjQ2actVVV3FUAAAoUv/9008/dTVsjd3o7Nevn6Kjo9lPFUx+Qb5mbJjpSh+MXz1OW3O3F1smpiBH/bPXuIzaLjF1Fdr270BtzfYBaTMqHoK2AAAACAirt7ds2TKXWWsZQlbbtk+fPi4Dt6oOdgoAwP6KiopSw4YNtXnzZg0cOFCNGjViZ1Yg+Z58zd4wR6lpYzQ2bay25G4ttkxUQa76Za9xNWq7R9dUWOu/Sx/U6hiQNqNiI2gLAACAgLjooos0ffp0HXroobr99ts1ePBgPf/888rLy9MzzzzDUQEAVGs5OTmaOnWqOnfu7MumPeSQQ9ygY5RCqBgKPAWamzFPqavGaExaqjblZBZbJrIgT30sUJu9Wj0j4xXeaqjUcqhUs6MUFBSQdqNyIGgLAACAgLjxxht9Px922GFuBOxJkyapZcuW6tq1K0cFAFBtLVq0SOPHj3eDi23fvl2HH364mx4RERHoplV7Ho9H8zctcKUPxqwarY3Zm4vtk3BPnnpnr3OlD3pGxiqy5ZBdgdpanQnUotQI2gIAACDgrD5f06ZN3QMAgOrKSh+kpqZq9erVvprvrVu3DnSzqj0L1C7avFij01KVumq00rN21eH3F+bJV8+/A7W9I6IV1eKMXYHa2l0J1GK/ELQFAABAQOTn5+uRRx7RK6+8onXr1mnBggWuzu3dd9+t5s2b65JLLuHIAACqBSsNNGXKFM2YMUMFBQUKCQlR9+7dXc8T+xmBCdQu3bLMF6hduzO92DKhngJ1z1mvlKxV6hseqWgL1FqN2jrdCdTigBG0BQAAQEA8/PDDeuedd/TEE0/osssu80232n3PPvssQVsAQLUxbdo09zDW62TAgAEuyxblb3nmco1eNUapaX8qbfu6YvNDPAXqmpPuMmr7hYUptsXpuzJq6/QgUIsyRdAWAAAAAfHuu+/qtdde0xFHHKErr7zSN71Lly6uvi0AANWFffelpaWpW7duatasWaCbU+2s3LpKqWljNHrVn1q5bVdpCn/BHo+65KQrOTtN/UOCFe8N1NbtRaAWBw1BWwAAAASEXZy2atWq2HTrFpqbmxuQNgEAUB6lEKZPn+5KAx1//PFuWnh4uE4++WR2fjlavW2NL1C7bOvKYvODPB51yt2g5Kw0DQgJUkKLU6UWQ6V6fQjUolwQtAUAAEBAdOzYUaNHjy6WUfTpp5+6On4AAFQ1K1as0NixY5WZmemer1y5UomJiYFuVrWxbvt6pa4eo9Er/9TizGUlLtM+Z6MrfTAwpEC1kk6RWjwp1e8rBQWXe3tRvRG0BQAAQEDce++9Ov/8813GrWXXfvHFF5o/f74rm/Ddd99xVAAAVca2bdtcsHbZsl2BwpiYGPXv35+AbTlI37FBY/4O1C7YsqTEZdrmZLjSB8lBeapjgdqWj0r1+xOoRUARtAUAAEBAnHjiifr444/1yCOPKCgoSPfcc4969Oihb7/9VkcddRRHBQBQ6dlNyRkzZmjKlCmuLEJwcLA6deqknj17KiwsLNDNq7IysjKUmjZWqav+1NxNC0tcplXupl0ZtUG5qp90ktTiYanBAAK1qDAI2gIAAKDc2YXrww8/rIsvvlijRo3iCAAAqqxFixa5772GDRsqOTlZNWvWDHSTqqTN2Zs1dvV4jV45SrMz5slTwjJJuVuUkrVKycpWw6QTpRYPSA0GSsEhAWgxsGcEbQEAAFDuQkND9eSTT2rYsGHsfQBAlbJjxw5FREQoJCTEZdampKS4GratW7cOdNOqnC3ZmRq/xgK1f2rmxjkqKCFU2zQv02XUJmunmjQbLLW8R2qQQqAWFR5BWwAAAATEkUceqZEjR+rCCy/kCAAAqkQphFmzZmny5Mnq2rWrK/lj6tev7x4oG9tytmn8mr80etWfmpY+s8RAbeO8rUrOSlOKZ4eaNTtOanmn1PAQKZgwGCoP3q0AAAAIiOOOO0633367u8C12n42KIu/k046iSMDAKgU1q5dq9TUVGVkZLjnq1ev9gVtceB25O7Q+DUT/g7UTleep6DYMg3ytisle5VSCrareeLRCmp1m9RoEIFaVFoEbQEAABAQV111lfv/mWeeKTbPBibLz88PQKsAACi9nTt36q+//tKCBQvc88jISPXp00dt27ZlNx6gnXk7NWHtJFf6YMr6qcr1FD8vqJe/Y1dGbcFWtUw8SkGtbpYaDpJCGOQNlR9BWwAAAASsGykAAJXVihUr9PvvvysnJ8c9b9eunQvYWuAW+ycrL1uT1k1yGbWT1k5WTgmB2tr5O/8O1GaqTZMjFNRquNTocAK1qHII2gIAAAAAAOyjhIQE1yukTp06Sk5OVr169diH+yEnP0eT101R6qrRmrB2grIK8ootUzM/SwOz05SSt0Xtmhym4FbXS42PkELC2eeosgjaAgAAIGB+++0391i/fn2xzNs333wzYO0CAKCorKwsrVy5Uq1bt3bP4+PjdeKJJ6pu3bqurA9KLzc/V1PXT9PoVaP115rx2lmQW2yZGgXZGmAZtfmb1aHxoQppea3U5CgCtag2CNoCAAAgIO6//3498MAD6tWrlxo2bMgFLwCgQvJ4PJo/f74mTJjgArc1atTwZdWSXVt6eQV5mp4+wwVqx68eq+35u8pK+IsryFH/rNVKyctQ58YpCulxlZR4tBQSUYZHFKgcCNoCAAAgIF555RW9/fbbOv/88zkCAIAKaePGjUpNTdW6devc85o1awa6SZVKfkG+Zm6Y5QK149JStTU/u9gyMQW56pe9K1DbteFAhfa4QmpytBRKbWBUbwRtAQAAEBA2cMuAAQPY+wCACvkdNWnSJM2ePdtl2oaFhalnz57q1KmTgoODA928Ci3fk685G+a6wcTGpqVqS97OYstEFeSqb/ZapeRuUPdG/RXW8h4p8RgpNCogbQYqIoK2AAAACIhLL71UH3zwge6++26OAACgQvnmm2+UkZHhfm7ZsqX69eunmJiYQDerwirwFGhuxjylrkrVmFWjtCl3R7FlIgvy1CdnrZJz0tWzYV+Fd7tDanocgVpgNwjaAgAAoNwMHz7c97MNPPbaa6/p119/VZcuXVwWk79nnnmGIwMACIjOnTtr+vTpGjhwoBo3bsxR2F2t300L/g7UjtSGnG3Flgn35Kl39jol56arV/1eimx1q5R4vBQWzT4F9oKgLQAAAMrN1KlTCz3v1q2b+3/WrFkcBQBAQOTm5mry5MmqX7++kpKS3LQ2bdqoVatWCgkJ4agUCdQu2rxYqWmpSl3xh9bnZBbbP2GefPW0QG1OuvrU766oLjdJTQdLYWQqA/uCoC0AAADKzR9//HFQXve+++7T/fffX2iaXXyvXbv2oKwPAFA1LFmyROPGjdP27dtd+YPExESFhoYqKCiIgK1foHZZ5jKNXmWB2t+1Jntzsf0Y6ilQt5z1SslZp351uyq6841SsxOksNjyPqRAlUHQFgAAAAFx8cUX67nnnlNcXFyh6Xbh/K9//UtvvvnmPr1ex44dXakFL7KjAAC7s2XLFo0ZM0arVq1yz+Pj493gmBawxS7LM1do9KrRLlCblrWrvq+/YBeoTf87UNtFsZ2vk5qeIIUX/l4HsH/4NAIAAEBAvPPOO3rssceKBW137typd999d5+Dtnah3aBBgzJuJQCgKsnLy3OleqxerdVWtxt8Vqqna9euBGwlrdqaptFW+mD5r1qxc0Ox/Rfs8aizN1Bbp6NqdLpGanaiFB4fiMMJVGkEbQEAAFCuMjMzXVdLe2zdulWRkZG+efn5+frhhx9Ur169fX7dhQsXqlGjRoqIiFDfvn31yCOPqEWLFmXcegBAZbZ+/XpffXUrhWADjVmWbXW2ZvuaXaUPlv+qpTvWF5sf5PGoY+5GpWSvUf/a7VWz45VSs5OkiBoBaS9QXRC0BQAAQLlKSEhwtQLtYQO9FGXTi9an3RsL0lp2rr3eunXr9NBDD7lurrNnz1bt2rWLLZ+dne0e/oFkY1lX9ihPtj4LYJf3eqsD9i0qI963Bye71lv2wHpkWDkd+9876Fh1/Pxdt2O9xqSNUeqKX7V4e8n139vnbFRy9hoNqNVatdpfLDU7WYpIkG9vVcP9hqqlIEDnYKVdH0FbAAAAlPtgZHaCfPjhh+vzzz9XrVq1fPPCw8PVrFkzlzG7L4477jjfz507d1b//v3VsmVLV4Jh+PDhxZZ/9NFHSwwMp6enKysrS+V94m61FW2fBAcHl+u6qzr2LSoj3rdlx3pv2M27efPm6YQTTlB0dLSbbt8P3qzb6iQje5Mmb5ysSetHa0lWydveNidDA7PXqE9somKbnaashscqLzxBbuktObbXyrvZQJX7vLWeZqVB0BYAAADl6tBDD3X/L126VE2bNnWZtWXNRgC34K2VTCjJ7bffXiiYa5m21k22bt265d5N1i4YbB/Yugnasm8BPhPKhg0wZgONWUDGbghmZGSoefPm1e4NlpG1SWNXj1Xq8l80d+uuQdeKapW7yQVqkxOSVK/NeVLzU6TIXb1UGFIMVVlBgM7B/EuD7QlBWwAAAASEZdQeLFb6YO7cuUpJSSlxvtW9tUdRdsIeiMCpXTAEat1VHfsWlRHv2/23fft2jRs3TkuWLHHPY2Nj1a9fP7Vq1UrVxebszRqbNk6jl/+s2VuWy1PCMs1ztygle7WSE5qpUdu/A7VRdQLQWqD6fd4Gl3JdBG0BAABQ6d1000068cQTXeaudXe1mraWPTts2LBANw0AUE5mzZqlCRMmuBq2Foix2rW9evVymbZVXWZOpsaljXeB2pmbl/xTd9ZPYl6mUrJWK7lGohLbnCUlnSpF1Q1AawGUBkFbAAAAVIlusGeffbY2bNjgurhZVtX48eMPajYvAKDiZdlawLZ+/fpKTk4ucSDKqmRbzjaNX/OXRi/7WdM3LVR+Ccs0ztuq5OzVSolrpGYdhkhJp0lR9QLQWgD7iqAtAAAAKr2PPvoo0E0AAJSzHTt2KDc3VzVq1HDPe/To4Qa3tFIIB6NeekWwI3eHxq+ZoNRlP2lqxgLllVD8oEHediVnpykltoGS2p2moBanS9ENAtJeAPuPoC0AAAAAAKhUgwfNmTNHkyZNUkJCgk4++WQXpA0LC1Pr1q1V1ezM26kJaycpdelPmrxxrnJLCNTWzd+h5CwL1NZTq7anKqjFGVJMw4C0F0DZIGgLAACACsUGEBs8eLBvEBkAALzWrVun1NRUbdy40T33eDzKyspSVFRUldpJWXnZmrxuskYv+0kTN8xSjqd4oLZ2/k4NtEBtTB21beMN1DYKSHsBlD2CtgAAAKhQcnJytHz58kA3AwBQgVhg9q+//tL8+fPd84iICPXu3Vvt27evMqUQcvJzNHndVKUu+1ET0mcqy1N8OLGa+VkamL1aydE11b71KQq2QG1sk4C0F8DBRdAWAAAA5Wr48OF7nJ+enl5ubQEAVHybNm3SN998o+zsbPe8bdu26tu3ryIjI1XZ5Rbkaur6aRq99Cf9tX6adpYQqI0vyNbArNVKiaqhDq1OVkjLIVJsYkDaC6D8ELQFAABAuXruuefUrVs3xcfHlzh/27ZtHBEAgI/VrY2Li1NMTIySk5PVoEHlHlQrryBP09NnuEDt+PVTtL0gv9gycQU56p+1WslRcerS4qRdgdq4ZgFpL4DAIGgLAACAcmWDxNx4440677zzSpw/bdo09ezZk6MCANWUZdTOmDFD3bt3V2hoqCt/cOyxx7rM2uDgYFVG+QX5mrlhllKX/aSxaydpa0FesWViCnLVz0ofRMSoW9IJCm11phTXPCDtBRB4BG0BAABQriwgO3ny5N0Gbe3i3AaWAQBUPwsWLND48eNdDVv7PujVq5ebHh0drcom35OvORvnuozacWsmaHNBbrFlogpy1Td7rVLCI9W9xQkKazlUim8RkPYCqFgI2gIAAKBcPf300766hCXp2rWrCgqK1/QDAFRdGRkZSk1N1dq1a30lERo1aqTKpsBToHkZ8zXaMmpXj1dGfk6xZSI8eepjgdqwcPVoPlgRllFbo2VA2gug4iJoCwAAgHJV2WsRAgDKTk5Ojut9MWvWLNfLwsohWI+Mzp07V5pSCNbuBZsWukDtmLSx2pBf/MZkuCdfvVygNlS9mh2nyNZnSTVaB6S9ACoHgrYAAAAAACAgxo0bp/nz57ufk5KS1L9/f8XGxlaKQO3iLUtc6YPUtFStz8sqtkyoJ189s9crJTRYvZsfo+hWZ0kJbQPSXgCVD0FbAAAAAAAQED169FB6err69u2rxMTECh+oXZa5TKOX/qzUtD+1JndnsWVCPQXqlrNeKSFS32ZHK8YCtTXbB6S9ACo3grYAAAAAAOCgy8vL05QpU1xd85SUFDctLi5OZ5xxRoXe+8szV2j0sp+VunKk0nJ3FJsf7AK16UoOLlD/ZkcptvXZUs0OAWkrgKqDoC0AAAAAADioli1bprFjx2rbtm3ueceOHVWrVq0Ku9dXbU1T6vJfNHrF71qRs6vN/oI9HnV2gdp89U88QjXanCPV6hiQtgKomgjaAgAAoELIz8/XzJkz1axZM9WsWTPQzQEAlIHMzEwXrF2xYoV7bvVqBwwYUCEDtmu2r1Hqsv9p9IpftTR7a7H5QR6POuRuVEpQjgYkHq6arR+UanWSgoIC0l4AVRtBWwAAAATEDTfc4EYHv+SSS1zA9tBDD3UX9tHR0fruu+80aNAgjgwAVFL2uT5t2jT3sJ+Dg4PVtWtXde/eXaGhFScUsX7HeqUu/59GL/ufFmVvKXGZ9jkblaxsDUw8VLXbnCvV6kKgFsBBV3E+KQEAAFCtfPbZZzrvvPPcz99++62WLl2qefPm6d1339Wdd96pMWPGBLqJAID9ZIHaOXPmuP8bN26sgQMHKiEhoULszw07Nyh1+a9KXfaz5mdtLnGZNrkZSlGWBjROUb0290i1uxGoBVCuCNoCAAAgIDZs2KAGDRq4n3/44QcNGTJEbdq0cZm3//73vzkqAFDJ7Nixw/WWMOHh4UpOTpbH41GLFi0C3TRlZGVozPLfNHrZT5q7M6PEZVrmblaKZ4cGNh6oBm3ukur0IFALIGAI2gIAACAg6tev77KwGjZsqJ9++kkvvfSS76I/JCSEowIAlURBQYFmzJihKVOm6JBDDlGrVq3c9KSkpIC2a3P2Zo1b8YdGL/1Bs3aky6PitWeb525Rsme7Uhr1V6O2d0p1ehKoBVAhELQFAABAQFx00UUaOnSoC9oGBQXpqKOOctP/+usvtWvXjqMCAJXA6tWrlZqaqs2bd5UZWLZsmS9oGwiZOZkat2KkUpd+rxnb16nAF6j9J2CbmJeplIJtSm7YV4ltb5fq9iZQC6DCIWgLAACAgLjvvvvUqVMnrVy50pVGiIiIcNMty/a2227jqABABWa9IsaNG6fFixe751FRUerXr59at25d7m3ZlrNd41eN1Ogl32r6trXKLyFQ2yhvm1IKMpXSoLeatrlFQfX7SkHB5d5WACgtgrYAAAAICMvGOuOMM4pNHzZsWEDaAwAonUWLFmn06NHKzc11PSU6dOig3r17uzq25WVH7g79tepPjV7yjaZuXaM835x/ArX187YrJX+LUhr0VFKbcxXUoD+BWgCVBkFbAAAABIQNTDNgwACdf/75LtO2Vq1aHAkAqARiYmJcwLZevXpusLE6deqUy3p35u3UxLRUjV70tSZvTVNuCcvUzd+h5PzNSqnXXa0sUNtwIIFaAJUSQVsAAAAExKRJk/TBBx/ooYce0vXXX69jjjlG5513nk466SRfqQQAQODt3LlT6enpatq0qXtutchPOOEEX03ygykrL1uTV4/V6MVfauKWlcopYZla+Tt3BWrrdlGbtucpuGEygVoAlR5BWwAAAAREjx493OPJJ5/UyJEjXQD3iiuu0KWXXqrTTz9db775JkcGAALI4/Fo7ty5mjBhggoKCtzgkbGxsW5eo0aNDtp6c/JzNHn1eKUu/kITNi9XVgnLJORnKTl/k5LrdFJ7F6hNkYJDDlqbAKC8EbQFAABAQFmW1mGHHeYeV111lS655BK98847BG0BIIAsszY1NdX9b6wEgpVEOFhyC3I1bc1EjV74uf7avEQ7SlgmviBbA3I3KaVue3Vsc65CGg0iUAugyiJoCwAAgIBauXKlPvzwQ5dpO3PmTPXv318vvPACRwUAAiA7O9tl1lqGrbHBxWyQMRtsrKxLIeQV5GnG2skavfAzjdu0SNtLWCa2IEf98zKUUrudurQ5RyGND5OCCWUAqPr4pAMAAEBAvPbaa3r//fc1ZswYtW3bVueee66++uorNW/enCMCAAGQl5enzz77TNu37wqftm7dWv369VNUVFSZrSO/IF+z1k/V6IWfamzGAm31FF8mpiBXffM2KqVWG3Vtc7bCmhxJoBZAtUPQFgAAAAHx4IMP6qyzztJzzz2nbt26cRQAIMBCQ0PVpk0bLVu2TMnJyW6gsbKQ78nXnPUzNHrBJxqXMV+bPcUjtVEuUJuhlJqt1N0bqA0JK5P1A0BlRNAWAAAAAbFixYqDPuo4AGD3cnJyNGnSJJdRW7duXTfNBojs2bOngoODD2jXFXgKNG/DLKUu+ERjNsxRRgmB2ghPnvrkZig5IUk925yliMSjpZBwDhkAELQFAABAoBCwBYDAWbRokcaPH68dO3Zo3bp1OvXUU930kJCQ/X5Nj8ejBRvnKHXBx0pNn6UNJQRqwz356pm7USk1mql3mzMV2fQ4ArUAUAIybQEAAAAAqCY2bdrkaomvXr3aPa9Ro4YbaOxAArWLMxYodcFHGp0+Q+sLCootE+oCtRlKrpGoPq2HKLrZYCkk4oC2AwCqOoK2AAAAAABUcbm5uZoyZYpmzpypgoICV7+2e/fu6tKlyz5n11qgdtnmxRo9/0Olrp+mNSUEakM8Beqem6GU+Ebq23qIYpqdIIVGluEWAUDVRtAWAAAAAIAqbunSpZo+fbr7uXnz5urfv7/i4uL26TVWbFmq0fM+UOq6qVpVkF9sfrCnQF3zNiklrqH6tTpdcc1PIlALAPuJoC0AAAACav369Zo/f76rcWujlterV48jAgBlwDJqvQOK2WBjK1eudP83bdq01K+RlrlCo+d/oNS1k7U8P6/Y/GCPR53yMpQSW1/9W52mGkmnSKFRHD8AOEAEbQEAABAQmZmZuuaaa/TRRx8pP39XxpZ10T3zzDP14osvujqLAIB9l5eXp2nTpmnx4sU6/fTTXSkEuzF2xBFHlOr3125drdHz31fqmglaUkKgNsjjUQfLqI2pqwEtT1HNFqdJYdEcKgAoQwRtAQAAEBCXXnqpCyp89913rpuuBRTGjh2r66+/Xpdddpk++eQTjgwA7KPly5e7z9KtW7e654sWLVK7du32+nvrt61V6vwPNHrNeC3Kyy1xmXa5FqitrYEtTlLtlmdIYTEcHwA4SAjaAgAAICC+//57/fzzz0pOTvZNO+aYY/T666/r2GOP5agAwD6wIK0Fay1oa2JiYjRgwAAlJSXt9nc27EjXmHkfaPTqsZqfl1PiMq1zNysluqYGJp2geq2HSmGxHBcAKAcEbQEAABAQtWvXLrEEgk2rWbNmQNoEAJWNx+PR1KlT3cNKzVgN286dO6tHjx4KCwsrtvymnRs1xjJqV43RnLzsEl+zRd4WpUTWUHKL49Wg1VlS+L4NWAYAOHAEbQEAABAQd911l4YPH653331XDRs2dNPWrl2rm2++WXfffTdHBQBKwUrLbNy40QVs7bPUei8UvfG1JWuTxs7/UKNXjdasnJ3yBAUVe53meZlKjoxTSvPj1KjN2VJ4PPsfAAKIoC0AAAAC4uWXX3a1Fps1a+YbyXzFihWKiIhQenq6Xn31Vd+yU6ZM4SgBwN+2b9/uMmqjoqLcc6sLbmUQWrVq5dtHW7MzNXb+R0pdNVIzsneowBuo9QvYNsnbppSIaKU0P0aJbc6VIhgAEgAqCoK2AAAACIhTTjmFPQ8A+6CgoECzZs3SpEmT1Lx5cx1++OFuemxsrAvYbsvZpvELPlbqit81LXu78ksI1DbM266UiEilNDtKzdqcp6BIytEAQEVE0BYAAAABce+997LnAaCIAk+BsvOz3f/BCvZNX7NmjVJTU7Vp0ybfwGN5eXnKKcjWXws/UeqK3zQla6vygoKLBWrr529XcliEUpodoRZtL1BQZC32OwBUcARtAQAAUKU8+uijuuOOO3T99ddrxIgRgW4OAJTK0i1L9fWib/VnWqpyC3IVFhymQxon6+gmR2ndnLVauHChWy4yMlLdenXTZk3TE/+7SJOyMpXrC9T+E+Stk79TyWGhSkk8XK3bDVNQVG2OBABUIgRtAQAAEBBWj9EG0NkdG1RnX02cOFGvvfaaunTpcoCtA4DyM2rVaD0z6VkFySPvJ58Fbv9Y+Yd+X/GHumd2VQNPPcU1zdBKzdfX8z9XTgmB2lreQG2TQ9Wm/YUKjqrLYQSASoqgLQAAAALiyy+/LPQ8NzdXU6dO1TvvvKP7779/n19v27ZtOvfcc/X666/roYceKsOWAsDBzbC1gK2VQ/AvaWAK3L8eTYmfpvD4AuVkhxQL1CbkZ2lgaLBSmhyi9u0vUnB0PQ4XAFQBBG0BAAAQECeffHKxaWeccYY6duyojz/+WJdccsk+vd4111yjwYMH68gjjyRoC6BSyMiYp/enPCd58gsFYgv5O5Cbo78DtpLiC7I1ICRIyY0HqlOHixUS3aC8mgwAKCcEbQEAAFCh9O3bV5dddtk+/c5HH32kKVOmuPIIpZGdne0eXpmZmb6R2e1Rnmx9Ho+n3NdbHbBvUdH9MOUZTdq6TgW7C9j683g0IDddRzc/Rp3bXazQ2Ea+WXx+AEDlOU8o7foI2gIAAKDC2Llzp55//nk1adKk1L+zcuVKN+jYL7/84gboKe1gZSWVYEhPT1dWVpbK+8R9y5Yt7qLB6vyCfYvqo2uTM/XxvBdKt3BQkE7s8qDqJHRQxg5JO9Yf7OYBQJVWEKBzsK1bt5ZqOYK2AAAACIiaNWsWGojMTpjtJDY6OlrvvfdeqV9n8uTJWr9+vXr27FloELM///xTL7zwgsuoDQn5p1uxuf322zV8+PBCmbaJiYmqW7eu4uPjVd4XDLYfbN0Ebdm3qD5ycnI0fvxUBXk88uxhUEavME++6ifEq3Y9atYCQGU+ByttkgFBWwAAAATEs88+WyhoayfLdtJs5REsoFtaRxxxhGbOnFlo2kUXXaR27drp1ltvLRawNREREe5RlLUhEIFT2w+BWndVx75FRZRfkKe3v79Ovwetlic4bK/Lh3gKlJyVpv/Nel3nHPlqubQRAKqDoACcg5V2XQRtAQAAEBAXXnhhmbxOXFycOnXqVGhaTEyMateuXWw6AATaguX/08tTn9cid0Pp74Ctx7Pr/5Iybi0TV9JRMXXUuMeN5dtYAEDAELQFAABAuZkxY0apl+3SpctBbQsAlJfc3FyNHf+rJm18R6M8WfIE/dMD4Chlq/XWeXolrrMrlZDvNyiZZdhawHZ45lR1PuFbqVY7DhoAVBMEbQEAAFBuunXr5rqhWf1a418eoSirS7u/Ro4cud+/CwBladHChfplwuMaE7lGmSHhvmza5gU7dVWbM9Sh06XSog/V7s9r9HVUkv6MbKzcoBBXw/aQrDSdvHOpkg55UardlQMDANUIQVsAAACUm6VLl/p+njp1qm666SbdfPPN6t+/v5s2btw4Pf3003riiSc4KgAqtc2bN+vXkW9pbM5PWhgTLSncTY/05OmcGk104sDHFRrx98CHrc5WUs0OumHGCP1r0YfKLchTeHCogludLXV5k4AtAFRDBG0BAABQbpo1a+b7eciQIfr3v/+t448/vlBJhMTERN1999065ZRTODIAKp28vDxNmDhKo5f8W+OjpbxwC9juMiAoT5f1u1t1GvQp/ouWSXvYWwo65HVtWbNCdRs2k0oYSBEAUD0QtAUAAEBAzJw5U0lJScWm27Q5c+YEpE0AcKDGTX1eb67+RRtiIn3TGhRk6YoWx6pX1+tKHmzMX1CwPKHRe18OAFClEbQFAABAQLRv314PPfSQ/vOf/ygycldwIzs7202zeQBQWdhn15Ytc/Ta+Pv1V0GQFLLrMy3Uk6/TY+tqSMqTioisFehmAgAqEYK2AAAACIhXXnlFJ554oiuH0LXrrgF2pk+f7gYn++677zgqACo8GzBxypTxGjP/aaXG5Co76J9L7K7K1VW9b1bjJocGtI0AgMqJoC0AAAACok+fPm5gsvfee0/z5s2Tx+PRmWeeqXPOOUcxMTEcFQAV2sqVK/XH6Kc1Nnyu0mKjfZfXNQtydGnTFKX0uEVBwcGBbiYAoJIiaAsAAICAiY6O1uWXX84RAFBpbNu2TaP//FQTt3yoidF2g2nXQGPBHo8GR8fp3IFPKCa2YaCbCQCo5LjtBwAAgID573//q+TkZDVq1EjLly9305599ll9/fXXHBUAFc6smVP13y8v1DtZn2ti1D89AtooV093v0KXH/NfArYAgDJB0BYAAAAB8fLLL2v48OE67rjjtGnTJlcb0tSsWVMjRozgqACoUBYt/FT/WXCbfojL1/bgMDctxpOnq+t31hMnfalWzY8LdBMBAFUI5REAAAAQEM8//7xef/11nXLKKXrsscd803v16qWbbrqJowIg4Hbs2KH0tXP007wn9EN2jgpCdpVCMIeHR+qigY8qoUbzgLYRAFA1EbQFAABAQNggZN27dy82PSIiQtu3bw9ImwDAFBQUaNbM6Zo4/XGNjMnU5pAIKSjIzUv05OrqThepU+vT2VkAgIOGoC0AAAACIikpSdOmTVOzZs0KTf/xxx/VoUMHjgqAgFi7dq1SR76oiUFjNSc+3m4luekRnnydVaedTu7/gMJCIzk6AICDiqAtAAAAAuLmm2/WNddco6ysLHk8Hk2YMEEffvihHn30Ub3xxhscFQDlaufOnfor9QdNW/eaRsVEKC/IAra79A0N1eUDHlG9Wu05KgCAckHQFgAAAAFx0UUXKS8vT7fccourG3nOOeeocePGeu6553TWWWdxVACUm507tuuzz67QqOh0rYv9p25tPU+urmg3VH3aX8DRAACUK4K2AAAACJjLLrvMPTZs2OBqSNarV4+jAaBcpa/6Q69PfFzj4qMk7QrYhnoKdGrNZho64FFFhsdwRAAA5Y6gLQAAAALGMm1HjhypxYsXu0xbs3r1asXHxys2NpYjA+CgyM7O1uS/ftPqHR/q4x2blRVsAdtdOocE66o+dyuxfg/2PgAgYAjaAgAAICCWL1+uY489VitWrHABlKOOOkpxcXF64oknXJ3bV155hSMDoExZ/ewF8+dr6oTHNTpqpVaExUnBuy6LEzx5urjFcRrU5SoFBQWx5wEAAUXQFgAAAAFx/fXXq1evXpo+fbpq167tm37qqafq0ksv5agAKFMbN27UXyPf0vSc7zQ2vqakODc9yOPRcfENdP6ARxQbVYu9DgCoEAjaAgAAICBSU1M1ZswYhYeHF5rerFkzpaWlcVQAlImcnBxNHj9Sc5aN0G+xQdoaZQHbXVoFS1f1/D+1aZzC3gYAVCgEbQEAABAQNvBYfn5+semrVq1yZRIA4IB5PPrr9wf0ZeZYLYyv4Zsc7cnXBU0P0bE9blRIUAg7GgBQ4QQHugEAAAConqyG7YgRI3zPrYbktm3bdO+99+r4448PaNsAVP7atTvSp+uNb07QUztnamH4PwHbQdE19cpRr2lwz5sI2AIAKiwybQEAABAQzz77rA477DB16NDBDTx2zjnnaOHChapTp44+/PBDjgqAfZabm6upE1K1fOUL+i5shzJCIqW/xxRrHOTRVV2vUtfmx7BnAQAVHkFbAAAABESjRo00bdo0F6CdMmWKK5dwySWX6Nxzz1VUVBRHBcA+WbpkiWaMeVYTwmdoRqQNKBbppod7CnRmo146tddtCgsJY68CACoFgrYAAAAIGAvOXnzxxe4BAPtjy5YtmjjyYy3c+p7+F5ug3CAL2O7SKyJWVwx4UA1qNGfnAgAqFYK2AAAACJj58+fr+eef19y5c11N23bt2unaa691/wPAnlh2/tRJ47RozuP6LTZLa2Jr++bVUYEu73ih+rU6xX22AABQ2TAQGQAAAALis88+U6dOnTR58mR17dpVXbp0cWUSOnfurE8//ZSjAmCPMhZ/pp+W3Kb3EkK0JjTGTQvxFOi0Ou310uAP1b/1qQRsAQCVFpm2AAAACIhbbrlFt99+ux544IFC0++9917deuutGjJkCEcGQCHbtm1TWM5q/TTuVr2fnaudkXV88zqGReqqfveoWe327DUAQKVH0BYAAAABsXbtWl1wwQXFpp933nl68sknA9ImABVTfn6+Zk6brGUzHtPvsVu0NCxeCt41qFi8CnRxmzN0ePvzyKwFAFQZBG0BAAAQEIMGDdLo0aPVqlWrQtNTU1OVkpLCUQHgpKWlafofz2tu8J8aWbO+C9N6HZOQpGH971NcxD/TAACoCgjaAgCAiqsgXwoOCXQrcJCcdNJJrgyC1bTt16+fmzZ+/HhXz/b+++/XN998U2hZANXL9u3bNfnPb7Rq/Sv6Pi5GmcEWsN2lRWiYrup1q9o16BnQNgIAcLAQtMX+G3WFlLewiu7BICmoteSx7fOoaqoO21hdtrM6bGN12U62sZCE9tIR7wfqYKAcXH311e7/l156yT1Kmmds5HfrGg2g+li+dJFmjrxDf8as09watXzTozwFOq/FcRrc+TKFcFMPAFCFEbTF/sucL+2YXkX3YLAUESJlT7M0L1VN1WEbq8t2VodtrC7byTaieikoqKp/ywAOxM5Vv+nXyQ/om4QEFQT9E7BNiWukS/rfr9rR/ww+BgBAVUXQFgAAAAAQUDt37tTKBZO0cd3Lem3rRm0I/SdY2zA4RFf2uE49mhwS0DYCAFCegst1bQAAAKj2/vrrL/3444+F9sO7776rpKQk1atXT5dffrmys7Or/X4CqgOPx6PZs2Zo5EeX6v15t+mRHTu1ISTazQvzeHROYopeGPwBAVsAQLVDpi0AAADK1X333adBgwbpuOOOc89nzpypSy65RBdeeKHat2+vJ598Uo0aNXLLAai61q9fr+l/vKGVuV/r+4T6ygmq55vXI7q2ruh3nxrFNwloGwEACBSCtgAAAChX06ZN04MPPuh7/tFHH6lv3756/fXX3fPExETde++9BG2BKiorK0tTxv5PG5c9q+/jg5UW0cg3r3ZQkC7rcpkGND/WDUIIAEB1RdAWAAAA5WrTpk2qX7++7/moUaN07LHH+p737t1bK1eu5KgAVZGnQOM/+5f+Cpmp1FoNfZODPR6d1LCXzu75f4oOiwpoEwEAqAioaQsAAIByZQHbpUuXup9zcnI0ZcoU9e/f3zd/69atCgsL46gAVUx++mR999VRejl6jVKj/gnYtouI14hBT+mSfncRsAUA4G9k2gIAAKBcWVbtbbfdpscff1xfffWVoqOjlZKS4ps/Y8YMtWzZkqMCVAHuxsz43xW+5b96N2u1FoclSH9XPYiTdGGHc3Vk69MUHEQ+EQAA/gjaAgAAoFw99NBDOu2003TooYcqNjZW77zzjsLDw33z33zzTR199NEcFaCSWzB/vlaOe0qzw6fp16jG8ljA9m9H1emgYb1vVY2I+IC2EQCAioqgLQAAAMpV3bp1NXr0aG3ZssUFbUNCQgrN//TTT910AJVTRkaGpv3xnjZv+6++jKujzSFNfPOah0Xrqj63qkPdLgFtIwAAFR1BWwAAAAREjRo1Spxeq1atcm8LgAOXm5urqRP+VObcJ/Rb/A7NqvFPsDZS0rmtT9EJ7c9VaDCXoQAA7A2FgwAAAFDpvfzyy+rSpYvi4+PdwwY2+/HHHwPdLKD68HiUMe0NzVh2s16oHalZ4XV8swYkJOnlY17XKR2HEbAFAKCUuMUJAACASq9JkyZ67LHH1KpVK/fc6uSefPLJmjp1qjp27Bjo5gFVOrs2bPsSTfjzer2a69H6mGa+eQ1CwnVFzxvVq1G/gLYRAIDKiKAtAAAAKr0TTzyx0POHH37YZd+OHz+eoC1wEOTl5Wn65HHaNvtx/RmzURMiGkh/l6cOlUenNz9aQzpfooiQCPY/AAD7gaAtAAAAqpT8/Hw3mNn27dtdmYSSZGdnu4dXZmam+7+goMA9ypOtz+PxlPt6qwP27cGxfNkyLfnzOa0NGaUvaiYqJ6iBb16X2Aa6svcdahzX2HcMsG943wJA+QjU521p10fQFgAAAFXCzJkzXZA2KytLsbGx+vLLL9WhQ4cSl3300Ud1//33F5uenp7ufr+8T9y3bNniLhqCgxlygn1bcW3dulVz/vpekTve1de1amplaJJvXkJQsIYknaPedQcoaGeQ1u9cH9C2VmZ8JgBA1f68te/T0iBoCwAAgCqhbdu2mjZtmjZv3qzPP/9cw4YN06hRo0oM3N5+++0aPnx4oUzbxMRE1a1b1w1kVt4XDEFBQW7dBG3ZtxXVjKkTtGXKI1oUu1oj6/1TtzZYHh3faIDO6XqVYsJiAtrGqoLPBACo2p+3kZGRpVqOoC0AAACqhPDwcN9AZL169dLEiRP13HPP6dVXXy22bEREhHsUZSfsgQic2gVDoNZd1bFvD1z+8u+1Yv7d+qB2Q20PTvRNbxNVW1f1uU2tau76u0PZ4X0LAFX387a06yJoCwAAgCrJurr5160FUHrbtm1T/palSp9xj17elqEF0f9k18YEBWtYxwt0dMsTFBL09+hjAACgTBG0BQAAQKV3xx136LjjjnMlDqxO2EcffaSRI0fqp59+CnTTgErXVXTW9CnKnPyoZkUv0Y9RzVQQVss3//D63XVRj+uUEJEQ0HYCAFDVEbQFAABApbdu3Tqdf/75WrNmjWrUqKEuXbq4gO1RRx0V6KYBlcbq1as1/4+XtTPvS31UM1GbQpr75iVGxOnqXreoU91OAW0jAADVBUFbAAAAVHr/+c9/At0EoNLasWOHpqR+p+AVI/RbXJCmx7T2zYtQkM5ue4ZOajtEYcFhAW0nAADVCUFbAAAAAKimNm1M15wv/6XFkbP1Ve0k5fnVqO1bq40u7/V/qhddL6BtBACgOiJoCwAAAADV0ZpULRpzk95KiNe60Fa+yfVCo3RFz+vVp2HfgDYPAIDqjKAtAAAAAFQTWVlZmjXxdyVmf6D/bFiocZGNCl0cntrieA3tcIEiQyMC2k4AAKo7grYAAAAAUMV5PB7NnTNLm8Y/obTwyXo8pqWy/AK2neOb6qreNykxLjGg7QQAALsQtAUAAACAKiw9PV2z/3hL4dve1ofxDbQ8rK1vXkJIuC7ucqUGNR2koKCggLYTAAD8g6AtAAAAAFRB2dnZmjruV4UueELT4rbqt1r/BGstPHtc4qE6v/Nlig2PCWg7AQBAcQRtAQAAAKCq8RRoyY93aUPmN/qgTittDa7lm9Uqpr6u6vV/alOzdUCbCAAAdo+gLQAAAABUJRumaWnqdXozR5oX38E3OTooRBd0HKZjWx6vkKCQgDYRAADsGUFbAAAAAKjkcnJyNG3CKNVKe0kj8xfr26gkFYQH++YPatBbF3e7SjUjawa0nQAAoHQI2gIAAABAJbZo4UKtHfO0svWrXoxrpYyIlr55jSMTdFXPG9W1bpeAthEAAOwbgrYAAAAAUAlt3rxZ0/54XwkbX9b/4uI1JaKzb154UJDObHumTm19msJCwgLaTgAAsO8I2gIAAABAJZKXl6dpE0craOZjWh29Qv+u3Vq5fjVqe9XuoCt6XKcGMfUD2k4AALD/CNoCAAAAQGXh8UiLP1HO/Lv1dq0WWhPazjerTliMLu/+L/Vr2EdBQUEBbSYAADgwBG0BAAAAoILbtm2bYvPStDH1Or2xLUOpCf/UqLUc21Nanqgz25+tqNCogLYTAACUDYK2AAAAAFCBSyHMmDJenmmPamXUQn0Q00Y7I5v45neskaSrel6vZvHNAtpOAABQtgjaAgAAAEAFtGLFCi0b9W/F5H6ud2omaWlYR9+8+JAIXdzlch3e9DBKIQAAUAURtAUAAACAClYKYeqoz1U77VnNiA3Sz3HdfPOsUu0xTQ/XBZ0uVFx4XEDbCQAADh6CtgAAAABQQSycO1NbUu/WjvAZeqVOO2UGR/jmtYhtqKt6XK92tdoGtI0AAODgI2gLAAAAABXBip8UPHW4Pq1RW3PCu/omRwWH6rwOF2hwi+MVEmzDjgEAgKqOoC0AAAAABMj27du1ccU01V3xtD7csEBfR7dWQVCwb35Kw766pMvlqh1Vi2MEAEA1QtAWAAAAAMpZQUGBZs+YqqxJjyordILujWuvDTGtffMbRdXWld2vVfd6/9SzBQAA1QdBWwAAAAAoR2vXrtX8P15Rg+3v6Iu4BpoU0dM3LywoWEPaDtXprU9VeEg4xwUAgGqKoC0AAAAAlIOdO3dq6pgfVGvx41obs0X/rt1ZOUH/1KjtUaeTruh2tRrFNuR4AABQzRG0BQAAAICDLC8nS7M/ukzh+kMjardXWmhj37za4XG6rOuVGtCov4KCgjgWAACAoC0AAAAAHFRrx2jr6H/pj8hg/RnVxzc5WEE6qeUJOrvd2YoOi+IgAAAAHzJtAQAAAKCMZWVladq4X9R55/v6Y8NE/Te2g3aEhfnmt0toqau7X6OkGknsewAAUAxBWwAAAAAoIx6PR/PnzdHmcU8ozvOL7oxvp8XxXX3z40IjdWGnS3Rks8MVHBTMfgcAACUiaAsAAAAAZWDDhg2a9cdbarbpFU2JjdVPUX3l8atRe1TTwzWs4zDViIhnfwMAgD0iaAsAAAAAByAnJ0dTx/2muHmPqSByqe6p3VGbQyJ985vHNtZV3a9Rh9rt2c8AAKBUCNoCAAAAwP7yFGjTuKdVa+FzerNma80K7+mbFRkcpnM7nKsTWgxWaDCXXgAAoPQ4cwAAAACAfZSXl6fQzbOUlXq1ftq+RV/W7qt8vxq1Axr21WVdLlWdqDrsWwAAsM8I2gIAAABAKeXm5mrahFGKn/+kNgXP1OtxnbU+pr5vfoOoOrqi25XqVf+fjFsAAIB9xXClAACgUnv55ZfVpUsXxcfHu0f//v31448/FlrmwgsvVFBQUKFHv379Ci2zePFinXrqqapbt657naFDh2rdunUHvG4AVceSxYs14f3rVG/Oufo0eoseTuir9SHRbl5oULDObDtELxz5AgFbAABwwAja/m3QoEG64YYbDniHfvXVV2rVqpVCQkLc67399ttKSEjY7wEN7LXGjBmjg+G7775T9+7dVVBQcFBeHwCA8tCkSRM99thjmjRpknscfvjhOvnkkzV79uxCyx177LFas2aN7/HDDz/45m3fvl1HH320C+b+/vvv7rvXvodPPPHEPX5PlnbdACq3LVu26M+vX1bor8drdchvGl6nj/6KbOib361OZ71wxL91XvtzFBESEdC2AgCAqoGgbRm74oordMYZZ2jlypV68MEHdeaZZ2rBggW++ffdd5+6detWqtd67bXX1KxZMw0cONA3zS4mLTBckpEjR7r5NWvWVFZWVqF5EyZM8GUWeZ1wwgnu+QcffLAfWwoAQMVggdXjjz9ebdq0cY+HH35YsbGxGj9+fKHlIiIi1KBBA9+jVq1avnkWpF22bJm72dq5c2f3eOuttzRx4kQXxD3Qdfvzngu8+eabatq0qVv+qquuUn5+vp544gnXtnr16rnXKvp7trxtR6NGjXTdddcd0H4DsHcej0eTx4/S8o/PVMLGe/VY7SZ6N66jcoJ2VZmrFR6vW3r9nx4YeL8axzZmlwIAgDJD0LYMbdu2TevXr9cxxxzjLqbi4uIUFRXlLrz2x/PPP69LL710n3/P1vvll18Wmua9MCzqoosucusBAKAqsMDnRx995DJnrVRB0Zub9p1swdXLLrvMfWd7ZWdnuxuZFhD1ioyMVHBwsFJTUw943UVZKQYro/DTTz/pww8/dN/TgwcP1qpVqzRq1Cg9/vjjuuuuu3zB388++0zPPvusXn31VS1cuNDdwLXAMoCDyONR0NLP1WTOOfo1Ll131hqolaHxblawgnRii8F66aiXlNIkuVBiBAAAQFkgaLsbzZs310MPPaQLLrjAZcBYxuvXX3+t9PR01+3RptnFknWF9F4IWrDUWNdIO3Gzaf7lEezn+++/X9OnT/dlvdq0kkyZMkWLFi1yF3D7atiwYe7iz2vnzp3uItKmF3XSSSe5LNwlS5bs83oAAKgoZs6c6b6bLeh65ZVXupuXHTp08M0/7rjj9P7777us2aefftpl0Nr3tQVrjdW3jYmJ0a233qodO3a4wOvNN9/sSiNYKYUDWXdJ7HXtu9qWs2zdww47TPPnz9eIESPUtm1bd1PV/rdzCbNixQqXgXvkkUe6m7B9+vRxgWcAZW/r1q3auXa68r8/Vj+m3qzrE7rpj6h/kh/aJLTU04Oe1OVdLlVMWAyHAAAAHBQEbffAMlqsNMHUqVNd8PT88893QdzzzjvPBVWt3qw9t25TAwYMcBdb5vPPP3cXeDbNn5VK+L//+z917NjRV0/PppXkzz//dJlANqjJvrJ2jh492l3gedtjQegePXoUW9aC0ZZ1ZMvvjl3QZmZmFnoAAFCRWIBz2rRpLjPVSg3Yjco5c+b45tv3rX2Xd+rUyQVJLcvVyhd9//33br4NPvbpp5/q22+/dQHYGjVquBqW9t1pdeoPZN0lse9l781eU79+fRfAtcxe/2nebOAhQ4a4m7AtWrRwwVoLDOfl5e33/gJQcrb81IljtPjDs7Xq+yN0y84svRTfTduDw938mNAoXd31Sj156BNqldCSXQgAAA4qgrZ7YDXqrEZt69atdc8997i77r1793YXThZQtWycuXPnupGlw8PDfWUQrEaeZcPYNH9WKsEuBENDQ3319GxaSayunpVY2B/WDsso8mbxWibPxRdfvNvlGzdu7Na3O48++qi7ePU+EhMT96tdAAAcLPadazdTe/Xq5b63unbtqueee263yzds2NDduLRSA142EJmVLbBA6YYNG/Tf//5XaWlpSkpK2ud1//vf/97j74SFhRV6br1vSprmHQTNvnvt5vCLL77ozh2uvvpqHXLIIcrNzd3jegCUjpUmGfvhbWo49TT9FbtSN9VK1oKwf+peH554mF456iUdl3SMgoO4hAIAAAcfZxx70KVLl0LZLsa/fpx3mn9NvLJi2TRWS29/WZDWgrZW9mDcuHE699xzd7usXfxZV9Dduf322122kfdhg6wBAFCRWS8Yb+mDkmzcuNF9n1nwtqg6deq40kZWSsG+462UUFmue3/Z97W1xQLCVjbBvt+tNAOA/WelUFJ//K/yvj9eBfkf6pbaPfRddAsV/F2jNjG2sR5Nfkg39rxOCRG7Sp4BAACUh13DnqJE/hkv3sEFSprmzYIpS3bBeCAXYt4s4UsuucR1A61du/Zul83IyHDdQnfHavT5D8wCAEBFcscdd7geJpaNar1irI67BTVtkC/vQKH33XefTj/9dBektd4l9jv2XXvqqaf6Xuett95S+/bt3XeiBUSvv/563Xjjja78gdcRRxzhfufaa6/d47p/+OGHMt1GuxFrXbf79u2r6OholwVsQVzLFgawf9asWqrVPw5XYtBIvZ7QQdMj/il5EBEcprPbn62TW56o0GAumQAAQPnjDKScWRdKu+jam+7du+vll1922Tr7Mxqt1d+z2rZPPPGEq9u3O1lZWa4rqK0PAIDKyMoU2Xee1Yq3Mj7WU8YCtkcddZTvO9FuhL777rvavHmzC9zawF8ff/xxobqyVn7AepfYzUyrOXvnnXe6oK0/+8600gl7W7cFd8uyJ45l/j722GMaPny4O4+wnj9Wf3dPN2WrGytN8cUXX2jevHkuoG1jCzz+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abzgf7H76aeflurVq9u7sp544gnJzc2VBx54QJKSkiQ5OVn+/ve/BxySoGPHjjaB3KJFC3uXl6+NGzdKz549pUKFCvZ333rrrbJ///4Cla/Dhg2TkSNHSpUqVeTyyy8P2I78/HwZN26c3Y+YmBjbps8//9y7Xosdvv76a/sa/Vnb7U/buXjxYnnhhRfsa/Shd2J56PvbtWsn5cuXt23Sil3/46jHoEGDBnYfjDGSkZEhQ4YMsfF1QkKCXHrppfLNN98USCT37t3btl2PwQUXXCDz588vsF/79u2Tq6++2sbp9evXDxjH6fb13EC3W6tWLbn33nsDHicAKCqStgBwXNu2bW0ANn36dO8x0Z81mdumTZsCx0kDw2eeecYGihrUpaSkyIcffljgNXPmzJEmTZrY9b/73e8KBKKBhkcIJpDUoF6D+EGDBtkqUd3f1157rdA+nDVrlt2GBtdKhyPQoFiDfo877rhD+vfvb3/+5Zdf7M8ahGuQfN5559mk9ZnSoPbNN9+UmTNnegNyPYHwHx7BM3TEF198YY+1HjMNtDVw/uyzz+Tcc8+1wbfu25EjR06rH/z985//tK/TNvr3xaeffipNmza1bdfhDQ4fPmz3X497YmKi3HPPPZKXl+d939SpU6Vx48b25Ej7Td/j65prrinS8QMAAOFzQVpjtBdffNFeIC6KL7/8Un744QdZsmSJHWpJ46mrrrrKxiL//e9/7Z1h+ti9e3eB92l8N2rUKFmzZo1NdGocorGd0jucunTpYpOdq1atsknWn376SW644YYCv0PjHr2o/u9//1teffXVgPunidZnn31W/vznP8u6devkiiuusNvaunWrd1uaNNZ90Z/vv//+gL+jQ4cOMnjwYPsafWjM7XtXlG5D91X3R2NfX99//72N6T766CNvPNmrVy/58ccfbQyuSV+N7bt16ya//vqrXX/o0CGbtNb4Wo+R7rcmaFNTUwskkzVO1T7QmFJjPY1HPXTZc889Z4+NtleHwNB4GQBKhAEAmAEDBpjevXubyZMnm27dunmPiP783HPP2XX6Go9HH33UNGvWzHz++edm27Zt5o033jAxMTFm0aJFdn1qaqp9Pnz4cPPdd9+Zd955x1SvXt3on90DBw7Y1+h7Klas6P2da9euNa+88opZt26d2bJlixk9erSJjY01u3bt8r6mXr16Jikp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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# FIGURE 1: Residual histograms (6 panels)\n", "truth_arrays = [truths[pn] for pn in PARAM_NAMES]\n", " \n", "fig1, axes1 = plt.subplots(2, 3, figsize=(16, 9))\n", "fig1.suptitle(\n", - " f\"Parameter Residuals — {ROWS}×{COLS} pixels, {N_SCAN} scan points\",\n", + " f\"Parameter Residuals \u2014 {ROWS}\u00d7{COLS} pixels, {N_SCAN} scan points\",\n", " fontsize=14, fontweight=\"bold\")\n", " \n", "for idx, (pname, truth) in enumerate(zip(PARAM_NAMES, truth_arrays)):\n", @@ -421,9 +322,9 @@ " linestyle=styles[mname][\"linestyle\"])\n", " \n", " ax.axvline(0, color=\"k\", linestyle=\"--\", linewidth=1, alpha=0.7)\n", - " ax.set_xlabel(f\"Fitted {pname} − True {pname}\")\n", + " ax.set_xlabel(f\"Fitted {pname} \u2212 True {pname}\")\n", " ax.set_ylabel(\"Pixel count\")\n", - " ax.set_title(f\"Δ{pname}\")\n", + " ax.set_title(f\"\u0394{pname}\")\n", " ax.legend(fontsize=8)\n", " ax.grid(alpha=0.3)\n", " \n", @@ -444,7 +345,7 @@ " color=[colors[n] for n in names],\n", " edgecolor=\"white\", height=0.5)\n", "ax2a.set_xlabel(\"Median wall time (ms)\")\n", - "ax2a.set_title(f\"Single call — {ROWS}×{COLS} px, {N_THREADS} threads\")\n", + "ax2a.set_title(f\"Single call \u2014 {ROWS}\u00d7{COLS} px, {N_THREADS} threads\")\n", "for bar, val in zip(bars, medians):\n", " ax2a.text(bar.get_width() + max(medians) * 0.02,\n", " bar.get_y() + bar.get_height() / 2,\n", diff --git a/src/Fit.cpp b/src/Fit.cpp index 3d04ecb8..f11aa611 100644 --- a/src/Fit.cpp +++ b/src/Fit.cpp @@ -4,7 +4,6 @@ #include "Minuit2/FunctionMinimum.h" #include "Minuit2/MnHesse.h" #include "Minuit2/MnMigrad.h" -#include "Minuit2/MnPrint.h" #include "Minuit2/MnStrategy.h" #include "Minuit2/MnUserParameters.h" #include "aare/Models.hpp" @@ -12,461 +11,11 @@ #include "aare/utils/task.hpp" #include #include -#include -#include #include #include -#include #include namespace aare { - -namespace func { - -double gaus(const double x, const double *par) { - return par[0] * exp(-pow(x - par[1], 2) / (2 * pow(par[2], 2))); -} - -NDArray gaus(NDView x, NDView par) { - NDArray y({x.shape(0)}, 0); - for (ssize_t i = 0; i < x.size(); i++) { - y(i) = gaus(x(i), par.data()); - } - return y; -} - -double pol1(const double x, const double *par) { return par[0] * x + par[1]; } - -NDArray pol1(NDView x, NDView par) { - NDArray y({x.shape()}, 0); - for (ssize_t i = 0; i < x.size(); i++) { - y(i) = pol1(x(i), par.data()); - } - return y; -} - -double scurve(const double x, const double *par) { - return (par[0] + par[1] * x) + - 0.5 * (1 + erf((x - par[2]) / (sqrt(2) * par[3]))) * - (par[4] + par[5] * (x - par[2])); -} - -NDArray scurve(NDView x, NDView par) { - NDArray y({x.shape()}, 0); - for (ssize_t i = 0; i < x.size(); i++) { - y(i) = scurve(x(i), par.data()); - } - return y; -} - -double scurve2(const double x, const double *par) { - return (par[0] + par[1] * x) + - 0.5 * (1 - erf((x - par[2]) / (sqrt(2) * par[3]))) * - (par[4] + par[5] * (x - par[2])); -} - -NDArray scurve2(NDView x, NDView par) { - NDArray y({x.shape()}, 0); - for (ssize_t i = 0; i < x.size(); i++) { - y(i) = scurve2(x(i), par.data()); - } - return y; -} - -} // namespace func - -NDArray fit_gaus(NDView x, NDView y) { - NDArray result = model::Gaussian::estimate_par(x, y); - lm_status_struct status; - - lmcurve(result.size(), result.data(), x.size(), x.data(), y.data(), - aare::func::gaus, &lm_control_double, &status); - - return result; -} - -NDArray fit_gaus(NDView x, NDView y, - int n_threads) { - NDArray result({y.shape(0), y.shape(1), 3}, 0); - - auto process = [&x, &y, &result](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView values(&y(row, col, 0), {y.shape(2)}); - auto res = fit_gaus(x, values); - result(row, col, 0) = res(0); - result(row, col, 1) = res(1); - result(row, col, 2) = res(2); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); - return result; -} - -void fit_gaus(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - double &chi2) { - - // Check that we have the correct sizes - if (y.size() != x.size() || y.size() != y_err.size() || - par_out.size() != 3 || par_err_out.size() != 3) { - throw std::runtime_error("Data, x, data_err must have the same size " - "and par_out, par_err_out must have size 3"); - } - - // /* Collection of output parameters for status info. */ - // typedef struct { - // double fnorm; /* norm of the residue vector fvec. */ - // int nfev; /* actual number of iterations. */ - // int outcome; /* Status indicator. Nonnegative values are used as - // index - // for the message text lm_infmsg, set in lmmin.c. */ - // int userbreak; /* Set when function evaluation requests termination. - // */ - // } lm_status_struct; - - lm_status_struct status; - par_out = model::Gaussian::estimate_par(x, y); - std::array cov{0, 0, 0, 0, 0, 0, 0, 0, 0}; - - // void lmcurve2( const int n_par, double *par, double *parerr, double - // *covar, const int m_dat, const double *t, const double *y, const double - // *dy, double (*f)( const double ti, const double *par ), const - // lm_control_struct *control, lm_status_struct *status); n_par - Number of - // free variables. Length of parameter vector par. par - Parameter vector. - // On input, it must contain a reasonable guess. On output, it contains the - // solution found to minimize ||r||. parerr - Parameter uncertainties - // vector. Array of length n_par or NULL. On output, unless it or covar is - // NULL, it contains the weighted parameter uncertainties for the found - // parameters. covar - Covariance matrix. Array of length n_par * n_par or - // NULL. On output, unless it is NULL, it contains the covariance matrix. - // m_dat - Number of data points. Length of vectors t, y, dy. Must statisfy - // n_par <= m_dat. t - Array of length m_dat. Contains the abcissae (time, - // or "x") for which function f will be evaluated. y - Array of length - // m_dat. Contains the ordinate values that shall be fitted. dy - Array of - // length m_dat. Contains the standard deviations of the values y. f - A - // user-supplied parametric function f(ti;par). control - Parameter - // collection for tuning the fit procedure. In most cases, the default - // &lm_control_double is adequate. If f is only computed with - // single-precision accuracy, &lm_control_float should be used. Parameters - // are explained in lmmin2(3). status - A record used to return information - // about the minimization process: For details, see lmmin2(3). - - lmcurve2(par_out.size(), par_out.data(), par_err_out.data(), cov.data(), - x.size(), x.data(), y.data(), y_err.data(), aare::func::gaus, - &lm_control_double, &status); - - // Calculate chi2 - chi2 = 0; - for (ssize_t i = 0; i < y.size(); i++) { - chi2 += - std::pow((y(i) - func::gaus(x(i), par_out.data())) / y_err(i), 2); - } -} - -void fit_gaus(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - NDView chi2_out, - - int n_threads) { - - auto process = [&](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView y_view(&y(row, col, 0), {y.shape(2)}); - NDView y_err_view(&y_err(row, col, 0), - {y_err.shape(2)}); - NDView par_out_view(&par_out(row, col, 0), - {par_out.shape(2)}); - NDView par_err_out_view(&par_err_out(row, col, 0), - {par_err_out.shape(2)}); - - fit_gaus(x, y_view, y_err_view, par_out_view, par_err_out_view, - chi2_out(row, col)); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); -} - -void fit_pol1(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - double &chi2) { - - // Check that we have the correct sizes - if (y.size() != x.size() || y.size() != y_err.size() || - par_out.size() != 2 || par_err_out.size() != 2) { - throw std::runtime_error("Data, x, data_err must have the same size " - "and par_out, par_err_out must have size 2"); - } - - lm_status_struct status; - par_out = model::Pol1::estimate_par(x, y); - std::array cov{0, 0, 0, 0}; - - lmcurve2(par_out.size(), par_out.data(), par_err_out.data(), cov.data(), - x.size(), x.data(), y.data(), y_err.data(), aare::func::pol1, - &lm_control_double, &status); - - // Calculate chi2 - chi2 = 0; - for (ssize_t i = 0; i < y.size(); i++) { - chi2 += - std::pow((y(i) - func::pol1(x(i), par_out.data())) / y_err(i), 2); - } -} - -void fit_pol1(NDView x, NDView y, NDView y_err, - NDView par_out, NDView par_err_out, - NDView chi2_out, int n_threads) { - - auto process = [&](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView y_view(&y(row, col, 0), {y.shape(2)}); - NDView y_err_view(&y_err(row, col, 0), - {y_err.shape(2)}); - NDView par_out_view(&par_out(row, col, 0), - {par_out.shape(2)}); - NDView par_err_out_view(&par_err_out(row, col, 0), - {par_err_out.shape(2)}); - - fit_pol1(x, y_view, y_err_view, par_out_view, par_err_out_view, - chi2_out(row, col)); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); -} - -NDArray fit_pol1(NDView x, NDView y) { - // // Check that we have the correct sizes - // if (y.size() != x.size() || y.size() != y_err.size() || - // par_out.size() != 2 || par_err_out.size() != 2) { - // throw std::runtime_error("Data, x, data_err must have the same size " - // "and par_out, par_err_out must have size 2"); - // } - NDArray par = model::Pol1::estimate_par(x, y); - - lm_status_struct status; - lmcurve(par.size(), par.data(), x.size(), x.data(), y.data(), - aare::func::pol1, &lm_control_double, &status); - - return par; -} - -NDArray fit_pol1(NDView x, NDView y, - int n_threads) { - NDArray result({y.shape(0), y.shape(1), 2}, 0); - - auto process = [&](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView values(&y(row, col, 0), {y.shape(2)}); - auto res = fit_pol1(x, values); - result(row, col, 0) = res(0); - result(row, col, 1) = res(1); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - - RunInParallel(process, tasks); - return result; -} - -// ~~ S-CURVES ~~ - -// - No error -NDArray fit_scurve(NDView x, NDView y) { - NDArray result = model::RisingScurve::estimate_par(x, y); - lm_status_struct status; - - lmcurve(result.size(), result.data(), x.size(), x.data(), y.data(), - aare::func::scurve, &lm_control_double, &status); - - return result; -} - -NDArray fit_scurve(NDView x, NDView y, - int n_threads) { - NDArray result({y.shape(0), y.shape(1), 6}, 0); - - auto process = [&x, &y, &result](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView values(&y(row, col, 0), {y.shape(2)}); - auto res = fit_scurve(x, values); - result(row, col, 0) = res(0); - result(row, col, 1) = res(1); - result(row, col, 2) = res(2); - result(row, col, 3) = res(3); - result(row, col, 4) = res(4); - result(row, col, 5) = res(5); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); - return result; -} - -// - Error -void fit_scurve(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, double &chi2) { - - // Check that we have the correct sizes - if (y.size() != x.size() || y.size() != y_err.size() || - par_out.size() != 6 || par_err_out.size() != 6) { - throw std::runtime_error("Data, x, data_err must have the same size " - "and par_out, par_err_out must have size 6"); - } - - lm_status_struct status; - par_out = model::RisingScurve::estimate_par(x, y); - std::array cov = {0}; // size 6x6 - // std::array cov{0, 0, 0, 0}; - - lmcurve2(par_out.size(), par_out.data(), par_err_out.data(), cov.data(), - x.size(), x.data(), y.data(), y_err.data(), aare::func::scurve, - &lm_control_double, &status); - - // Calculate chi2 - chi2 = 0; - for (ssize_t i = 0; i < y.size(); i++) { - chi2 += - std::pow((y(i) - func::pol1(x(i), par_out.data())) / y_err(i), 2); - } -} - -void fit_scurve(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, NDView chi2_out, - int n_threads) { - - auto process = [&](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView y_view(&y(row, col, 0), {y.shape(2)}); - NDView y_err_view(&y_err(row, col, 0), - {y_err.shape(2)}); - NDView par_out_view(&par_out(row, col, 0), - {par_out.shape(2)}); - NDView par_err_out_view(&par_err_out(row, col, 0), - {par_err_out.shape(2)}); - - fit_scurve(x, y_view, y_err_view, par_out_view, - par_err_out_view, chi2_out(row, col)); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); -} - -// SCURVE2 --- - -// - No error -NDArray fit_scurve2(NDView x, NDView y) { - NDArray result = model::FallingScurve::estimate_par(x, y); - lm_status_struct status; - - lmcurve(result.size(), result.data(), x.size(), x.data(), y.data(), - aare::func::scurve2, &lm_control_double, &status); - - return result; -} - -NDArray fit_scurve2(NDView x, NDView y, - int n_threads) { - NDArray result({y.shape(0), y.shape(1), 6}, 0); - - auto process = [&x, &y, &result](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView values(&y(row, col, 0), {y.shape(2)}); - auto res = fit_scurve2(x, values); - result(row, col, 0) = res(0); - result(row, col, 1) = res(1); - result(row, col, 2) = res(2); - result(row, col, 3) = res(3); - result(row, col, 4) = res(4); - result(row, col, 5) = res(5); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); - return result; -} - -// - Error -void fit_scurve2(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, double &chi2) { - - // Check that we have the correct sizes - if (y.size() != x.size() || y.size() != y_err.size() || - par_out.size() != 6 || par_err_out.size() != 6) { - throw std::runtime_error("Data, x, data_err must have the same size " - "and par_out, par_err_out must have size 6"); - } - - lm_status_struct status; - par_out = model::FallingScurve::estimate_par(x, y); - std::array cov = {0}; // size 6x6 - // std::array cov{0, 0, 0, 0}; - - lmcurve2(par_out.size(), par_out.data(), par_err_out.data(), cov.data(), - x.size(), x.data(), y.data(), y_err.data(), aare::func::scurve2, - &lm_control_double, &status); - - // Calculate chi2 - chi2 = 0; - for (ssize_t i = 0; i < y.size(); i++) { - chi2 += - std::pow((y(i) - func::pol1(x(i), par_out.data())) / y_err(i), 2); - } -} - -void fit_scurve2(NDView x, NDView y, - NDView y_err, NDView par_out, - NDView par_err_out, NDView chi2_out, - int n_threads) { - - auto process = [&](ssize_t first_row, ssize_t last_row) { - for (ssize_t row = first_row; row < last_row; row++) { - for (ssize_t col = 0; col < y.shape(1); col++) { - NDView y_view(&y(row, col, 0), {y.shape(2)}); - NDView y_err_view(&y_err(row, col, 0), - {y_err.shape(2)}); - NDView par_out_view(&par_out(row, col, 0), - {par_out.shape(2)}); - NDView par_err_out_view(&par_err_out(row, col, 0), - {par_err_out.shape(2)}); - - fit_scurve2(x, y_view, y_err_view, par_out_view, - par_err_out_view, chi2_out(row, col)); - } - } - }; - - auto tasks = split_task(0, y.shape(0), n_threads); - RunInParallel(process, tasks); -} - // ============================================================================ // FitModel — method definitions // (constructor, destructor, copy, and all methods that touch Minuit2 state) @@ -756,4 +305,4 @@ AARE_INSTANTIATE_FIT(model::FallingScurve) #undef AARE_INSTANTIATE_FIT // NOLINTEND -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/src/Fit.test.cpp b/src/Fit.test.cpp new file mode 100644 index 00000000..2b0fd1bb --- /dev/null +++ b/src/Fit.test.cpp @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "aare/Fit.hpp" +#include "aare/FitModel.hpp" +#include "aare/Models.hpp" + +#include +#include +#include + +namespace { + +constexpr ssize_t n_points = 61; + +void fill_gaussian(aare::NDArray &x, aare::NDArray &y, + double amplitude, double mean, double sigma) { + for (ssize_t i = 0; i < n_points; ++i) { + x(i) = -6.0 + 0.2 * static_cast(i); + const double z = (x(i) - mean) / sigma; + y(i) = amplitude * std::exp(-0.5 * z * z); + } +} + +} // namespace + +TEST_CASE("Minuit2 fits weighted and unweighted Gaussian data", "[fit]") { + aare::NDArray x({n_points}); + aare::NDArray y({n_points}); + aare::NDArray y_err({n_points}, 1.0); + fill_gaussian(x, y, 120.0, 0.8, 1.3); + + const aare::FitModel unweighted_model; + const auto unweighted = + aare::fit_pixel(unweighted_model, x.view(), y.view()); + + REQUIRE(unweighted.size() == 4); + CHECK(unweighted(0) == Catch::Approx(120.0).epsilon(1e-5)); + CHECK(unweighted(1) == Catch::Approx(0.8).epsilon(1e-5)); + CHECK(unweighted(2) == Catch::Approx(1.3).epsilon(1e-5)); + CHECK(unweighted(3) == Catch::Approx(0.0).margin(1e-4)); + + const aare::FitModel weighted_model(0, 100, 0.5, + true); + const auto weighted = + aare::fit_pixel(weighted_model, x.view(), y.view(), y_err.view()); + + REQUIRE(weighted.size() == 7); + CHECK(weighted(0) == Catch::Approx(120.0).epsilon(1e-5)); + CHECK(weighted(1) == Catch::Approx(0.8).epsilon(1e-5)); + CHECK(weighted(2) == Catch::Approx(1.3).epsilon(1e-5)); + CHECK(weighted(6) == Catch::Approx(0.0).margin(1e-4)); +} + +TEST_CASE("Minuit2 fits a Gaussian data cube in parallel", "[fit]") { + aare::NDArray x({n_points}); + aare::NDArray values({n_points}); + fill_gaussian(x, values, 80.0, -0.6, 0.9); + + aare::NDArray y({2, 2, n_points}); + for (ssize_t row = 0; row < 2; ++row) { + for (ssize_t col = 0; col < 2; ++col) { + for (ssize_t i = 0; i < n_points; ++i) + y(row, col, i) = values(i); + } + } + + aare::NDArray par({2, 2, 3}); + aare::NDArray chi2({2, 2}); + const aare::FitModel model; + + aare::fit_3d(model, x.view(), y.view(), aare::NDView{}, + par.view(), aare::NDView{}, chi2.view(), 2); + + for (ssize_t row = 0; row < 2; ++row) { + for (ssize_t col = 0; col < 2; ++col) { + CHECK(par(row, col, 0) == Catch::Approx(80.0).epsilon(1e-5)); + CHECK(par(row, col, 1) == Catch::Approx(-0.6).epsilon(1e-5)); + CHECK(par(row, col, 2) == Catch::Approx(0.9).epsilon(1e-5)); + CHECK(chi2(row, col) == Catch::Approx(0.0).margin(1e-8)); + } + } +} From aa39470686732cb036a900166645b94234e620db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Fri, 14 Aug 2026 11:11:43 +0200 Subject: [PATCH 03/10] Removed unused DynamicCluster (#346) - Removed unused DynamicCluster - Removed commented out references to ClusterFileV2 --- include/aare/defs.hpp | 81 ---------------------------------- python/src/ctb_raw_file.hpp | 1 - python/src/file.hpp | 51 +-------------------- python/src/raw_master_file.hpp | 1 - python/src/raw_sub_file.hpp | 1 - python/src/var_cluster.hpp | 6 --- src/defs.test.cpp | 14 ------ 7 files changed, 1 insertion(+), 154 deletions(-) diff --git a/include/aare/defs.hpp b/include/aare/defs.hpp index af73a84d..f810f411 100644 --- a/include/aare/defs.hpp +++ b/include/aare/defs.hpp @@ -38,87 +38,6 @@ inline constexpr size_t bits_per_byte = 8; void assert_failed(const std::string &msg); -class DynamicCluster { - public: - int cluster_sizeX; - int cluster_sizeY; - int16_t x; - int16_t y; - Dtype dt; // 4 bytes - - private: - std::byte *m_data; - - public: - DynamicCluster(int cluster_sizeX_, int cluster_sizeY_, - Dtype dt_ = Dtype(typeid(int32_t))) - : cluster_sizeX(cluster_sizeX_), cluster_sizeY(cluster_sizeY_), - dt(dt_) { - m_data = new std::byte[cluster_sizeX * cluster_sizeY * dt.bytes()]{}; - } - DynamicCluster() : DynamicCluster(3, 3) {} - DynamicCluster(const DynamicCluster &other) - : DynamicCluster(other.cluster_sizeX, other.cluster_sizeY, other.dt) { - if (this == &other) - return; - x = other.x; - y = other.y; - memcpy(m_data, other.m_data, other.bytes()); - } - DynamicCluster &operator=(const DynamicCluster &other) { - if (this == &other) - return *this; - this->~DynamicCluster(); - new (this) DynamicCluster(other); - return *this; - } - DynamicCluster(DynamicCluster &&other) noexcept - : cluster_sizeX(other.cluster_sizeX), - cluster_sizeY(other.cluster_sizeY), x(other.x), y(other.y), - dt(other.dt), m_data(other.m_data) { - other.m_data = nullptr; - other.dt = Dtype(Dtype::TypeIndex::ERROR); - } - ~DynamicCluster() { delete[] m_data; } - template T get(int idx) { - (sizeof(T) == dt.bytes()) - ? 0 - : throw std::invalid_argument("[ERROR] Type size mismatch"); - return *reinterpret_cast(m_data + idx * dt.bytes()); - } - template auto set(int idx, T val) { - (sizeof(T) == dt.bytes()) - ? 0 - : throw std::invalid_argument("[ERROR] Type size mismatch"); - return memcpy(m_data + idx * dt.bytes(), &val, dt.bytes()); - } - - template std::string to_string() const { - (sizeof(T) == dt.bytes()) - ? 0 - : throw std::invalid_argument("[ERROR] Type size mismatch"); - std::string s = "x: " + std::to_string(x) + " y: " + std::to_string(y) + - "\nm_data: ["; - for (int i = 0; i < cluster_sizeX * cluster_sizeY; i++) { - s += std::to_string( - *reinterpret_cast(m_data + i * dt.bytes())) + - " "; - } - s += "]"; - return s; - } - /** - * @brief size of the cluster in bytes when saved to a file - */ - size_t size() const { return cluster_sizeX * cluster_sizeY; } - size_t bytes() const { return cluster_sizeX * cluster_sizeY * dt.bytes(); } - auto begin() const { return m_data; } - auto end() const { - return m_data + cluster_sizeX * cluster_sizeY * dt.bytes(); - } - std::byte *data() { return m_data; } -}; - /** * @brief header contained in parts of frames */ diff --git a/python/src/ctb_raw_file.hpp b/python/src/ctb_raw_file.hpp index 5570254a..5a6953b7 100644 --- a/python/src/ctb_raw_file.hpp +++ b/python/src/ctb_raw_file.hpp @@ -9,7 +9,6 @@ #include "aare/decode.hpp" #include "aare/defs.hpp" -// #include "aare/fClusterFileV2.hpp" #include "np_helper.hpp" diff --git a/python/src/file.hpp b/python/src/file.hpp index b6026730..ccb62d1f 100644 --- a/python/src/file.hpp +++ b/python/src/file.hpp @@ -7,7 +7,6 @@ #include "aare/RawSubFile.hpp" #include "aare/defs.hpp" -// #include "aare/fClusterFileV2.hpp" #include #include @@ -195,52 +194,4 @@ void define_file_io_bindings(py::module &m) { }); #pragma GCC diagnostic pop - // py::class_(m, "ClusterHeader") - // .def(py::init<>()) - // .def_readwrite("frame_number", &ClusterHeader::frame_number) - // .def_readwrite("n_clusters", &ClusterHeader::n_clusters) - // .def("__repr__", [](const ClusterHeader &a) { return ""; }); - - // py::class_(m, "ClusterV2_") - // .def(py::init<>()) - // .def_readwrite("x", &ClusterV2_::x) - // .def_readwrite("y", &ClusterV2_::y) - // .def_readwrite("data", &ClusterV2_::data) - // .def("__repr__", [](const ClusterV2_ &a) { return ""; }); - - // py::class_(m, "ClusterV2") - // .def(py::init<>()) - // .def_readwrite("cluster", &ClusterV2::cluster) - // .def_readwrite("frame_number", &ClusterV2::frame_number) - // .def("__repr__", [](const ClusterV2 &a) { return ""; }); - - // py::class_(m, "ClusterFileV2") - // .def(py::init()) - // .def("read", py::overload_cast<>(&ClusterFileV2::read)) - // .def("read", py::overload_cast(&ClusterFileV2::read)) - // .def("frame_number", &ClusterFileV2::frame_number) - // .def("write", py::overload_cast const - // &>(&ClusterFileV2::write)) - - // .def("close", &ClusterFileV2::close); - - // m.def("to_clustV2", [](std::vector &clusters, const int - // frame_number) { - // std::vector clusters_; - // for (auto &c : clusters) { - // ClusterV2 cluster; - // cluster.cluster.x = c.x; - // cluster.cluster.y = c.y; - // int i=0; - // for(auto &d : cluster.cluster.data) { - // d=c.get(i++); - // } - // cluster.frame_number = frame_number; - // clusters_.push_back(cluster); - // } - // return clusters_; - // }); -} +} \ No newline at end of file diff --git a/python/src/raw_master_file.hpp b/python/src/raw_master_file.hpp index b67720b2..7f1837c0 100644 --- a/python/src/raw_master_file.hpp +++ b/python/src/raw_master_file.hpp @@ -8,7 +8,6 @@ #include "aare/RawSubFile.hpp" #include "aare/defs.hpp" -// #include "aare/fClusterFileV2.hpp" #include #include diff --git a/python/src/raw_sub_file.hpp b/python/src/raw_sub_file.hpp index 6e2b6ca1..5359c054 100644 --- a/python/src/raw_sub_file.hpp +++ b/python/src/raw_sub_file.hpp @@ -7,7 +7,6 @@ #include "aare/RawSubFile.hpp" #include "aare/defs.hpp" -// #include "aare/fClusterFileV2.hpp" #include #include diff --git a/python/src/var_cluster.hpp b/python/src/var_cluster.hpp index 3437acd5..44d70db1 100644 --- a/python/src/var_cluster.hpp +++ b/python/src/var_cluster.hpp @@ -1,17 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 #include "aare/VarClusterFinder.hpp" #include "np_helper.hpp" -// #include "aare/defs.hpp" -// #include "aare/fClusterFileV2.hpp" #include -// #include #include -// #include #include #include -// #include -// #include namespace py = pybind11; using namespace ::aare; diff --git a/src/defs.test.cpp b/src/defs.test.cpp index af7902ad..815d4321 100644 --- a/src/defs.test.cpp +++ b/src/defs.test.cpp @@ -22,20 +22,6 @@ TEST_CASE("Enum values") { REQUIRE(static_cast(aare::DetectorType::Moench03) == 100); } -TEST_CASE("DynamicCluster creation") { - aare::DynamicCluster c(13, 15); - REQUIRE(c.cluster_sizeX == 13); - REQUIRE(c.cluster_sizeY == 15); - REQUIRE(c.dt == aare::Dtype(typeid(int32_t))); - REQUIRE(c.data() != nullptr); - - aare::DynamicCluster c2(c); - REQUIRE(c2.cluster_sizeX == 13); - REQUIRE(c2.cluster_sizeY == 15); - REQUIRE(c2.dt == aare::Dtype(typeid(int32_t))); - REQUIRE(c2.data() != nullptr); -} - TEST_CASE("Basic ops on BitOffset") { REQUIRE_THROWS(aare::BitOffset(10)); From af25aea684d6bd5b481416d024baa775620550a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Wed, 19 Aug 2026 12:34:37 +0200 Subject: [PATCH 04/10] Fixed bug in reading analog samples for (j.at("Analog Flag").get()); - if (m_analog_flag) { - m_analog_samples = j.at("Analog Samples"); + if (m_type == DetectorType::Moench) { + if (auto it = j.find("Analog Samples"); it != j.end()) { + m_analog_flag = true; + m_analog_samples = it->get(); + } else { + m_analog_flag = false; + } + } else { + if (auto it = j.find("Analog Flag"); it != j.end()) { + m_analog_flag = static_cast(it->get()); + if (m_analog_flag) { + m_analog_samples = j.at("Analog Samples"); + } + } else { + m_analog_flag = false; } - } catch (const json::out_of_range &e) { - // keep the optional empty } //----------------------------------------------------------------- + try { m_quad = j.at("Quad"); } catch (const json::out_of_range &e) { diff --git a/src/RawMasterFile.test.cpp b/src/RawMasterFile.test.cpp index 5d077ce9..59efed71 100644 --- a/src/RawMasterFile.test.cpp +++ b/src/RawMasterFile.test.cpp @@ -411,6 +411,10 @@ TEST_CASE("Parse EIGER 7.2 master from string stream") { REQUIRE(f.frame_padding() == 1); REQUIRE(f.total_frames_expected() == 3); + REQUIRE(f.quad() == 0); + REQUIRE(f.number_of_rows() == 256); + REQUIRE(f.n_modules() == 4); + REQUIRE(f.bitdepth() == 32); REQUIRE(f.frames_in_file() == 3); @@ -489,9 +493,473 @@ TEST_CASE("Parse JUNGFRAU 7.2 master from string stream") { REQUIRE(f.number_of_rows() == 512); REQUIRE(f.frames_in_file() == 10); + REQUIRE(f.quad() == 0); + REQUIRE(f.n_modules() == 2); REQUIRE(f.udp_interfaces_per_module() == xy{2, 1}); } +TEST_CASE( + "Parse CTB 7.2 master (SW 7.0.3) with analog samples from string stream") { + std::string master_content = R"({ + "Version": 7.2, + "Timestamp": "Wed Aug 19 09:45:29 2026", + "Detector Type": "ChipTestBoard", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 192000, + "Pixels": { + "x": 32, + "y": 1 + }, + "Max Frames Per File": 20000, + "Frame Discard Policy": "nodiscard", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "0ns", + "Period": "1ms", + "Ten Giga": 0, + "ADC Mask": "0xffffffff", + "Analog Flag": 1, + "Analog Samples": 3000, + "Digital Flag": 0, + "Digital Samples": 2000, + "Dbit Offset": 0, + "Dbit Bitset": 0, + "Frames in File": 1, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +})"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.2"); + REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.image_size_in_bytes() == 192000); + REQUIRE(f.pixels_x() == 32); + REQUIRE(f.pixels_y() == 1); + REQUIRE(f.max_frames_per_file() == 20000); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.total_frames_expected() == 1); + REQUIRE(f.exptime() == std::chrono::nanoseconds(0)); + REQUIRE(f.period() == std::chrono::milliseconds(1)); + REQUIRE(f.analog_samples() == 3000); + REQUIRE(f.digital_samples() == std::nullopt); + REQUIRE(f.transceiver_samples() == std::nullopt); + REQUIRE(f.frames_in_file() == 1); +} + +TEST_CASE( + "Parse CTB 7.2 master (SW 7.0.3) with digital samples from string stream") { + std::string master_content = R"({ + "Version": 7.2, + "Timestamp": "Wed Aug 19 09:48:34 2026", + "Detector Type": "ChipTestBoard", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 16000, + "Pixels": { + "x": 64, + "y": 1 + }, + "Max Frames Per File": 20000, + "Frame Discard Policy": "nodiscard", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "0ns", + "Period": "1ms", + "Ten Giga": 0, + "ADC Mask": "0xffffffff", + "Analog Flag": 0, + "Analog Samples": 3000, + "Digital Flag": 1, + "Digital Samples": 2000, + "Dbit Offset": 0, + "Dbit Bitset": 0, + "Frames in File": 1, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +})"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.2"); + REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.image_size_in_bytes() == 16000); + REQUIRE(f.pixels_x() == 64); + REQUIRE(f.pixels_y() == 1); + REQUIRE(f.max_frames_per_file() == 20000); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.total_frames_expected() == 1); + REQUIRE(f.exptime() == std::chrono::nanoseconds(0)); + REQUIRE(f.period() == std::chrono::milliseconds(1)); + REQUIRE(f.analog_samples() == std::nullopt); + REQUIRE(f.digital_samples() == 2000); + REQUIRE(f.transceiver_samples() == std::nullopt); + REQUIRE(f.frames_in_file() == 1); +} + +TEST_CASE("Parse Moench 7.2 master (SW 7.0.3) from string stream") { + std::string master_content = R"({ + "Version": 7.2, + "Timestamp": "Wed Aug 19 10:32:06 2026", + "Detector Type": "Moench", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 320000, + "Pixels": { + "x": 400, + "y": 400 + }, + "Max Frames Per File": 100000, + "Frame Discard Policy": "nodiscard", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "20us", + "Period": "2ms", + "Ten Giga": 0, + "ADC Mask": "0xffffffff", + "Analog Samples": 5000, + "Frames in File": 1, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +})"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.2"); + REQUIRE(f.detector_type() == DetectorType::Moench03_old); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.image_size_in_bytes() == 320000); + REQUIRE(f.pixels_x() == 400); + REQUIRE(f.pixels_y() == 400); + REQUIRE(f.max_frames_per_file() == 100000); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.total_frames_expected() == 1); + REQUIRE(f.exptime() == std::chrono::microseconds(20)); + REQUIRE(f.period() == std::chrono::milliseconds(2)); + REQUIRE(f.analog_samples() == 5000); + REQUIRE(f.digital_samples() == std::nullopt); + REQUIRE(f.transceiver_samples() == std::nullopt); + REQUIRE(f.frames_in_file() == 1); +} + +TEST_CASE("Parse Moench 7.2 master (SW 8.0.0) from string stream") { + std::string master_content = R"({ + "Version": 7.2, + "Timestamp": "Wed Aug 19 09:54:53 2026", + "Detector Type": "Moench", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 320000, + "Pixels": { + "x": 400, + "y": 400 + }, + "Max Frames Per File": 100000, + "Frame Discard Policy": "discardpartial", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "10us", + "Period": "2ms", + "Number of UDP Interfaces": 1, + "Number of rows": 400, + "Frames in File": 1, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +})"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.2"); + REQUIRE(f.detector_type() == DetectorType::Moench03); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.image_size_in_bytes() == 320000); + REQUIRE(f.pixels_x() == 400); + REQUIRE(f.pixels_y() == 400); + REQUIRE(f.max_frames_per_file() == 100000); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::DiscardPartial); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.total_frames_expected() == 1); + REQUIRE(f.exptime() == std::chrono::microseconds(10)); + REQUIRE(f.period() == std::chrono::milliseconds(2)); + REQUIRE(f.number_of_rows() == 400); + REQUIRE(f.frames_in_file() == 1); + REQUIRE(f.analog_samples() == std::nullopt); + REQUIRE(f.digital_samples() == std::nullopt); + REQUIRE(f.transceiver_samples() == std::nullopt); + REQUIRE(f.udp_interfaces_per_module() == xy{1, 1}); +} + +TEST_CASE("Parse CTB 7.2 master (SW 8.0.0) from string stream") { + std::string master_content = R"({ + "Version": 7.2, + "Timestamp": "Wed Aug 19 09:56:30 2026", + "Detector Type": "ChipTestBoard", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 192000, + "Pixels": { + "x": 32, + "y": 1 + }, + "Max Frames Per File": 20000, + "Frame Discard Policy": "nodiscard", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "0ns", + "Period": "1ms", + "Ten Giga": 0, + "ADC Mask": "0xffffffff", + "Analog Flag": 1, + "Analog Samples": 3000, + "Digital Flag": 0, + "Digital Samples": 2000, + "Dbit Offset": 0, + "Dbit Bitset": 0, + "Transceiver Mask": "0x3", + "Transceiver Flag": 0, + "Transceiver Samples": 1, + "Frames in File": 1, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +})"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.2"); + REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.image_size_in_bytes() == 192000); + REQUIRE(f.pixels_x() == 32); + REQUIRE(f.pixels_y() == 1); + REQUIRE(f.max_frames_per_file() == 20000); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.total_frames_expected() == 1); + REQUIRE(f.exptime() == std::chrono::nanoseconds(0)); + REQUIRE(f.period() == std::chrono::milliseconds(1)); + REQUIRE(f.analog_samples() == 3000); + REQUIRE(f.digital_samples() == std::nullopt); + REQUIRE(f.transceiver_samples() == std::nullopt); + REQUIRE(f.frames_in_file() == 1); +} + +TEST_CASE( + "Parse CTB 7.2 master (SW 8.0.0) with digital samples from string stream") { + std::string master_content = R"({ + "Version": 7.2, + "Timestamp": "Wed Aug 19 09:58:22 2026", + "Detector Type": "ChipTestBoard", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 16000, + "Pixels": { + "x": 64, + "y": 1 + }, + "Max Frames Per File": 20000, + "Frame Discard Policy": "nodiscard", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "0ns", + "Period": "1ms", + "Ten Giga": 0, + "ADC Mask": "0xffffffff", + "Analog Flag": 0, + "Analog Samples": 3000, + "Digital Flag": 1, + "Digital Samples": 2000, + "Dbit Offset": 0, + "Dbit Bitset": 0, + "Transceiver Mask": "0x3", + "Transceiver Flag": 0, + "Transceiver Samples": 1, + "Frames in File": 1, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +})"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.2"); + REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.image_size_in_bytes() == 16000); + REQUIRE(f.pixels_x() == 64); + REQUIRE(f.pixels_y() == 1); + REQUIRE(f.max_frames_per_file() == 20000); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.total_frames_expected() == 1); + REQUIRE(f.exptime() == std::chrono::nanoseconds(0)); + REQUIRE(f.period() == std::chrono::milliseconds(1)); + REQUIRE(f.analog_samples() == std::nullopt); + REQUIRE(f.digital_samples() == 2000); + REQUIRE(f.transceiver_samples() == std::nullopt); + REQUIRE(f.frames_in_file() == 1); +} + TEST_CASE("Parse a CTB file from stream") { std::string master_content = R"({ "Version": 8.0, @@ -738,4 +1206,83 @@ TEST_CASE("Parse a v7.1 Mythen3 from stream") { // Period is ok though REQUIRE(f.period() == std::chrono::milliseconds(2)); -} \ No newline at end of file +} + +TEST_CASE("Parse old Moench03 from stream") { + std::string master_content = R"( + { + "Version": 7.1, + "Timestamp": "Mon Mar 25 10:07:02 2024", + "Detector Type": "Moench", + "Timing Mode": "auto", + "Geometry": { + "x": 1, + "y": 1 + }, + "Image Size in bytes": 320000, + "Pixels": { + "x": 400, + "y": 400 + }, + "Max Frames Per File": 100000, + "Frame Discard Policy": "discardpartial", + "Frame Padding": 1, + "Scan Parameters": "[disabled]", + "Total Frames": 1000000, + "Receiver Roi": { + "xmin": 4294967295, + "xmax": 4294967295, + "ymin": 4294967295, + "ymax": 4294967295 + }, + "Exptime": "50us", + "Period": "600us", + "Ten Giga": 1, + "ADC Mask": "0xffffffff", + "Analog Samples": 5000, + "Additional Json Header": "{detectorMode: analog, frameMode: newPedestal}", + "Frames in File": 999995, + "Frame Header Format": { + "Frame Number": "8 bytes", + "SubFrame Number/ExpLength": "4 bytes", + "Packet Number": "4 bytes", + "Bunch ID": "8 bytes", + "Timestamp": "8 bytes", + "Module Id": "2 bytes", + "Row": "2 bytes", + "Column": "2 bytes", + "Reserved": "2 bytes", + "Debug": "4 bytes", + "Round Robin Number": "2 bytes", + "Detector Type": "1 byte", + "Header Version": "1 byte", + "Packets Caught Mask": "64 bytes" + } +} + +)"; + + std::istringstream iss(master_content); + RawMasterFile f(iss, "test_master_0.json"); + + REQUIRE(f.version() == "7.1"); + REQUIRE(f.detector_type() == DetectorType::Moench03_old); + REQUIRE(f.timing_mode() == TimingMode::Auto); + REQUIRE(f.geometry().col == 1); + REQUIRE(f.geometry().row == 1); + REQUIRE(f.image_size_in_bytes() == 320000); + REQUIRE(f.pixels_x() == 400); + REQUIRE(f.pixels_y() == 400); + REQUIRE(f.max_frames_per_file() == 100000); + REQUIRE((f.total_frames_expected() == + 1000000)); // This is Total Frames in the master file + REQUIRE(f.frames_in_file() == 999995); + REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::DiscardPartial); + REQUIRE(f.frame_padding() == 1); + REQUIRE(f.n_modules() == 1); + REQUIRE(f.quad() == 0); + REQUIRE(f.bitdepth() == 16); + REQUIRE(f.exptime() == std::chrono::microseconds(50)); + REQUIRE(f.period() == std::chrono::microseconds(600)); + REQUIRE(f.analog_samples() == 5000); +} From fef57c13c906f00ff3d46004649f74aebe857059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Wed, 19 Aug 2026 16:21:37 +0200 Subject: [PATCH 05/10] added const overload for NDArray::view() (#351) - Added missing const T overload for NDArray::view() - Tests --- include/aare/NDArray.hpp | 13 +++++++------ include/aare/Pedestal.hpp | 4 ++-- include/aare/hist/PixelHistogramImpl.hpp | 4 ++-- src/NDArray.test.cpp | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/aare/NDArray.hpp b/include/aare/NDArray.hpp index 079915d6..43964028 100644 --- a/include/aare/NDArray.hpp +++ b/include/aare/NDArray.hpp @@ -506,12 +506,13 @@ class NDArray : public ArrayExpr, Ndim> { return *this; } - /** - * @brief Create a view of the NDArray. - * - * @return NDView - */ - NDView view() const { return NDView{data_, shape_}; } + /** @brief Create a mutable view of the NDArray. */ + NDView view() { return NDView{data_, shape_}; } + + /** @brief Create a read-only view of a const NDArray. */ + NDView view() const { + return NDView{data_, shape_}; + } private: /** diff --git a/include/aare/Pedestal.hpp b/include/aare/Pedestal.hpp index 6ea6a6fb..48bb6568 100644 --- a/include/aare/Pedestal.hpp +++ b/include/aare/Pedestal.hpp @@ -45,7 +45,7 @@ template class Pedestal { NDArray mean() { return m_mean; } - const NDView view() const { return m_mean.view(); } + NDView view() const { return m_mean.view(); } SUM_TYPE mean(const uint32_t row, const uint32_t col) const { return m_mean(row, col); @@ -221,4 +221,4 @@ template class Pedestal { m_mean(row, col) = m_sum(row, col) / m_samples; } }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/hist/PixelHistogramImpl.hpp b/include/aare/hist/PixelHistogramImpl.hpp index 715537f6..30b468b9 100644 --- a/include/aare/hist/PixelHistogramImpl.hpp +++ b/include/aare/hist/PixelHistogramImpl.hpp @@ -42,7 +42,7 @@ template class PixelHistogramImpl { // Zero-copy view of the underlying [rows x cols x n_bins] storage. // Lifetime is tied to *this. Use for low-level merge/stitching paths; // prefer values() for the public API where you want an owned copy. - NDView view() const; + NDView view() const; NDArray bin_centers() const; NDArray bin_edges() const; }; @@ -122,7 +122,7 @@ NDArray PixelHistogramImpl::values() const { } template -NDView PixelHistogramImpl::view() const { +NDView PixelHistogramImpl::view() const { return m_values.view(); } diff --git a/src/NDArray.test.cpp b/src/NDArray.test.cpp index e2609d27..3a3cafc0 100644 --- a/src/NDArray.test.cpp +++ b/src/NDArray.test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using aare::NDArray; using aare::NDView; @@ -71,6 +72,20 @@ TEST_CASE("Accessing a const object") { REQUIRE(img.shape(2) == 5); } +TEST_CASE("A const NDArray returns a read-only view") { + NDArray mutable_array({2, 3}, 1); + const auto &const_array = mutable_array; + + static_assert( + std::is_same_v>); + static_assert( + std::is_same_v>); + + auto view = const_array.view(); + static_assert(std::is_same_v); + REQUIRE(view(1, 2) == 1); +} + TEST_CASE("Indexing of a 2D image") { std::array shape{{3, 7}}; NDArray img(shape, 5); From 4970a92d50e29d7768563e9daa5bf0b3d26896e6 Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Thu, 20 Aug 2026 16:14:32 +0200 Subject: [PATCH 06/10] updated clang-format commands (#352) - updated custom targets clang-format, check-format, clang-tidy - find: paths must precede expression: `\' (double \\ not properly escaped in bash) --- CMakeLists.txt | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 995abd97..e044b3f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -492,9 +492,8 @@ endif() add_custom_target( check-format COMMAND - find \\ (-name "*.cpp" -o -name "*.hpp" \\) -not -path "./build/*" | xargs - -I {} -n 1 -P 10 bash -c - "clang-format -Werror -style=\"file:.clang-format\" {} | diff {} -" + bash -c + [=[ find \( -name "*.cpp" -o -name "*.hpp" \) -not -path "./build/*" | xargs -I {} -n 1 -P 10 bash -c "clang-format -Werror -style=\"file:.clang-format\" {} | diff {} -" ]=] WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Checking code formatting with clang-format" VERBATIM) @@ -502,8 +501,8 @@ add_custom_target( add_custom_target( format-files COMMAND - find \\ (-name "*.cpp" -o -name "*.hpp" \\) -not -path "./build/*" | xargs - -I {} -n 1 -P 10 bash -c "clang-format -i -style=\"file:.clang-format\" {}" + bash -c + [=[ find \( -name "*.cpp" -o -name "*.hpp" \) -not -path "./build/*" | xargs -I {} -n 1 -P 10 bash -c "clang-format -i -style=\"file:.clang-format\" {}" ]=] WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Formatting with clang-format" VERBATIM) @@ -518,18 +517,9 @@ endif() add_custom_target( clang-tidy COMMAND - find \\ (-path - "./src/*" - -a - -not - -path - "./src/python/*" - -a - \\ - (-name "*.cpp" -not -name "*.test.cpp" \\) - \\) -not -name "CircularFifo.hpp" -not -name - "ProducerConsumerQueue.hpp" -not -name "VariableSizeClusterFinder.hpp" | - xargs -I {} -n 1 -P 10 bash -c + find -path "./src/*" -not -path "./src/python/*" -not -name "*.test.cpp" + -not -name "CircularFifo.hpp" -not -name "ProducerConsumerQueue.hpp" -not + -name "VariableSizeClusterFinder.hpp" | xargs -I {} -n 1 -P 10 bash -c "${CLANG_TIDY_COMMAND} --config-file=.clang-tidy -p build {}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "linting with clang-tidy" From 26d11f74a897f880c8b25595933f91576ee2ee94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Tue, 25 Aug 2026 08:51:41 +0200 Subject: [PATCH 07/10] Fix/expand const buffer (#354) Minor API change: - NDView implicitly converts to NDView (as std::span in C++20) - expand4to8bit and expand24to32bit now takes a view over const uint8_t to show that the input is not changes closes #291 --- RELEASE.md | 3 ++- include/aare/NDView.hpp | 11 ++++++++++- include/aare/decode.hpp | 4 ++-- src/NDView.test.cpp | 17 ++++++++++++++++- src/decode.cpp | 4 ++-- src/decode.test.cpp | 37 ++++++++++++++++++++++++++++++++++++- 6 files changed, 68 insertions(+), 8 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 74d799b1..8d647df4 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -11,6 +11,8 @@ - Removed the legacy ``gaus``, ``pol1``, ``scurve``, and ``scurve2`` function evaluators. Model objects are callable and provide the replacement, for example ``Gaussian()(x, par)``. +- ``NDView`` now converts to ``NDView``; + ``expand4to8bit`` and ``expand24to32bit`` accept const input views. ### Bugfixes: - Fixed broken reading of old (pre reordering) Moench03 @@ -165,4 +167,3 @@ dhanya.thattil@psi.ch - diff --git a/include/aare/NDView.hpp b/include/aare/NDView.hpp index 8c2432d4..c507b41f 100644 --- a/include/aare/NDView.hpp +++ b/include/aare/NDView.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace aare { @@ -92,6 +93,12 @@ class NDView : public ArrayExpr, Ndim> { : buffer_(buffer), strides_(c_strides(shape)), shape_(shape), size_(num_elements(shape)) {} + template , int> = 0> + NDView(const NDView &other) noexcept + : buffer_(other.buffer_), strides_(other.strides_), + shape_(other.shape_), size_(other.size_) {} + template std::enable_if_t operator()(Ix... index) { return buffer_[element_offset(strides_, index...)]; @@ -216,6 +223,8 @@ class NDView : public ArrayExpr, Ndim> { } private: + template friend class NDView; + T *buffer_{nullptr}; std::array strides_{}; std::array shape_{}; @@ -263,4 +272,4 @@ template NDView make_view(std::vector &vec) { return NDView(vec.data(), {static_cast(vec.size())}); } -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/decode.hpp b/include/aare/decode.hpp index c3397093..a6efd187 100644 --- a/include/aare/decode.hpp +++ b/include/aare/decode.hpp @@ -35,7 +35,7 @@ uint32_t mask32to24bits(uint32_t input, BitOffset offset = {}); * @param offset Offset within the first byte to where the data starts (0-7 * bits) */ -void expand24to32bit(NDView input, NDView output, +void expand24to32bit(NDView input, NDView output, BitOffset offset = {}); /** @@ -43,7 +43,7 @@ void expand24to32bit(NDView input, NDView output, * @param input input buffer with 4 bit values packed into 8 bit * @param output output buffer with 8 bit values */ -void expand4to8bit(NDView input, NDView output); +void expand4to8bit(NDView input, NDView output); /** * @brief Apply custom weights to a 16-bit input value. Will sum up diff --git a/src/NDView.test.cpp b/src/NDView.test.cpp index b89d0cd5..1a8c6a2b 100644 --- a/src/NDView.test.cpp +++ b/src/NDView.test.cpp @@ -5,12 +5,16 @@ #include #include #include +#include #include using aare::NDView; using aare::num_elements; using aare::Shape; +static_assert(std::is_convertible_v, NDView>); +static_assert(!std::is_convertible_v, NDView>); + TEST_CASE("Calculate size from a shape") { Shape<3> shape{2, 3, 4}; REQUIRE(num_elements(shape) == 24); @@ -88,6 +92,17 @@ TEST_CASE("Element reference 1D with a const NDView") { } } +TEST_CASE("Convert a mutable NDView to a const NDView") { + std::vector vec{1, 2, 3, 4}; + NDView mutable_view(vec.data(), Shape<2>{2, 2}); + + NDView const_view = mutable_view; + + REQUIRE(const_view.data() == mutable_view.data()); + REQUIRE(const_view.shape() == mutable_view.shape()); + REQUIRE(const_view.strides() == mutable_view.strides()); +} + TEST_CASE("Element reference 2D") { std::vector vec(12); std::iota(vec.begin(), vec.end(), 0); @@ -279,4 +294,4 @@ TEST_CASE("NDView over byte") { auto v = aare::make_view(buf); REQUIRE(v.shape()[0] == 5); REQUIRE(v[0] == std::byte{0}); -} \ No newline at end of file +} diff --git a/src/decode.cpp b/src/decode.cpp index 157fed25..afe261f9 100644 --- a/src/decode.cpp +++ b/src/decode.cpp @@ -144,7 +144,7 @@ uint32_t mask32to24bits(uint32_t input, BitOffset offset) { return (input >> offset.value()) & mask24bits; } -void expand4to8bit(NDView input, NDView output) { +void expand4to8bit(NDView input, NDView output) { if (2 * input.size() != output.size()) throw std::runtime_error( @@ -162,7 +162,7 @@ void expand4to8bit(NDView input, NDView output) { } } -void expand24to32bit(NDView input, NDView output, +void expand24to32bit(NDView input, NDView output, BitOffset bit_offset) { ssize_t bytes_per_channel = 3; // 24bit diff --git a/src/decode.test.cpp b/src/decode.test.cpp index d7f3901d..bab7c38b 100644 --- a/src/decode.test.cpp +++ b/src/decode.test.cpp @@ -154,6 +154,20 @@ TEST_CASE("Expand container with 24 bit data to 32") { } } +TEST_CASE("Expand 24 bit values to 32 bit values from a const buffer") { + const uint8_t buffer[] = { + 0x0F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, + }; + + aare::NDView input(buffer, {9}); + aare::NDArray out({3}); + aare::expand24to32bit(input, out.view()); + + CHECK(out(0) == 0xF); + CHECK(out(1) == 0xFF); + CHECK(out(2) == 0xFFFFFF); +} + TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") { { uint8_t buffer[] = { @@ -172,4 +186,25 @@ TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") { CHECK(out(i) == expected_output[i]); } } -} \ No newline at end of file +} + +TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values from a const " + "buffer") { + { + const uint8_t buffer[] = { + 0x00, 0xF0, 0xFF, 0x00, 0xF0, 0xFF, + }; + + aare::NDView input(&buffer[0], {6}); + aare::NDArray out({12}); + aare::expand4to8bit(input, out.view()); + + uint8_t expected_output[] = { + 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, + 0x0, 0x0, 0x0, 0xF, 0xF, 0xF}; // assuming little endian + + for (size_t i = 0; i < 12; ++i) { + CHECK(out(i) == expected_output[i]); + } + } +} From 85f9d0176a455cc9daa04f5aba441ab4fe815707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Tue, 25 Aug 2026 09:15:38 +0200 Subject: [PATCH 08/10] Dev/guess clusters (#355) - exposing ClusterVector::empty in python - added member guess_n_clusters --- RELEASE.md | 2 ++ include/aare/ClusterFile.hpp | 13 +++++++++++++ python/src/bind_ClusterFile.hpp | 4 +++- python/src/bind_ClusterVector.hpp | 3 ++- python/tests/test_ClusterFile.py | 4 +++- python/tests/test_ClusterVector.py | 4 +++- src/ClusterFile.test.cpp | 4 ++++ 7 files changed, 30 insertions(+), 4 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 8d647df4..638b8b3b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -4,6 +4,8 @@ ### API Changes: +- Exposed ``ClusterVector.empty()`` in the Python API. +- Added ClusterVector.estimate_n_clusters - Removed the lmfit dependency and the legacy ``fit_gaus``, ``fit_pol1``, ``fit_scurve``, and ``fit_scurve2`` APIs. Use ``Gaussian``, ``Pol1``, ``RisingScurve``, or ``FallingScurve`` and call ``model.fit(...)`` (or diff --git a/include/aare/ClusterFile.hpp b/include/aare/ClusterFile.hpp index 885d4b45..4e416c23 100644 --- a/include/aare/ClusterFile.hpp +++ b/include/aare/ClusterFile.hpp @@ -144,6 +144,19 @@ class ClusterFile { */ size_t chunk_size() const { return m_chunk_size; } + /** + * @brief Estimate the number of clusters in the file from its size + * + * Frame headers are included in the estimate, so the result may be slightly + * larger than the actual number of clusters. The file position is not + * changed. + * + * @throws std::runtime_error if the file is not opened for reading + */ + size_t estimate_n_clusters() const { + return std::filesystem::file_size(m_filename) / sizeof(ClusterType); + } + /** * @brief Set the region of interest to use when reading * clusters. If set only clusters within the ROI will be diff --git a/python/src/bind_ClusterFile.hpp b/python/src/bind_ClusterFile.hpp index fbdd1909..0831d64f 100644 --- a/python/src/bind_ClusterFile.hpp +++ b/python/src/bind_ClusterFile.hpp @@ -47,6 +47,8 @@ void define_ClusterFile(py::module &m, const std::string &typestr) { }) .def("set_roi", &ClusterFile::set_roi, py::arg("roi")) .def("tell", &ClusterFile::tell) + .def("estimate_n_clusters", + &ClusterFile::estimate_n_clusters) .def( "set_noise_map", [](ClusterFile &self, py::array_t noise_map) { @@ -82,4 +84,4 @@ void define_ClusterFile(py::module &m, const std::string &typestr) { }); } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/python/src/bind_ClusterVector.hpp b/python/src/bind_ClusterVector.hpp index 254f96a9..a3d59f57 100644 --- a/python/src/bind_ClusterVector.hpp +++ b/python/src/bind_ClusterVector.hpp @@ -72,6 +72,7 @@ void define_ClusterVector(py::module &m, const std::string &typestr) { R"(calculates sum of 2x2 subcluster with highest energy and index relative to cluster center 0: top_left, 1: top_right, 2: bottom_left, 3: bottom_right )") .def_property_readonly("size", &ClusterVector::size) + .def("empty", &ClusterVector::empty) .def("item_size", &ClusterVector::item_size) .def_property_readonly("fmt", [typestr](ClusterVector &self) { @@ -169,4 +170,4 @@ void define_3x3_reduction(py::module &m) { py::arg("clustervector")); } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/python/tests/test_ClusterFile.py b/python/tests/test_ClusterFile.py index 5e032009..3e2fb81d 100644 --- a/python/tests/test_ClusterFile.py +++ b/python/tests/test_ClusterFile.py @@ -14,6 +14,8 @@ from conftest import test_data_path def test_cluster_file(test_data_path): """Test ClusterFile""" f = ClusterFile(test_data_path / "clust/single_frame_97_clustrers.clust") + assert f.estimate_n_clusters() == 97 + assert f.tell() == 0 cv = f.read_clusters(10) #conversion does not work @@ -62,4 +64,4 @@ def test_read_clusters_and_fill_histogram(test_data_path): hist_py = pickle.load(f) #Compare the two histograms - assert hist_aare == hist_py \ No newline at end of file + assert hist_aare == hist_py diff --git a/python/tests/test_ClusterVector.py b/python/tests/test_ClusterVector.py index 0ae96c3e..30f934fa 100644 --- a/python/tests/test_ClusterVector.py +++ b/python/tests/test_ClusterVector.py @@ -16,6 +16,7 @@ def test_create_cluster_vector(): assert cv.cluster_size_x == 3 assert cv.cluster_size_y == 3 assert cv.size == 0 + assert cv.empty() def test_push_back_on_cluster_vector(): @@ -27,6 +28,7 @@ def test_push_back_on_cluster_vector(): cluster = _aare.Cluster2x2i(19, 22, np.ones(4, dtype=np.int32)) cv.push_back(cluster) assert cv.size == 1 + assert not cv.empty() arr = np.array(cv, copy=False) assert arr[0]['x'] == 19 @@ -127,4 +129,4 @@ def test_masking(): assert cv_masked_array[0]["x"] == 1 assert cv_masked_array[0]["y"] == 2 - assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all() \ No newline at end of file + assert (cv_masked_array[0]["data"] == np.ones((3,3),dtype=np.int32)).all() diff --git a/src/ClusterFile.test.cpp b/src/ClusterFile.test.cpp index 78ba3e61..15caf6bb 100644 --- a/src/ClusterFile.test.cpp +++ b/src/ClusterFile.test.cpp @@ -17,6 +17,8 @@ TEST_CASE("Read one frame from a cluster file", "[.with-data]") { REQUIRE(std::filesystem::exists(fpath)); ClusterFile> f(fpath); + CHECK(f.estimate_n_clusters() == 97); + CHECK(f.tell() == 0); auto clusters = f.read_frame(); CHECK(clusters.size() == 97); CHECK(clusters.frame_number() == 135); @@ -250,6 +252,8 @@ TEST_CASE("Read cluster from multiple frame file", "[.with-data]") { SECTION("Read clusters from both frames") { ClusterFile f(fpath); + CHECK(f.estimate_n_clusters() == 8); + CHECK(f.tell() == 0); auto clusters = f.read_clusters(2); REQUIRE(clusters.size() == 2); REQUIRE(clusters.frame_number() == 0); From 568f94926b822bf344d16749d6208fd3710ac6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Fri, 28 Aug 2026 09:22:29 +0200 Subject: [PATCH 09/10] baseline AGENTS.md (#356) - AI generated baseline for AGENTS.md - Meant to be edited in the future --- AGENTS.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..be6fda68 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,166 @@ +# Repository guidelines + +## Scope and project goals + +These instructions apply to the entire repository unless a more specific +`AGENTS.md` exists in a subdirectory. + +Aare is a data-analysis library for PSI hybrid detectors. The C++17 core is +the canonical implementation, while Python is the main user-facing interface +through pybind11. Changes should preserve the project's priorities: fast and +efficient processing, simple interfaces, API stability, and a small dependency +footprint. + +## Repository layout + +- `include/aare/` contains the public C++ API and header-defined templates. +- `src/` contains C++ implementations and colocated Catch2 tests named + `*.test.cpp`. +- `tests/` contains the C++ test executable, configuration, and shared helpers. +- `python/src/` contains pybind11 bindings and module registration. +- `python/aare/` contains Python facades, convenience APIs, and public exports. +- `python/tests/` contains the pytest suite. +- `docs/src/` contains Sphinx/reStructuredText documentation. Doxygen and + Breathe integrate the C++ API into the Sphinx site. +- `benchmarks/` contains Google Benchmark programs. +- `cmake/`, `conda-recipe/`, and `pyproject.toml` support builds and packaging. +- `RELEASE.md` contains pending and published release notes. + +## Build environment + +The project requires CMake 3.15 or newer, C++17 with compiler extensions +disabled, and Python 3.11 or newer. `etc/dev-env.yml` defines the Conda +development environment. + +CMake fetches several dependencies by default. Use +`-DAARE_SYSTEM_LIBRARIES=ON` only when all required system or Conda packages +are available. + +Use this configuration for normal development: + +```bash +cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DAARE_TESTS=ON \ + -DAARE_PYTHON_BINDINGS=ON +cmake --build build -j4 +``` + +Useful optional settings include `AARE_DOCS`, `AARE_BENCHMARKS`, `AARE_ASAN`, +and `AARE_WARNINGS_AS_ERRORS`. Reconfigure an existing build directory instead +of creating alternate in-tree build layouts unless isolation is needed. + +## Implementation conventions + +- Put public declarations in `include/aare/` and private implementation details + in `src/`. +- Add new compiled headers, sources, and C++ tests to the explicit lists in the + root `CMakeLists.txt`. +- Keep template implementations in headers unless the supported types are + explicitly instantiated. +- Follow the naming in adjacent code. Broadly, use the `aare` namespace, + CamelCase types, and snake_case functions. +- Treat ownership, lifetime, const-correctness, array shapes, and buffer + contiguity as part of the API when working with `NDArray`, `NDView`, or NumPy + bindings. +- Avoid unnecessary allocations and copies in detector-data and per-pixel + processing paths. Add or update a benchmark when performance is central to a + change. +- Place APIs that may change without notice under the existing experimental + namespace/module. +- Start new source files with `SPDX-License-Identifier: MPL-2.0`, using the + appropriate comment syntax. +- Prefer descriptive names to comments that only restate the code. +- Do not add a dependency unless the benefit justifies the packaging and + deployment cost. + +## Python-facing changes + +A Python-facing feature can require coordinated changes in several layers: + +1. Update the C++ public API and implementation. +2. Add or update its binding in `python/src/`. +3. Register new bindings in `python/src/module.cpp`. +4. Update the facade or public exports in `python/aare/`. +5. If adding a Python module, add it to `PYTHON_FILES` in + `python/CMakeLists.txt` so it is copied and installed. +6. Add Python tests and update user documentation. + +Bindings must validate NumPy dimensions and data types before constructing +views. Do not return a view whose backing C++ or Python storage can expire while +the view remains reachable. + +## Tests + +Run tests that do not require external detector data with: + +```bash +ctest --test-dir build --output-on-failure -j4 +PYTHONPATH="$PWD/build" python -m pytest python/tests +``` + +For focused runs, use a Catch2 tag or an individual pytest file/test: + +```bash +build/run_tests "[tag]" +PYTHONPATH="$PWD/build" python -m pytest python/tests/test_example.py +``` + +Large detector test files live outside this repository. To include data-backed +tests, set `AARE_TEST_DATA` and opt in explicitly: + +```bash +export AARE_TEST_DATA=/path/to/aare-test-data +build/run_tests "[.with-data]" +PYTHONPATH="$PWD/build" python -m pytest python/tests --with-data +``` + +- Start bug fixes with a failing regression test when practical. +- Put C++ tests beside the relevant implementation as `src/Thing.test.cpp`. +- Mark C++ tests requiring external files with `[.with-data]`. +- Mark Python tests requiring external files with `@pytest.mark.withdata`. +- Run both suites for changes that cross the C++/Python boundary. +- Do not silently skip required data-backed coverage. Report when the external + test data is unavailable. + +## Formatting and static analysis + +Before handing off a broad change, run the relevant checks: + +```bash +pre-commit run --all-files +cmake --build build --target check-format +cmake --build build --target clang-tidy +``` + +C++ formatting follows `.clang-format` (four-space indentation and an 80-column +limit). CMake files are checked by `cmake-format`. Avoid formatting unrelated +code as part of a focused change. + +## Documentation and release notes + +- Update the relevant `.rst` pages for public behavior or API changes. +- Add new pages to `docs/src/index.rst` or the relevant nested toctree. +- Update `RELEASE.md` under `## Next` for user-visible features, bug fixes, and + API changes. +- Do not change `VERSION` unless performing an explicitly requested release. +- Preserve compatibility with existing detector formats and older recorded + files where practical. Call out intentional API or format incompatibilities. + +Build the documentation with: + +```bash +cmake -S . -B build \ + -DAARE_DOCS=ON \ + -DAARE_PYTHON_BINDINGS=ON +cmake --build build --target docs +``` + +## Working practices and handoff + +- Inspect the adjacent implementation, tests, and documentation before editing. +- Keep changes focused and preserve unrelated modifications in the worktree. +- Do not edit generated files or fetched dependency sources under `build/`. +- For large features, prefer independently testable increments. +- At handoff, summarize the behavior and important files changed, checks run, + checks not run and why, and any compatibility or performance considerations. From eddb919328941b718d54f10e7165750131bd3f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Tue, 1 Sep 2026 18:41:12 +0200 Subject: [PATCH 10/10] FastPedestal + optimizations in the cluster finder (#341) Optimized pedestal tracking with FastPedestal and performance improvements to the cluster finder. - ~2x faster hitting 14k FPS on benchmark dataset on 16 core threadripper pro **Changes** - No bounds check on interior of frame. (only within half of a cluster) (~15% improvement) - Cache threshold (std*n_rms) Biggest improvement comes from not computing std for each access - Cache pedestal subtracted frame - Switched to FastPedestal which assumes we reached steady state **Things tried but rejected:** - Always store cluster values, commit on val==max (~15% drop in frame rate) **Options** - using 16 bit clusters and 16 bit pedestal. (slows down the single threaded case but allow us to reach 12k with multi threading) **Other notes on performance** - Passing in memory frames from python and doing nothing: 0.8us/frame - Passing + pedestal subtraction: 28us/frame - Passing + pedestal + cluster finding (no store): 652us/frame - Passing + pedestal + cluster finding: 886 us/frame --- CMakeLists.txt | 22 + RELEASE.md | 23 +- benchmarks/CMakeLists.txt | 4 +- benchmarks/ndview_benchmark.cpp | 17 + docs/src/index.rst | 1 + .../pyPedestalTrackingPixelHistogram.rst | 8 + docs/src/python/pedestal/index.rst | 9 + docs/src/python/pedestal/pyFastPedestal.rst | 83 ++++ docs/src/python/pedestal/pyPedestal.rst | 42 ++ include/aare/Backoff.hpp | 48 ++ include/aare/CircularFifo.hpp | 44 +- include/aare/ClusterCollector.hpp | 46 +- include/aare/ClusterFinder.hpp | 333 +++++++++----- include/aare/ClusterFinderMT.hpp | 173 +++++-- include/aare/ClusterVector.hpp | 27 +- include/aare/FastPedestal.hpp | 424 ++++++++++++++++++ include/aare/NDArray.hpp | 17 + include/aare/Pedestal.hpp | 26 +- .../hist/PedestalTrackingPixelHistogram.hpp | 25 +- include/aare/hist/PixelHistogramImpl.hpp | 25 +- python/CMakeLists.txt | 2 + python/aare/Cluster.py | 4 +- python/aare/ClusterFinder.py | 13 +- python/aare/FastPedestal.py | 56 +++ python/aare/__init__.py | 11 + python/aare/factory.py | 36 ++ python/src/bind_Cluster.hpp | 1 - python/src/bind_ClusterCollector.hpp | 5 +- python/src/bind_ClusterFileSink.hpp | 4 +- python/src/bind_ClusterFinder.hpp | 5 +- python/src/bind_ClusterFinderMT.hpp | 11 +- python/src/bind_ClusterVector.hpp | 1 - python/src/bind_FastPedestal.hpp | 147 ++++++ .../bind_PedestalTrackingPixelHistogram.hpp | 6 +- python/src/module.cpp | 9 + python/src/module_config.hpp | 5 + python/src/pedestal.hpp | 6 + python/tests/test_Cluster.py | 17 +- python/tests/test_FastPedestal.py | 145 ++++++ src/ClusterFinderMT.test.cpp | 81 +++- src/ClusterVector.test.cpp | 26 +- src/hist/PedestalTrackingPixelHistogram.cpp | 252 +++++++---- .../PedestalTrackingPixelHistogram.test.cpp | 289 ++++++++++++ src/hist/PixelHistogramImpl.test.cpp | 11 + 44 files changed, 2220 insertions(+), 320 deletions(-) create mode 100644 benchmarks/ndview_benchmark.cpp create mode 100644 docs/src/python/pedestal/index.rst create mode 100644 docs/src/python/pedestal/pyFastPedestal.rst create mode 100644 docs/src/python/pedestal/pyPedestal.rst create mode 100644 include/aare/Backoff.hpp create mode 100644 include/aare/FastPedestal.hpp create mode 100644 python/aare/FastPedestal.py create mode 100644 python/aare/factory.py create mode 100644 python/src/bind_FastPedestal.hpp create mode 100644 python/src/module_config.hpp create mode 100644 python/tests/test_FastPedestal.py create mode 100644 src/hist/PedestalTrackingPixelHistogram.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e044b3f8..5cb45ea8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,10 @@ option( "Install the python extension in the install tree under CMAKE_INSTALL_PREFIX/aare/" OFF) option(AARE_ASAN "Enable AddressSanitizer" OFF) +option( + AARE_TUNE_LOCAL + "Optimize for the building machine's CPU (-march=native -mtune=native). Not portable, the resulting binaries might not run on other machines." + OFF) # Configure which of the dependencies to use FetchContent for option(AARE_FETCH_FMT "Use FetchContent to download fmt" ON) @@ -296,6 +300,22 @@ else() target_compile_options(aare_compiler_flags INTERFACE -Werror) endif() + if(AARE_TUNE_LOCAL) + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("-march=native" AARE_HAS_MARCH_NATIVE) + check_cxx_compiler_flag("-mtune=native" AARE_HAS_MTUNE_NATIVE) + if(AARE_HAS_MARCH_NATIVE AND AARE_HAS_MTUNE_NATIVE) + message(STATUS "Tuning for the local CPU: -march=native -mtune=native") + target_compile_options(aare_compiler_flags INTERFACE -march=native + -mtune=native) + else() + message( + WARNING + "AARE_TUNE_LOCAL requested but the compiler does not support -march=native/-mtune=native. Ignoring." + ) + endif() + endif() + endif() # GCC/Clang specific if(AARE_PYTHON_BINDINGS) @@ -339,6 +359,7 @@ set(PUBLICHEADERS include/aare/Models.hpp include/aare/FileInterface.hpp include/aare/FilePtr.hpp + include/aare/FastPedestal.hpp include/aare/Frame.hpp include/aare/MultiThreadedFileReader.hpp include/aare/hist/PixelHistogram.hpp @@ -449,6 +470,7 @@ if(AARE_TESTS) ${CMAKE_CURRENT_SOURCE_DIR}/src/Pedestal.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PixelHistogramImpl.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PixelHistogram.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PedestalTrackingPixelHistogram.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/MultiThreadedFileReader.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.test.cpp diff --git a/RELEASE.md b/RELEASE.md index 638b8b3b..f4ed7745 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,8 +2,29 @@ ## Next +### New Features: + +- Added ``FastPedestal`` in C++ and Python for per-pixel running mean, + population variance, and standard deviation. It supports exponentially + weighted updates, initialization from files, direct subtraction from NumPy + arrays, and ``float64``, ``float32``, and ``int16`` output types. +- Added the ``Pedestal_i16`` Python binding alongside + ``FastPedestal_d``, ``FastPedestal_f``, and ``FastPedestal_i16``. +- ``PedestalTrackingPixelHistogram.fill_from_file()`` now uses parallel, + double-buffered file reading and accepts ``reader_threads`` and + ``reader_chunk_size`` tuning parameters. +- Added the ``AARE_TUNE_LOCAL`` CMake option to build with ``-march=native`` + and ``-mtune=native`` when supported. Binaries built with this option are + specific to the local CPU and may not be portable. + ### API Changes: +- ``ClusterFinder`` now uses ``FastPedestal``. It must receive 1000 pedestal + frames before cluster finding; ``find_clusters()`` raises + an error until initialization is complete. Added ``update_threshold()`` to + recompute the per-pixel detection thresholds. +- Added the ``queue_depth`` constructor argument to ``ClusterFinderMT`` to + configure the number of preallocated frame buffers per worker thread. - Exposed ``ClusterVector.empty()`` in the Python API. - Added ClusterVector.estimate_n_clusters - Removed the lmfit dependency and the legacy ``fit_gaus``, ``fit_pol1``, @@ -167,5 +188,3 @@ dhanya.thattil@psi.ch - - diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index ef378e4d..78031277 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -18,8 +18,8 @@ FetchContent_MakeAvailable(benchmark) add_executable(benchmarks) target_sources( - benchmarks PRIVATE ndarray_benchmark.cpp calculateeta_benchmark.cpp - reduce_benchmark.cpp) + benchmarks PRIVATE ndarray_benchmark.cpp ndview_benchmark.cpp + calculateeta_benchmark.cpp reduce_benchmark.cpp) # Link Google Benchmark and other necessary libraries target_link_libraries(benchmarks PRIVATE benchmark::benchmark aare_core diff --git a/benchmarks/ndview_benchmark.cpp b/benchmarks/ndview_benchmark.cpp new file mode 100644 index 00000000..b0653193 --- /dev/null +++ b/benchmarks/ndview_benchmark.cpp @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "aare/NDArray.hpp" +#include "aare/NDView.hpp" +#include + +using aare::NDArray; +using aare::NDView; + +static void BM_CreateNDView(benchmark::State &st) { + NDArray arr{{1024, 1024}, 0}; + for (auto _ : st) { + // This code gets timed + auto res = arr.view(); + benchmark::DoNotOptimize(res); + } +} +BENCHMARK(BM_CreateNDView); \ No newline at end of file diff --git a/docs/src/index.rst b/docs/src/index.rst index d60fc78e..ab22e57d 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -30,6 +30,7 @@ AARE python/file/index python/experimental/index python/histogram/index + python/pedestal/index pyFit diff --git a/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst b/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst index 18d438cd..66fe092d 100644 --- a/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst +++ b/docs/src/python/histogram/pyPedestalTrackingPixelHistogram.rst @@ -15,6 +15,14 @@ Use ``push_pedestal_no_update()`` to seed the pedestal estimate, then asynchronous fills are drained by ``flush()``, and snapshot methods such as ``values()`` and ``pedestal_mean()`` return numpy arrays. +``fill_from_file()`` uses parallel file-reader workers and a double-buffered +pipeline. After the initial batch has been read, histogram processing of one +batch overlaps reading of the next. ``reader_threads`` and +``reader_chunk_size`` tune the I/O stage independently of the histogram worker +count. Two fixed-capacity buffers are allocated once and reused by alternating +their read and histogram roles. Their approximate memory use is ``2 * +reader_threads * reader_chunk_size * rows * cols * sizeof(uint16)``. + .. py:currentmodule:: aare .. autoclass:: PedestalTrackingPixelHistogram diff --git a/docs/src/python/pedestal/index.rst b/docs/src/python/pedestal/index.rst new file mode 100644 index 00000000..4e0a05e4 --- /dev/null +++ b/docs/src/python/pedestal/index.rst @@ -0,0 +1,9 @@ +Pedestal +======== + +.. toctree:: + :caption: Pedestal + :maxdepth: 1 + + pyFastPedestal + pyPedestal diff --git a/docs/src/python/pedestal/pyFastPedestal.rst b/docs/src/python/pedestal/pyFastPedestal.rst new file mode 100644 index 00000000..d442d298 --- /dev/null +++ b/docs/src/python/pedestal/pyFastPedestal.rst @@ -0,0 +1,83 @@ +FastPedestal +============ + +``FastPedestal`` calculates a running mean, variance and standard deviation for each pixel in a +series of frames. The python binding only exposes ``uint16`` input but the underlying +C++ class is templated. Initialize it with ``n_samples`` frames using +``add_init_frame()``. Once ``ready`` is true, use ``push_ema()`` to update the exponential +moving average initialized by the mean and with smoothing factor 1/n_samples. + +.. warning:: + + FastPedestal is not usable until you have added ``n_samples`` initial frames with ``add_init_frame(raw)``. + You can check the state with ``ready``. + +The public factory selects the bound C++ specialization from ``dtype``: + +* ``numpy.float64`` creates ``FastPedestal_d`` +* ``numpy.float32`` creates ``FastPedestal_f`` +* ``numpy.int16`` creates ``FastPedestal_i16`` + +The internal calculations are done with double, but the cached mean and on demand var and std are returned in the specified type. + +Factory +------- + +.. py:currentmodule:: aare + +.. autofunction:: FastPedestal + +Loading from a file +------------------- + +``FastPedestal.from_file()`` initializes the pedestal from ``n_samples`` +frames after ``skip_first``, then applies steady-state updates for any frames +remaining in the file. The input frames must contain ``uint16`` data; ``dtype`` +selects the output type of the pedestal statistics. + +.. autofunction:: aare.FastPedestal.from_file + +.. code-block:: python + + pedestal = FastPedestal.from_file( + "frames.npy", n_samples=100, skip_first=10, dtype=np.float32 + ) + +Example +------- + +.. code-block:: python + + import numpy as np + from aare import FastPedestal + + pedestal = FastPedestal(512, 1024, n_samples=100, dtype=np.float32) + + # Initialize with n_samples frames + for frame in initialization_frames: + pedestal.add_init_frame(frame) + + # Now we can push a frame for pedestal update + if pedestal.ready: + pedestal.push_ema(next_frame) + + # Mean and std are also ready + mean = pedestal.mean() + noise = pedestal.std() + + # Direct pedestal subtraction is also supported + for frame in raw_data: + image = frame - pedestal + +Complete API +------------ + +The API below is for the ``float64`` specialization. All dtype variants share +the same API. + +.. autoclass:: aare._aare.FastPedestal_d + :special-members: __init__ + :members: + :undoc-members: + :show-inheritance: + :inherited-members: diff --git a/docs/src/python/pedestal/pyPedestal.rst b/docs/src/python/pedestal/pyPedestal.rst new file mode 100644 index 00000000..7d6aecb2 --- /dev/null +++ b/docs/src/python/pedestal/pyPedestal.rst @@ -0,0 +1,42 @@ +Pedestal +======== + +``Pedestal`` calculates a running mean and variance for each pixel in a series +of ``uint16`` frames. ``push()`` updates the cached mean immediately. For +faster batch initialization, use ``push_no_update()`` for each frame and call +``update_mean()`` after the batch. + +Three specializations are available from :mod:`aare`: + +* ``Pedestal_d`` uses ``float64`` storage +* ``Pedestal_f`` uses ``float32`` storage +* ``Pedestal_i16`` uses ``int16`` storage + +Example +------- + +.. code-block:: python + + from aare import Pedestal_d + + pedestal = Pedestal_d(512, 1024, 100) + + for frame in initialization_frames: + pedestal.push_no_update(frame) + + pedestal.update_mean() + mean = pedestal.mean() + noise = pedestal.std() + +Complete API +------------ + +The API below is for the ``float64`` specialization. All dtype variants share +the same API. + +.. autoclass:: aare._aare.Pedestal_d + :special-members: __init__ + :members: + :undoc-members: + :show-inheritance: + :inherited-members: diff --git a/include/aare/Backoff.hpp b/include/aare/Backoff.hpp new file mode 100644 index 00000000..617c8aae --- /dev/null +++ b/include/aare/Backoff.hpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MPL-2.0 +#pragma once + +#include +#include + +namespace aare { + +/** + * @brief Hint the CPU that this is a spin-wait. Prefer this over yield() when + * the wait is expected to be short. + */ +inline void cpu_relax() noexcept { +#if defined(__x86_64__) || defined(__i386__) + __builtin_ia32_pause(); +#elif defined(__aarch64__) + asm volatile("yield" ::: "memory"); +#else + std::this_thread::yield(); +#endif +} + +/** + * @brief Escalating wait for producer/consumer idle and backpressure loops. + * + * Starts with a pause instruction (sub-microsecond, no syscall), then yield, + * then a short sleep. Call reset() whenever work arrives so a busy pipeline + * never leaves the pause tier. + */ +class Backoff { + int m_count{0}; + + public: + void reset() noexcept { m_count = 0; } + + void pause() noexcept { + if (m_count < 64) { + cpu_relax(); + } else if (m_count < 256) { + std::this_thread::yield(); + } else { + std::this_thread::sleep_for(std::chrono::microseconds(50)); + } + ++m_count; + } +}; + +} // namespace aare diff --git a/include/aare/CircularFifo.hpp b/include/aare/CircularFifo.hpp index e0f4842b..fb275b72 100644 --- a/include/aare/CircularFifo.hpp +++ b/include/aare/CircularFifo.hpp @@ -1,11 +1,13 @@ // SPDX-License-Identifier: MPL-2.0 #pragma once +#include #include #include #include #include #include +#include #include "aare/ProducerConsumerQueue.hpp" @@ -29,6 +31,23 @@ template class CircularFifo { } } + /** + * @brief Construct a fifo and seed the free list using a factory. + * @param size number of items circulating in the fifo + * @param make_item callable invoked as make_item(i) for each slot index i + * + * Use this instead of CircularFifo(size) when the items need to be + * initialized, for example to hold a preallocated buffer or to carry + * their own slot index. + */ + template + CircularFifo(uint32_t size, F make_item) + : fifo_size(size), free_slots(size + 1), filled_slots(size + 1) { + for (size_t i = 0; i < fifo_size; ++i) { + free_slots.write(make_item(i)); + } + } + bool next() { // TODO! avoid default constructing ItemType ItemType it; @@ -47,6 +66,13 @@ template class CircularFifo { auto numFreeSlots() const noexcept { return free_slots.sizeGuess(); } auto isFull() const noexcept { return filled_slots.isFull(); } + /** + * @brief True if there are no filled slots waiting to be consumed. + * @note Prefer this over numFilledSlots() == 0 since sizeGuess() may + * under-report when called from the producing thread. + */ + auto isEmpty() const noexcept { return filled_slots.isEmpty(); } + ItemType pop_free() { ItemType v; while (!free_slots.read(v)) @@ -75,8 +101,22 @@ template class CircularFifo { ItemType *frontPtr() { return filled_slots.frontPtr(); } - // TODO! Add function to move item from filled to free to be used - // with the frontPtr function + /** + * @brief Return the front filled item to the free list. To be used + * together with frontPtr() once the item has been consumed in place. + * @warning The fifo must not be empty when calling this. + * + * The item is written to the free list before it is popped from the + * filled list, so it can never be dropped. The write cannot fail: both + * queues hold size + 1 slots while only size items circulate. + */ + void recycle_front() { + ItemType *it = filled_slots.frontPtr(); + assert(it != nullptr); + [[maybe_unused]] const bool ok = free_slots.write(std::move(*it)); + assert(ok); + filled_slots.popFront(); + } template void push_value(Args &&...recordArgs) { while (!filled_slots.write(std::forward(recordArgs)...)) diff --git a/include/aare/ClusterCollector.hpp b/include/aare/ClusterCollector.hpp index 23666b7d..aa4e334c 100644 --- a/include/aare/ClusterCollector.hpp +++ b/include/aare/ClusterCollector.hpp @@ -2,7 +2,11 @@ #pragma once #include #include +#include +#include +#include +#include "aare/Backoff.hpp" #include "aare/ClusterFinderMT.hpp" #include "aare/ClusterVector.hpp" #include "aare/ProducerConsumerQueue.hpp" @@ -13,48 +17,64 @@ namespace aare { template >> class ClusterCollector { - ProducerConsumerQueue> *m_source; + using SourceQueue = ProducerConsumerQueue>; + + SourceQueue *m_source; std::atomic m_stop_requested{false}; std::atomic m_stopped{true}; - std::chrono::milliseconds m_default_wait{1}; std::thread m_thread; std::vector> m_clusters; void process() { - m_stopped = false; + m_stopped.store(false, std::memory_order_release); fmt::print("ClusterCollector started\n"); - while (!m_stop_requested || !m_source->isEmpty()) { + Backoff backoff; + while (true) { if (ClusterVector *clusters = m_source->frontPtr(); clusters != nullptr) { + backoff.reset(); m_clusters.push_back(std::move(*clusters)); m_source->popFront(); - } else { - std::this_thread::sleep_for(m_default_wait); + continue; } + + if (m_stop_requested.load(std::memory_order_acquire)) + break; + + backoff.pause(); } fmt::print("ClusterCollector stopped\n"); - m_stopped = true; + m_stopped.store(true, std::memory_order_release); } public: - ClusterCollector(ClusterFinderMT *source) { - m_source = source->sink(); + explicit ClusterCollector(SourceQueue *source) : m_source(source) { m_thread = std::thread(&ClusterCollector::process, this); // only one process does that so why isnt it // automatically written to m_cluster in collect // - instead of writing first to m_sink? } + + template ().sink()), + SourceQueue *>, + int> = 0> + explicit ClusterCollector(Finder *source) + : ClusterCollector(source->sink()) {} + void stop() { - m_stop_requested = true; - m_thread.join(); + m_stop_requested.store(true, std::memory_order_release); + if (m_thread.joinable()) + m_thread.join(); } std::vector> steal_clusters() { - if (!m_stopped) { + if (!m_stopped.load(std::memory_order_acquire)) { throw std::runtime_error("ClusterCollector is still running"); } return std::move(m_clusters); } }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/ClusterFinder.hpp b/include/aare/ClusterFinder.hpp index 83b5c780..33bd4636 100644 --- a/include/aare/ClusterFinder.hpp +++ b/include/aare/ClusterFinder.hpp @@ -3,6 +3,7 @@ #include "aare/ClusterFile.hpp" #include "aare/ClusterVector.hpp" #include "aare/Dtype.hpp" +#include "aare/FastPedestal.hpp" #include "aare/NDArray.hpp" #include "aare/NDView.hpp" #include "aare/Pedestal.hpp" @@ -18,6 +19,13 @@ struct no_2x2_cluster { ClusterType::cluster_size_x > 2 && ClusterType::cluster_size_y > 2; }; +/** + * @brief Find fixed-size photon clusters using a per-pixel pedestal and noise + * threshold. + * @tparam ClusterType Output cluster type; both dimensions must exceed 2. + * @tparam FRAME_TYPE Input pixel type. + * @tparam PEDESTAL_TYPE Type used for pedestal and threshold calculations. + */ template , typename FRAME_TYPE = uint16_t, typename PEDESTAL_TYPE = double, typename = std::enable_if_t::value>> @@ -26,51 +34,86 @@ class ClusterFinder { PEDESTAL_TYPE m_nSigma; const PEDESTAL_TYPE c2; const PEDESTAL_TYPE c3; - Pedestal m_pedestal; + FastPedestal m_pedestal; ClusterVector m_clusters; static const uint8_t ClusterSizeX = ClusterType::cluster_size_x; static const uint8_t ClusterSizeY = ClusterType::cluster_size_y; using CT = typename ClusterType::value_type; + NDArray m_threshold; + NDArray m_pd_corrected_frame; + public: /** - * @brief Construct a new ClusterFinder object - * @param image_size size of the image - * @param cluster_size size of the cluster (x, y) - * @param nSigma number of sigma above the pedestal to consider a photon - * @param capacity initial capacity of the cluster vector - * + * @brief Construct a cluster finder with an empty pedestal. + * @param image_size Image shape as (rows, columns). + * @param nSigma Per-pixel noise threshold multiplier. + * @param capacity Initial cluster-vector capacity. */ ClusterFinder(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0, size_t capacity = 1000000) : m_image_size(image_size), m_nSigma(nSigma), c2(sqrt((ClusterSizeY + 1) / 2 * (ClusterSizeX + 1) / 2)), c3(sqrt(ClusterSizeX * ClusterSizeY)), - m_pedestal(image_size[0], image_size[1]), m_clusters(capacity) { + m_pedestal(image_size[0], image_size[1]), m_clusters(capacity), + m_threshold({image_size[0], image_size[1]}, 0), + m_pd_corrected_frame({image_size[0], image_size[1]}, 0) { LOG(logDEBUG) << "ClusterFinder: " << "image_size: " << image_size[0] << "x" << image_size[1] << ", nSigma: " << nSigma << ", capacity: " << capacity; } - void set_nSigma(PEDESTAL_TYPE nSigma) { m_nSigma = nSigma; } - - PEDESTAL_TYPE get_nSigma() const { return m_nSigma; } - - void push_pedestal_frame(NDView frame) { - m_pedestal.push(frame); + /** + * @brief Set the noise multiplier used for threshold calculation and + * recompute the threshold. + * @param nSigma New per-pixel noise multiplier. + */ + void set_nSigma(PEDESTAL_TYPE nSigma) { + m_nSigma = nSigma; + update_threshold(); } - NDArray pedestal() { return m_pedestal.mean(); } - NDArray noise() { return m_pedestal.std(); } - void clear_pedestal() { m_pedestal.clear(); } + /** @brief Return the current noise multiplier used for threshold + * calculation. */ + PEDESTAL_TYPE get_nSigma() const { return m_nSigma; } /** - * @brief Move the clusters from the ClusterVector in the ClusterFinder to a - * new ClusterVector and return it. - * @param realloc_same_capacity if true the new ClusterVector will have the - * same capacity as the old one + * @brief Add a dark frame to the pedestal estimator. * + * The threshold is initialized automatically when the pedestal first + * becomes ready. Later frames update the ready pedestal. + * @param frame Dark frame matching the configured image shape. + * @throws std::runtime_error if the frame shape does not match. + */ + void push_pedestal_frame(NDView frame) { + if (!m_pedestal.ready()) { + m_pedestal.add_init_frame(frame); + // Initialize the threshold when the pedestal becomes ready. + if (m_pedestal.ready()) { + update_threshold(); + } + } else { + m_pedestal.push_ema(frame); + } + } + + /** @brief Return a copy of the per-pixel pedestal mean. */ + NDArray pedestal() { return m_pedestal.mean(); } + + /** @brief Return the per-pixel pedestal standard deviation (noise). */ + NDArray noise() { return m_pedestal.std(); } + + /** @brief Clear the pedestal and mark it as not ready. */ + void clear_pedestal() { m_pedestal.clear(); } + + /** @brief Recompute the threshold as noise multiplied by nSigma. */ + void update_threshold() { m_threshold = m_pedestal.std() * m_nSigma; } + + /** + * @brief Move out all accumulated clusters and reset the internal vector. + * @param realloc_same_capacity Preserve the previous capacity when true. + * @return The accumulated clusters and their frame metadata. */ ClusterVector steal_clusters(bool realloc_same_capacity = false) { @@ -81,109 +124,179 @@ class ClusterFinder { m_clusters = ClusterVector{}; return tmp; } + + private: + /** + * @brief Process a single pixel: scan its cluster window, decide whether it + * is a photon or a pedestal value, and store the cluster if needed. + * @tparam CheckBounds Skip out-of-image neighbours when true; assume the + * complete window is in bounds when false. Skipped cluster values remain 0. + */ + template + void process_pixel(const NDView &frame, const int iy, + const int ix) { + + constexpr int dy = ClusterSizeY / 2; + constexpr int dx = ClusterSizeX / 2; + constexpr int has_center_pixel_x = ClusterSizeX % 2; + constexpr int has_center_pixel_y = ClusterSizeY % 2; + + PEDESTAL_TYPE max = std::numeric_limits::lowest(); + PEDESTAL_TYPE total = 0; + + const int cols = static_cast(frame.shape(1)); + const int rows = static_cast(frame.shape(0)); + const auto center = + (static_cast(iy) * static_cast(cols)) + + static_cast(ix); + const auto *corrected = m_pd_corrected_frame.data(); + const PEDESTAL_TYPE threshold = m_threshold.data()[center]; + const PEDESTAL_TYPE value = corrected[center]; + + if (value < -threshold) + return; // NEGATIVE_PEDESTAL, nothing to do for this pixel + // TODO! No pedestal update??? + + for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) { + const int y = iy + ir; + if constexpr (CheckBounds) { + if (y < 0 || y >= rows) + continue; + } + + const auto *row = corrected + static_cast(y) * cols; + for (int ic = -dx; ic < dx + has_center_pixel_x; ic++) { + const int x = ix + ic; + if constexpr (CheckBounds) { + if (x < 0 || x >= cols) + continue; + } + + const PEDESTAL_TYPE val = row[x]; + total += val; + max = std::max(max, val); + } + } + + if ((max > threshold)) { + if (value < max) + return; // Not max go to the next pixel, no pedestal update + } else if (total > c3 * threshold) { + // pass, store the cluster below + } else { + m_pedestal.push_ema_unchecked(center, frame.data()[center]); + return; // It was a pedestal value nothing to store + } + + // Store cluster + if (value == max) { + ClusterType cluster{}; + cluster.x = ix; + cluster.y = iy; + + int i = 0; + for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) { + const int y = iy + ir; + for (int ic = -dx; ic < dx + has_center_pixel_x; ic++, i++) { + const int x = ix + ic; + if constexpr (CheckBounds) { + if (x < 0 || x >= cols || y < 0 || y >= rows) + continue; + } + + const PEDESTAL_TYPE corrected_value = + corrected[(static_cast(y) * cols) + x]; + // If the cluster type is an integral type, and the + // pedestal is a floating point type then we need to + // round the value before storing it + if constexpr (std::is_integral_v && + std::is_floating_point_v) { + cluster.data[i] = + static_cast(std::lround(corrected_value)); + } + // On the other hand if both are floating point or both + // are integral then we can just static cast directly + else { + cluster.data[i] = static_cast(corrected_value); + } + } + } + + // Add the cluster to the output ClusterVector + m_clusters.push_back(cluster); + } + } + + public: + /** + * @brief Find clusters in one frame and update eligible pedestal pixels. + * @param frame Input frame matching the configured image shape. + * @param frame_number Metadata assigned to the accumulated clusters. + * @pre frame has the same shape as image_size passed to the constructor. + * @throws std::runtime_error if the pedestal is not ready. + * @note Clusters accumulate until steal_clusters() is called. + */ void find_clusters(NDView frame, uint64_t frame_number = 0) { + // // TODO! deal with even size clusters // // currently 3,3 -> +/- 1 // // 4,4 -> +/- 2 - int dy = ClusterSizeY / 2; - int dx = ClusterSizeX / 2; - int has_center_pixel_x = - ClusterSizeX % - 2; // for even sized clusters there is no proper cluster center and - // even amount of pixels around the center - int has_center_pixel_y = ClusterSizeY % 2; + if (!m_pedestal.ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot find clusters"); + } + + constexpr int dy = ClusterSizeY / 2; + constexpr int dx = ClusterSizeX / 2; + constexpr int has_center_pixel_x = ClusterSizeX % 2; + constexpr int has_center_pixel_y = ClusterSizeY % 2; + + // Largest neighbour offset below/right of the current pixel. Pixels + // further than this from an edge have their whole window in bounds. + constexpr int down = dy + has_center_pixel_y - 1; + constexpr int right = dx + has_center_pixel_x - 1; m_clusters.set_frame_number(frame_number); - for (int iy = 0; iy < frame.shape(0); iy++) { - for (int ix = 0; ix < frame.shape(1); ix++) { - PEDESTAL_TYPE max = std::numeric_limits::min(); - PEDESTAL_TYPE total = 0; + const int rows = static_cast(frame.shape(0)); + const int cols = static_cast(frame.shape(1)); - // What can we short circuit here? - PEDESTAL_TYPE rms = m_pedestal.std(iy, ix); - PEDESTAL_TYPE value = (frame(iy, ix) - m_pedestal.mean(iy, ix)); + // TODO! See if we can get the same performace using the operator- + // m_pd_corrected_frame = frame - m_pedestal.view(); - if (value < -m_nSigma * rms) - continue; // NEGATIVE_PEDESTAL go to next pixel - // TODO! No pedestal update??? + // here we should be able to safely assume that the frame and corrected + // frame have the same size + auto n_pixels = frame.size(); + auto pd = m_pedestal.view().data(); + auto corrected = m_pd_corrected_frame.data(); + auto frame_data = frame.data(); + for (ssize_t i = 0; i < n_pixels; i++) { + corrected[i] = static_cast(frame_data[i]) - pd[i]; + } - for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) { - for (int ic = -dx; ic < dx + has_center_pixel_x; ic++) { - if (ix + ic >= 0 && ix + ic < frame.shape(1) && - iy + ir >= 0 && iy + ir < frame.shape(0)) { - PEDESTAL_TYPE val = - frame(iy + ir, ix + ic) - - m_pedestal.mean(iy + ir, ix + ic); + // Interior pixels can skip the per-neighbour bounds checks; pixels + // within dx/dy of an edge take the bounds-checked path. Iteration order + // (row-major, increasing ix) is preserved so results are identical. + const int ix_begin = dx; + const int ix_end = cols - right; // exclusive - total += val; - max = std::max(max, val); - } - } - } + for (int iy = 0; iy < rows; iy++) { + const bool interior_row = iy >= dy && iy < rows - down; - if ((max > m_nSigma * rms)) { - if (value < max) - continue; // Not max go to the next pixel - // but also no pedestal update - } else if (total > c3 * m_nSigma * rms) { - // pass - } else { - // m_pedestal.push(iy, ix, frame(iy, ix)); // Safe option - m_pedestal.push_fast( - iy, ix, - frame(iy, - ix)); // Assume we have reached n_samples in the - // pedestal, slight performance improvement - continue; // It was a pedestal value nothing to store - } - - // Store cluster - if (value == max) { - ClusterType cluster{}; - cluster.x = ix; - cluster.y = iy; - - // Fill the cluster data since we have a photon to store - // It's worth redoing the look since most of the time we - // don't have a photon - int i = 0; - for (int ir = -dy; ir < dy + has_center_pixel_y; ir++) { - for (int ic = -dx; ic < dx + has_center_pixel_x; ic++) { - if (ix + ic >= 0 && ix + ic < frame.shape(1) && - iy + ir >= 0 && iy + ir < frame.shape(0)) { - - // If the cluster type is an integral type, and - // the pedestal is a floating point type then we - // need to round the value before storing it - if constexpr (std::is_integral_v && - std::is_floating_point_v< - PEDESTAL_TYPE>) { - auto tmp = std::lround( - frame(iy + ir, ix + ic) - - m_pedestal.mean(iy + ir, ix + ic)); - cluster.data[i] = static_cast(tmp); - } - // On the other hand if both are floating point - // or both are integral then we can just static - // cast directly - else { - auto tmp = - frame(iy + ir, ix + ic) - - m_pedestal.mean(iy + ir, ix + ic); - cluster.data[i] = static_cast(tmp); - } - } - i++; - } - } - - // Add the cluster to the output ClusterVector - m_clusters.push_back(cluster); - } + if (!interior_row || ix_begin >= ix_end) { + for (int ix = 0; ix < cols; ix++) + process_pixel(frame, iy, ix); + continue; } + + for (int ix = 0; ix < ix_begin; ix++) + process_pixel(frame, iy, ix); + for (int ix = ix_begin; ix < ix_end; ix++) + process_pixel(frame, iy, ix); + for (int ix = ix_end; ix < cols; ix++) + process_pixel(frame, iy, ix); } } }; -} // namespace aare \ No newline at end of file +} // namespace aare diff --git a/include/aare/ClusterFinderMT.hpp b/include/aare/ClusterFinderMT.hpp index d4ff0018..a4995d0d 100644 --- a/include/aare/ClusterFinderMT.hpp +++ b/include/aare/ClusterFinderMT.hpp @@ -6,11 +6,13 @@ #include #include +#include "aare/Backoff.hpp" +#include "aare/CircularFifo.hpp" #include "aare/ClusterFinder.hpp" #include "aare/NDArray.hpp" #include "aare/ProducerConsumerQueue.hpp" #include "aare/logger.hpp" - +#include namespace aare { enum class FrameType { @@ -18,10 +20,40 @@ enum class FrameType { PEDESTAL, }; -struct FrameWrapper { - FrameType type; - uint64_t frame_number; - NDArray data; +/** + * @brief Ticket identifying a frame buffer in a FramePool. Trivially + * copyable, the buffer itself never travels through the queues. + */ +struct FrameRef { + FrameType type{}; + uint32_t slot{}; + uint64_t frame_number{}; +}; + +/** + * @brief Fixed set of frame buffers allocated once at construction. + * + * Buffers are addressed by slot index and are never moved or reallocated, so + * the queues only need to pass a FrameRef around. This keeps the per frame + * allocation out of the hot path entirely. + */ +class FramePool { + std::vector> m_buffers; + + public: + FramePool(size_t depth, Shape<2> shape) { + m_buffers.reserve(depth); // no reallocation, slots stay stable + for (size_t i = 0; i < depth; ++i) { + m_buffers.emplace_back(shape); + } + } + + NDArray &operator[](uint32_t slot) { return m_buffers[slot]; } + const NDArray &operator[](uint32_t slot) const { + return m_buffers[slot]; + } + + size_t size() const { return m_buffers.size(); } }; /** @@ -42,17 +74,18 @@ class ClusterFinderMT { size_t m_current_thread{0}; size_t m_n_threads{0}; using Finder = ClusterFinder; - using InputQueue = ProducerConsumerQueue; + using InputQueue = CircularFifo; using OutputQueue = ProducerConsumerQueue>; std::vector> m_input_queues; std::vector> m_output_queues; + std::vector> m_frame_pools; OutputQueue m_sink{1000}; // All clusters go into this queue std::vector> m_cluster_finders; std::vector m_threads; std::thread m_collect_thread; - std::chrono::milliseconds m_default_wait{1}; + std::chrono::microseconds m_default_wait{50}; private: std::atomic m_stop_requested{false}; @@ -65,31 +98,35 @@ class ClusterFinderMT { void process(int thread_id) { auto cf = m_cluster_finders[thread_id].get(); auto q = m_input_queues[thread_id].get(); - bool realloc_same_capacity = true; + auto *pool = m_frame_pools[thread_id].get(); + Backoff backoff; while (!m_stop_requested || !q->isEmpty()) { - if (FrameWrapper *frame = q->frontPtr(); frame != nullptr) { + if (FrameRef *ref = q->frontPtr(); ref != nullptr) { + backoff.reset(); + auto view = (*pool)[ref->slot].view(); - switch (frame->type) { - case FrameType::DATA: - cf->find_clusters(frame->data.view(), frame->frame_number); + switch (ref->type) { + case FrameType::DATA: { + cf->find_clusters(view, ref->frame_number); + // Steal before the write so a failed write cannot drop the + // clusters by re-stealing an empty vector on retry. + auto clusters = cf->steal_clusters(true); while (!m_output_queues[thread_id]->write( - cf->steal_clusters(realloc_same_capacity))) { - std::this_thread::sleep_for(m_default_wait); + std::move(clusters))) { + backoff.pause(); } - break; - + } case FrameType::PEDESTAL: - m_cluster_finders[thread_id]->push_pedestal_frame( - frame->data.view()); + m_cluster_finders[thread_id]->push_pedestal_frame(view); break; } - // frame is processed now discard it - m_input_queues[thread_id]->popFront(); + // frame is processed, hand the buffer back to the free list + q->recycle_front(); } else { - std::this_thread::sleep_for(m_default_wait); + backoff.pause(); } } } @@ -100,18 +137,24 @@ class ClusterFinderMT { */ void collect() { bool empty = true; + Backoff backoff; while (!m_stop_requested || !empty || !m_processing_threads_stopped) { - empty = true; + bool moved_any = false; for (auto &queue : m_output_queues) { - if (!queue->isEmpty()) { - - while (!m_sink.write(std::move(*queue->frontPtr()))) { - std::this_thread::sleep_for(m_default_wait); + while (auto *front = queue->frontPtr()) { + while (!m_sink.write(std::move(*front))) { + backoff.pause(); } queue->popFront(); - empty = false; + moved_any = true; } } + empty = !moved_any; + if (moved_any) { + backoff.reset(); + } else { + backoff.pause(); + } } } @@ -124,16 +167,22 @@ class ClusterFinderMT { * @param capacity initial capacity of the cluster vector. Should match * expected number of clusters in a frame per frame. * @param n_threads number of threads to use + * @param queue_depth number of frame buffers per thread. These are + * allocated once and recycled, so the total resident frame memory is + * n_threads * queue_depth * frame size. Keeping the in flight data below + * the L3 size keeps the per frame copy cheap. */ ClusterFinderMT(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0, - size_t capacity = 2000, size_t n_threads = 3) + size_t capacity = 2000, size_t n_threads = 3, + size_t queue_depth = 16) : m_n_threads(n_threads) { LOG(logDEBUG1) << "ClusterFinderMT: " << "image_size: " << image_size[0] << "x" << image_size[1] << ", nSigma: " << nSigma << ", capacity: " << capacity - << ", n_threads: " << n_threads; + << ", n_threads: " << n_threads + << ", queue_depth: " << queue_depth; for (size_t i = 0; i < n_threads; i++) { m_cluster_finders.push_back( @@ -142,7 +191,13 @@ class ClusterFinderMT { image_size, nSigma, capacity)); } for (size_t i = 0; i < n_threads; i++) { - m_input_queues.emplace_back(std::make_unique(200)); + m_frame_pools.emplace_back( + std::make_unique(queue_depth, image_size)); + m_input_queues.emplace_back(std::make_unique( + static_cast(queue_depth), [](size_t slot) { + return FrameRef{FrameType::DATA, + static_cast(slot), 0}; + })); m_output_queues.emplace_back(std::make_unique(200)); } // TODO! Should we start automatically? @@ -212,13 +267,22 @@ class ClusterFinderMT { * expected to be dark. No photon finding is done. Just pedestal update. */ void push_pedestal_frame(NDView frame) { - FrameWrapper fw{FrameType::PEDESTAL, 0, - NDArray(frame)}; // TODO! copies the data! + for (size_t i = 0; i < m_n_threads; ++i) { + auto *q = m_input_queues[i].get(); + Backoff backoff; - for (auto &queue : m_input_queues) { - while (!queue->write(fw)) { - std::this_thread::sleep_for(m_default_wait); + FrameRef ref; + while (!q->try_pop_free(ref)) { + backoff.pause(); } + + ref.type = FrameType::PEDESTAL; + ref.frame_number = 0; + (*m_frame_pools[i])[ref.slot].copy_from(frame); + + // Cannot fail, the free list is what limits how many frames are + // in flight so there is always room in the filled list. + q->try_push_value(ref); } } @@ -228,11 +292,29 @@ class ClusterFinderMT { * @note Spin locks with a default wait if the queue is full. */ void find_clusters(NDView frame, uint64_t frame_number = 0) { - FrameWrapper fw{FrameType::DATA, frame_number, - NDArray(frame)}; // TODO! copies the data! - while (!m_input_queues[m_current_thread % m_n_threads]->write(fw)) { - std::this_thread::sleep_for(m_default_wait); + const size_t tid = m_current_thread % m_n_threads; + auto *q = m_input_queues[tid].get(); + Backoff backoff; + + FrameRef ref; + while (!q->try_pop_free(ref)) { + backoff.pause(); } + + ref.type = FrameType::DATA; + ref.frame_number = frame_number; + + // DualTimer dt; + (*m_frame_pools[tid])[ref.slot].copy_from(frame); + // auto [wall_ns, cpu_ns] = dt.elapsed(); + // std::cerr << "ClusterFinderMT: find_clusters: copied frame " + // << frame_number << " took " << wall_ns/1000.0 << " wall_us + // and " + // << cpu_ns/1000.0 << " cpu_us" << std::endl; + + // Cannot fail, the free list is what limits how many frames are in + // flight so there is always room in the filled list. + q->try_push_value(ref); m_current_thread++; } @@ -245,6 +327,19 @@ class ClusterFinderMT { } } + /** + * @brief Recompute the threshold (nSigma * pedestal std) on all cluster + * finders. Requires the processing threads to be stopped. + */ + void update_threshold() { + if (!m_processing_threads_stopped) { + throw std::runtime_error("ClusterFinderMT is still running"); + } + for (auto &cf : m_cluster_finders) { + cf->update_threshold(); + } + } + /** * @brief Return the pedestal currently used by the cluster finder * @param thread_index index of the thread diff --git a/include/aare/ClusterVector.hpp b/include/aare/ClusterVector.hpp index 0adcb5e3..a4141857 100644 --- a/include/aare/ClusterVector.hpp +++ b/include/aare/ClusterVector.hpp @@ -53,11 +53,8 @@ class ClusterVector> { m_data.reserve(capacity); } - // Move constructor - ClusterVector(ClusterVector &&other) noexcept - : m_data(other.m_data), m_frame_number(other.m_frame_number) { - other.m_data.clear(); - } + ClusterVector(ClusterVector &&other) noexcept = default; + ClusterVector &operator=(ClusterVector &&other) noexcept = default; /** * @brief Create a copy of the clustervector by filtering clusters in the @@ -80,16 +77,16 @@ class ClusterVector> { return result; } - // Move assignment operator - ClusterVector &operator=(ClusterVector &&other) noexcept { - if (this != &other) { - m_data = other.m_data; - m_frame_number = other.m_frame_number; - other.m_data.clear(); - other.m_frame_number = 0; - } - return *this; - } + // // Move assignment operator + // ClusterVector &operator=(ClusterVector &&other) noexcept { + // if (this != &other) { + // m_data = other.m_data; + // m_frame_number = other.m_frame_number; + // other.m_data.clear(); + // other.m_frame_number = 0; + // } + // return *this; + // } /** * @brief Sum the pixels in each cluster diff --git a/include/aare/FastPedestal.hpp b/include/aare/FastPedestal.hpp new file mode 100644 index 00000000..48aec4bf --- /dev/null +++ b/include/aare/FastPedestal.hpp @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: MPL-2.0 +#pragma once +#include "aare/File.hpp" +#include "aare/Frame.hpp" +#include "aare/NDArray.hpp" +#include "aare/NDView.hpp" +#include +#include + +namespace aare { + +/** + * @brief Maintain per-pixel mean, population variance, and standard deviation. + * + * Initialization accumulates exactly n_samples frames. Subsequent push_ema() + * update the exponential moving average initialized with the mean using + * a smoothing factor of (1/ n_samples). Internal moments are stored + * in double precision. + * @tparam PEDESTAL_TYPE Type returned for the mean, variance, and standard + * deviation. + */ +template class FastPedestal { + + // Did we accumulate enough samples and updated the mean? + bool m_ready = false; + + uint32_t m_rows; + uint32_t m_cols; + + uint32_t m_samples; + double m_inv_samples; // precompute 1/m_samples for faster division + uint32_t m_cur_samples = 0; // number of samples accumulated so far + + // For cache locality we want to keep sum and sum2 close. Should + // improve performance for random access. + struct Entry { + double sum; + double sum2; + }; + NDArray m_sum; + + // Cache mean since it is used over and over in the ClusterFinder + // This optimization is related to the access pattern of the ClusterFinder + // Relies on having more reads than pushes to the pedestal + // But also makes sense when subtracting the pedestal from the frame + NDArray m_mean; + + // Helper function to convert row and column indices to a flat index + // used to provide both row column and flat index access to the pedestal + size_t rc_to_index(uint32_t row, uint32_t col) const { + return (static_cast(row) * m_cols) + col; + } + + public: + /** + * @brief Construct an empty pedestal that becomes ready after n_samples + * initialization frames. + * @param rows Number of image rows. + * @param cols Number of image columns. + * @param n_samples Number of initialization frames and reciprocal of the + * weight assigned to each subsequent value. + * @throws std::runtime_error if rows, cols, or n_samples is zero. + */ + FastPedestal(uint32_t rows, uint32_t cols, uint32_t n_samples = 1000) + : m_rows(rows), m_cols(cols), m_samples(n_samples), + m_inv_samples(1.0 / n_samples), m_sum({rows, cols}, Entry{0, 0}), + m_mean({rows, cols}, PEDESTAL_TYPE(0)) { + if (!(rows > 0 && cols > 0 && n_samples > 0)) { + throw std::runtime_error( + fmt::format("Invalid parameters for FastPedestal: rows={}, " + "cols={}, n_samples={} need to be positive", + rows, cols, n_samples)); + } + } + + ~FastPedestal() = default; + + /** + * @brief Return a non-owning view of the cached mean. + * @throws std::runtime_error if ready() is false. + * @note The caller must treat the data as read-only and must not retain the + * view after this object is destroyed, moved, or assigned. + */ + NDView view() const { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return view"); + } + return m_mean.view(); + } + + /** + * @brief Return a copy of the cached mean. + * @throws std::runtime_error if ready() is false. + */ + NDArray mean() { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return mean"); + } + return m_mean; + } + + /** + * @brief Return the cached mean at (row, col). + * @throws std::runtime_error if ready() is false or either index is out of + * range. + */ + PEDESTAL_TYPE mean(uint32_t row, uint32_t col) const { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return mean"); + } + if (row >= m_rows || col >= m_cols) { + throw std::runtime_error( + fmt::format("Invalid indices for FastPedestal mean: row={}, " + "col={} must be in [0, {}), [0, {})", + row, col, m_rows, m_cols)); + } + return m_mean(row, col); + } + + /** + * @brief Return the cached mean at a flat row-major index. + * @pre ready() is true, the cache is current, and index is valid; the index + * is not checked. + */ + PEDESTAL_TYPE mean_unchecked(ssize_t index) const { return m_mean[index]; } + + /** + * @brief Calculate and return the population variance of every pixel. + * @throws std::runtime_error if ready() is false. + * @note The result is normalized by n_samples. + */ + NDArray variance() { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return variance"); + } + NDArray res({m_rows, m_cols}); + for (ssize_t i = 0; i < m_sum.size(); ++i) { + res[i] = variance_unchecked(i); + } + return res; + } + + /** + * @brief Calculate the population variance at (row, col). + * @throws std::runtime_error if ready() is false or either index is out of + * range. + */ + PEDESTAL_TYPE variance(const uint32_t row, const uint32_t col) const { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return variance"); + } + if (row >= m_rows || col >= m_cols) { + throw std::runtime_error(fmt::format( + "Invalid indices for FastPedestal variance: row={}, " + "col={} must be in [0, {}), [0, {})", + row, col, m_rows, m_cols)); + } + return variance_unchecked(rc_to_index(row, col)); + } + + /** + * @brief Calculate the population variance at a flat row-major index. + * @pre ready() is true and index is valid; the index is not checked. + */ + PEDESTAL_TYPE variance_unchecked(ssize_t index) const { + const auto &entry = m_sum[index]; + const auto m = entry.sum * m_inv_samples; + return std::fma(-m, m, entry.sum2 * m_inv_samples); + } + + /** + * @brief Calculate and return the population standard deviation of every + * pixel. + * @throws std::runtime_error if ready() is false. + */ + NDArray std() { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return std"); + } + NDArray res({m_rows, m_cols}); + for (ssize_t i = 0; i < m_sum.size(); ++i) { + res[i] = std_unchecked(i); + } + return res; + } + + /** + * @brief Calculate the population standard deviation at (row, col). + * @throws std::runtime_error if ready() is false or either index is out of + * range. + */ + PEDESTAL_TYPE std(const uint32_t row, const uint32_t col) const { + if (!ready()) { + throw std::runtime_error( + "Pedestal is not ready, cannot return std"); + } + if (row >= m_rows || col >= m_cols) { + throw std::runtime_error( + fmt::format("Invalid indices for FastPedestal std: row={}, " + "col={} must be in [0, {}), [0, {})", + row, col, m_rows, m_cols)); + } + return std::sqrt(variance(row, col)); + } + + /** + * @brief Calculate the population standard deviation at a flat row-major + * index. + * @pre ready() is true and index is valid; the index is not checked. + */ + PEDESTAL_TYPE std_unchecked(ssize_t index) const { + return std::sqrt(variance_unchecked(index)); + } + + /** + * @brief Return whether initialization is complete (cur_samples() equals + * n_samples()). + */ + bool ready() const { return m_ready; } + + /** + * @brief Return the stored number of accumulated initialization frames. + * @note The value is in [0, n_samples] and does not change during + * steady-state pushes. + */ + uint32_t cur_samples() const { return m_cur_samples; } + + /** + * @brief Zero the moments and cached mean, and mark the pedestal not ready. + */ + void clear() { + m_sum = Entry{0., 0.}; + m_mean = PEDESTAL_TYPE(0.); + m_cur_samples = 0; + m_ready = false; + } + + /** + * @brief Update every pixel using the steady-state exponential estimator, + * giving the new value weight 1 / n_samples. + * @param frame Frame whose shape must exactly match the pedestal. + * @throws std::runtime_error if the shape differs or ready() is false. + */ + template void push_ema(NDView frame) { + if (frame.shape() != std::array{m_rows, m_cols}) { + throw std::runtime_error( + "Frame shape does not match pedestal shape"); + } + + if (!ready()) { + throw std::runtime_error("Pedestal is not ready, cannot push"); + } + + const auto size = static_cast(m_rows) * m_cols; + const auto *data = frame.data(); + for (std::size_t index = 0; index < size; ++index) { + push_ema_unchecked(index, data[index]); + } + } + + /** + * @brief Update every pixel from a Frame using the steady-state estimator. + * @tparam T Actual pixel type stored in frame; this is not runtime-checked. + * @param frame Frame whose shape must exactly match the pedestal. + * @throws std::runtime_error if the shape differs or ready() is false. + */ + template void push_ema(Frame &frame) { + push_ema(frame.view()); + } + + /** + * @brief Update the exponential moving average with smoothing factor + * 1/n_samples + * @param row Pixel row. + * @param col Pixel column. + * @param val New pixel value. + * @pre row and col are valid; indices are not checked. + * @throws std::runtime_error if ready() is false. + */ + template + void push_ema(const uint32_t row, const uint32_t col, const T val) { + if (!ready()) { + throw std::runtime_error("Pedestal is not ready, cannot push"); + } + + push_ema_unchecked(rc_to_index(row, col), val); + } + + /** + * @brief Update one pixel and its cached mean without runtime checks in + * release builds. + * @param index Flat row-major pixel index. + * @param value New pixel value, with weight 1 / n_samples. + * @pre ready() is true and index is a valid flat row-major index. These + * preconditions are asserted only in debug builds. + */ + template + void push_ema_unchecked(const std::size_t index, const T value) noexcept { + assert(m_ready); + assert(index < static_cast(m_sum.size())); + + const auto val = static_cast(value); + auto &entry = m_sum[index]; + entry.sum += val - entry.sum * m_inv_samples; + entry.sum2 += val * val - entry.sum2 * m_inv_samples; + m_mean[index] = static_cast(entry.sum * m_inv_samples); + } + + /** + * @brief Accumulate one initialization frame. + * @param frame Frame whose shape must exactly match the pedestal. + * @throws std::runtime_error if the shape differs or n_samples frames have + * already been accumulated. + * @note The statistics can be accessed and ready() becomes true only after + * the n_samples frames have been added. + */ + template void add_init_frame(NDView frame) { + if (frame.shape() != std::array{m_rows, m_cols}) { + throw std::runtime_error( + "Frame shape does not match pedestal shape"); + } + + // if the pedestal is already initialized we cannot add more frames + if (ready()) { + throw std::runtime_error("Pedestal initialization is already done"); + } + + for (ssize_t i = 0; i < m_sum.size(); ++i) { + const auto val = static_cast(frame[i]); + auto &entry = m_sum[i]; + entry.sum += val; + entry.sum2 += val * val; + } + m_cur_samples += 1; + + if (m_cur_samples == m_samples) { + update_mean(); + m_ready = true; + } + } + + /** + * @brief Initialize from n_samples frames after skip_first, then apply all + * remaining file frames as steady-state updates. + * @tparam T Pixel representation stored in the file; this is not checked. + * @param filename Input image file. Its dimensions define the pedestal + * shape. + * @param n_samples Number of initialization frames. + * @param skip_first Number of leading frames to ignore. + * @throws std::runtime_error if fewer than n_samples frames remain after + * skip_first, or if any constructor argument is invalid. + */ + template + static FastPedestal from_file(const std::filesystem::path &filename, + uint32_t n_samples = 1000, + uint32_t skip_first = 0) { + File f(filename); + + const auto total_frames = f.total_frames(); + const auto first_frame = static_cast(skip_first); + const auto initialization_frames = static_cast(n_samples); + if (first_frame > total_frames || + initialization_frames > total_frames - first_frame) { + throw std::runtime_error( + "File has less frames than the number of samples needed to " + "initialize the pedestal"); + } + + if (skip_first > 0) { + f.seek(static_cast(skip_first)); + } + const auto rows = static_cast(f.rows()); + const auto cols = static_cast(f.cols()); + FastPedestal pedestal(rows, cols, n_samples); + NDArray frame({rows, cols}); + + auto frame_index = first_frame; + const auto initialization_end = first_frame + initialization_frames; + while (frame_index < initialization_end) { + f.read_into(frame.buffer()); + pedestal.template add_init_frame(frame.view()); + frame_index++; + } + + // read the rest of the file + while (frame_index < total_frames) { + f.read_into(frame.buffer()); + pedestal.template push_ema(frame.view()); + frame_index++; + } + return pedestal; + } + + /** @brief Return the number of image rows. */ + uint32_t rows() const { return m_rows; } + + /** @brief Return the number of image columns. */ + uint32_t cols() const { return m_cols; } + + /** + * @brief Return the initialization frame count and steady-state + * update-weight denominator. + */ + uint32_t n_samples() const { return m_samples; } + + private: + /** + * @brief Write the cached mean after the final add_init_frame. All other + * (non initialization) pushes update the cached mean immediately. + */ + void update_mean() { + for (ssize_t i = 0; i < m_sum.size(); i++) { + auto &entry = m_sum[i]; + m_mean[i] = static_cast(entry.sum * m_inv_samples); + } + } +}; +} // namespace aare diff --git a/include/aare/NDArray.hpp b/include/aare/NDArray.hpp index 43964028..fb6c3bb5 100644 --- a/include/aare/NDArray.hpp +++ b/include/aare/NDArray.hpp @@ -146,6 +146,23 @@ class NDArray : public ArrayExpr, Ndim> { */ ~NDArray() { delete[] data_; } + /** + * @brief Copy data from a view of matching shape into this array without + * reallocating. + * @param v view to copy from, must have the same shape as this array + * @throws std::runtime_error if the shapes differ + * + * Use this instead of assigning a new NDArray when the buffer needs to be + * kept, for example when the array is part of a preallocated pool. + */ + void copy_from(const NDView v) { + if (v.shape() != shape_) { + throw std::runtime_error(LOCATION + + "Shape mismatch in NDArray::copy_from"); + } + std::copy(v.begin(), v.end(), begin()); + } + /////////////////////////////////////////////////////////////////////////////// // Iterators and indexing // diff --git a/include/aare/Pedestal.hpp b/include/aare/Pedestal.hpp index 48bb6568..4e68384f 100644 --- a/include/aare/Pedestal.hpp +++ b/include/aare/Pedestal.hpp @@ -29,17 +29,22 @@ template class Pedestal { // Relies on having more reads than pushes to the pedestal NDArray m_mean; + // Cache std. Only refreshed via update_std() to keep push() cheap. + NDArray m_std; + public: Pedestal(uint32_t rows, uint32_t cols, uint32_t n_samples = 1000) : m_rows(rows), m_cols(cols), m_samples(n_samples), m_cur_samples(NDArray({rows, cols}, 0)), m_sum(NDArray({rows, cols})), m_sum2(NDArray({rows, cols})), - m_mean(NDArray({rows, cols})) { + m_mean(NDArray({rows, cols})), + m_std(NDArray({rows, cols})) { assert(rows > 0 && cols > 0 && n_samples > 0); m_sum = 0; m_sum2 = 0; m_mean = 0; + m_std = 0; } ~Pedestal() = default; @@ -51,6 +56,12 @@ template class Pedestal { return m_mean(row, col); } + NDArray cached_std() { return m_std; } + + SUM_TYPE cached_std(const uint32_t row, const uint32_t col) const { + return m_std(row, col); + } + SUM_TYPE std(const uint32_t row, const uint32_t col) const { return std::sqrt(variance(row, col)); } @@ -87,6 +98,7 @@ template class Pedestal { m_sum2 = 0; m_cur_samples = 0; m_mean = 0; + m_std = 0; } void clear(const uint32_t row, const uint32_t col) { @@ -94,6 +106,7 @@ template class Pedestal { m_sum2(row, col) = 0; m_cur_samples(row, col) = 0; m_mean(row, col) = 0; + m_std(row, col) = 0; } template void push(NDView frame) { @@ -211,6 +224,17 @@ template class Pedestal { */ void update_mean() { m_mean = m_sum / m_cur_samples; } + /** + * @brief Refresh the cached std for all pixels from the current sums. + * Kept separate from push() so pushes stay cheap; call before reading + * cached_std() (analogous to update_mean()). + */ + void update_std() { + for (uint32_t i = 0; i < m_rows * m_cols; i++) { + m_std(i / m_cols, i % m_cols) = std(i / m_cols, i % m_cols); + } + } + template void push_fast(const uint32_t row, const uint32_t col, const T val_) { // Assume we reached the steady state where all pixels have diff --git a/include/aare/hist/PedestalTrackingPixelHistogram.hpp b/include/aare/hist/PedestalTrackingPixelHistogram.hpp index 263ea450..88ca7f2f 100644 --- a/include/aare/hist/PedestalTrackingPixelHistogram.hpp +++ b/include/aare/hist/PedestalTrackingPixelHistogram.hpp @@ -1,4 +1,5 @@ #pragma once +#include "aare/FastPedestal.hpp" #include "aare/NDArray.hpp" #include "aare/NDView.hpp" #include "aare/Pedestal.hpp" @@ -44,7 +45,7 @@ class PedestalTrackingPixelHistogram { // worker using the LOCAL row index (i.e. 0..row_count(t)-1), NOT the // global row index. Owned exclusively by worker `t` during a // dispatched fan-out. - std::vector> partial_pedestals_; + std::vector> partial_pedestals_; std::vector> partial_std_; // cached for pedestal // tracking @@ -65,6 +66,11 @@ class PedestalTrackingPixelHistogram { // Always the outermost lock; work_mutex_ is taken briefly inside it. mutable std::mutex fill_mutex_; + // Serialises producers of async_queue_ (which is SPSC) and gives + // fill_from_file exclusive ownership of the ingestion path while its + // direct, double-buffered dispatch is active. + std::mutex ingestion_mutex_; + // Async producer/consumer pipeline. SPSC queue feeds the coordinator // thread, which batches queued images before dispatching them. std::unique_ptr async_queue_; @@ -107,8 +113,23 @@ class PedestalTrackingPixelHistogram { void fill_async(NDArray &&image); + /** + * @brief Fill from ordered, parallel-read batches. + * + * After the first batch, reading batch N+1 overlaps histogram processing + * for batch N. Two fixed-capacity read buffers are allocated once and + * reused for the duration of the call. + * + * @param fname input file accepted by File + * @param max_frames maximum frames to process, or all frames for -1 + * @param verbose print periodic progress information + * @param reader_threads number of MultiThreadedFileReader workers + * @param reader_chunk_size frames claimed per reader worker and batch + */ void fill_from_file(const std::filesystem::path &fname, - ssize_t max_frames = -1, bool verbose = false); + ssize_t max_frames = -1, bool verbose = false, + std::size_t reader_threads = 2, + std::size_t reader_chunk_size = 4); void process_pedestal_file(const std::filesystem::path &fname, ssize_t max_frames = -1, bool verbose = false); diff --git a/include/aare/hist/PixelHistogramImpl.hpp b/include/aare/hist/PixelHistogramImpl.hpp index 30b468b9..d68795c8 100644 --- a/include/aare/hist/PixelHistogramImpl.hpp +++ b/include/aare/hist/PixelHistogramImpl.hpp @@ -10,6 +10,7 @@ silently dropped. #include "aare/NDArray.hpp" #include "aare/NDView.hpp" +#include #include #include #include @@ -37,6 +38,9 @@ template class PixelHistogramImpl { void fill(const NDView &frame); void fill(int row, int col, T value); void fill_unchecked(int row, int col, T value); + // Fill using a row-major pixel index, avoiding repeated 3D stride + // calculation in tiled worker loops. The index is intentionally unchecked. + void fill_flat_unchecked(std::size_t pixel, T value); NDArray values() const; // Zero-copy view of the underlying [rows x cols x n_bins] storage. @@ -98,22 +102,29 @@ void PixelHistogramImpl::fill(int row, int col, T value) { template void PixelHistogramImpl::fill_unchecked(int row, int col, T value) { + const auto pixel = + static_cast(row) * static_cast(m_cols) + + static_cast(col); + fill_flat_unchecked(pixel, value); +} + +template +void PixelHistogramImpl::fill_flat_unchecked(std::size_t pixel, + T value) { if (value < m_xmin || value >= m_xmax) { return; } int bin = static_cast((value - m_xmin) * m_scale); // Guard against floating-point rounding pushing val just below - // xmax to bin == n_bins. - if (bin >= m_n_bins) { - bin = m_n_bins - 1; - } + bin = std::clamp(bin, 0, m_n_bins - 1); + auto &cell = m_values.data()[pixel * static_cast(m_n_bins) + + static_cast(bin)]; if constexpr (std::is_integral_v) { - if (m_values(row, col, bin) >= - std::numeric_limits::max()) { + if (cell >= std::numeric_limits::max()) { return; } } - ++m_values(row, col, bin); + ++cell; } template diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 401bf26b..74f35ce3 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -39,7 +39,9 @@ set(PYTHON_FILES aare/ClusterFinder.py aare/ClusterVector.py aare/Cluster.py + aare/FastPedestal.py aare/calibration.py + aare/factory.py aare/experimental.py aare/RawFile.py aare/transform.py diff --git a/python/aare/Cluster.py b/python/aare/Cluster.py index f2ea07eb..7946567d 100644 --- a/python/aare/Cluster.py +++ b/python/aare/Cluster.py @@ -1,6 +1,6 @@ from . import _aare import numpy as np -from .ClusterFinder import _type_to_char +from .factory import _type_to_char def Cluster(x : int, y : int, data, cluster_size=(3,3), dtype = np.int32): @@ -21,4 +21,4 @@ def Cluster(x : int, y : int, data, cluster_size=(3,3), dtype = np.int32): except AttributeError: raise ValueError(f"Unsupported combination of type and cluster size: {dtype}/{cluster_size} when requesting {class_name}") - return cls(x, y, data) \ No newline at end of file + return cls(x, y, data) diff --git a/python/aare/ClusterFinder.py b/python/aare/ClusterFinder.py index eae7ad7d..93af877a 100644 --- a/python/aare/ClusterFinder.py +++ b/python/aare/ClusterFinder.py @@ -1,21 +1,10 @@ # SPDX-License-Identifier: MPL-2.0 from . import _aare import numpy as np +from .factory import _type_to_char _supported_cluster_sizes = [(2,2), (3,3), (5,5), (7,7), (9,9),] -def _type_to_char(dtype): - if dtype == np.int32: - return 'i' - elif dtype == np.float32: - return 'f' - elif dtype == np.float64: - return 'd' - elif dtype == np.int16: - return 'i16' - else: - raise ValueError(f"Unsupported dtype: {dtype}. Only np.int32, np.float32, and np.float64 are supported.") - def _get_class(name, cluster_size, dtype): """ Helper function to get the class based on the name, cluster size, and dtype. diff --git a/python/aare/FastPedestal.py b/python/aare/FastPedestal.py new file mode 100644 index 00000000..38bbeb5b --- /dev/null +++ b/python/aare/FastPedestal.py @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: MPL-2.0 + +import numpy as np + +from .factory import _get_typed_class + + +def _get_fast_pedestal_class(dtype): + return _get_typed_class("FastPedestal", dtype) + + +def FastPedestal(rows, cols, n_samples=1000, dtype=np.float64): + """Create an empty per-pixel running pedestal. + + This factory hides the dtype suffix used by the templated C++ bindings. + Call ``add_init_frame()`` exactly ``n_samples`` times before using the + statistics or calling ``push_ema()``. Subsequent frames have weight + ``1 / n_samples`` in the running mean and population variance. + + Args: + rows: Number of image rows. + cols: Number of image columns. + n_samples: Initialization frame count and steady-state update-weight + denominator. + dtype: Output dtype for the mean, variance, and standard deviation. + Supported values are ``np.float64``, ``np.float32``, and + ``np.int16``. + """ + cls = _get_fast_pedestal_class(dtype) + return cls(rows, cols, n_samples) + + +def from_file(filename, n_samples=1000, skip_first=0, dtype=np.float64): + """Create a FastPedestal from frames in a file. + + After ignoring ``skip_first`` frames, the next ``n_samples`` frames + initialize the pedestal. Every remaining frame is then applied as a + steady-state update. Input frames are read as uint16 data. + + Args: + filename: Input image file. + n_samples: Number of frames used for initialization. + skip_first: Number of leading frames to ignore. + dtype: Output dtype for the mean, variance, and standard deviation. + + Raises: + RuntimeError: If fewer than ``n_samples`` frames remain after + ``skip_first`` or ``n_samples`` is zero. + """ + cls = _get_fast_pedestal_class(dtype) + return cls.from_file( + filename, n_samples=n_samples, skip_first=skip_first + ) + + +FastPedestal.from_file = from_file diff --git a/python/aare/__init__.py b/python/aare/__init__.py index 84224613..72c2573b 100644 --- a/python/aare/__init__.py +++ b/python/aare/__init__.py @@ -5,6 +5,16 @@ from . import _aare from . import transform from . import experimental +from ._aare import ( + FastPedestal_d, + FastPedestal_f, + FastPedestal_i16, + Pedestal_d, + Pedestal_f, + Pedestal_i16, + ClusterFinder_Cluster3x3i, + VarClusterFinder, +) from ._aare import ( File, JungfrauDataFile, @@ -20,6 +30,7 @@ from ._aare import corner # from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i from ._version import __version__ +from .FastPedestal import FastPedestal from .ClusterFinder import ClusterFinder, ClusterCollector, ClusterFinderMT, ClusterFileSink, ClusterFile from .ClusterVector import ClusterVector from .Cluster import Cluster diff --git a/python/aare/factory.py b/python/aare/factory.py new file mode 100644 index 00000000..4da3fdbe --- /dev/null +++ b/python/aare/factory.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: MPL-2.0 + +import numpy as np + +from . import _aare + + +_TYPE_TO_CHAR = { + np.dtype(np.int32): "i", + np.dtype(np.float32): "f", + np.dtype(np.float64): "d", + np.dtype(np.int16): "i16", +} + + +def _type_to_char(dtype): + """Return the suffix used by bindings instantiated for ``dtype``.""" + try: + return _TYPE_TO_CHAR[np.dtype(dtype)] + except (KeyError, TypeError): + supported = ", ".join(str(dtype) for dtype in _TYPE_TO_CHAR) + raise ValueError( + f"Unsupported dtype: {dtype}. Supported dtypes are {supported}." + ) from None + + +def _get_typed_class(name, dtype): + """Return a bound class named ``_``.""" + class_name = f"{name}_{_type_to_char(dtype)}" + try: + return getattr(_aare, class_name) + except AttributeError: + raise ValueError( + f"Unsupported dtype for {name}: {dtype} " + f"(binding {class_name} is not available)." + ) from None diff --git a/python/src/bind_Cluster.hpp b/python/src/bind_Cluster.hpp index eca0881b..5416d04f 100644 --- a/python/src/bind_Cluster.hpp +++ b/python/src/bind_Cluster.hpp @@ -10,7 +10,6 @@ #include namespace py = pybind11; -using pd_type = double; using namespace aare; diff --git a/python/src/bind_ClusterCollector.hpp b/python/src/bind_ClusterCollector.hpp index 2e621eda..01dd03c2 100644 --- a/python/src/bind_ClusterCollector.hpp +++ b/python/src/bind_ClusterCollector.hpp @@ -6,6 +6,8 @@ #include "aare/ClusterVector.hpp" #include "aare/NDView.hpp" #include "aare/Pedestal.hpp" + +#include "module_config.hpp" #include "np_helper.hpp" #include @@ -15,7 +17,6 @@ #include namespace py = pybind11; -using pd_type = double; using namespace aare; @@ -30,7 +31,7 @@ void define_ClusterCollector(py::module &m, const std::string &typestr) { using ClusterType = Cluster; py::class_>(m, class_name.c_str()) - .def(py::init *>()) + .def(py::init *>()) .def("stop", &ClusterCollector::stop) .def( "steal_clusters", diff --git a/python/src/bind_ClusterFileSink.hpp b/python/src/bind_ClusterFileSink.hpp index ade574d4..b366886a 100644 --- a/python/src/bind_ClusterFileSink.hpp +++ b/python/src/bind_ClusterFileSink.hpp @@ -15,8 +15,6 @@ #include namespace py = pybind11; -using pd_type = double; - using namespace aare; #pragma GCC diagnostic push @@ -29,6 +27,8 @@ void define_ClusterFileSink(py::module &m, const std::string &typestr) { using ClusterType = Cluster; + // TODO! adapt to set pedestal type (needs templating of ClusterFileSink) + // or maybe access through base class? py::class_>(m, class_name.c_str()) .def(py::init *, const std::filesystem::path &>()) diff --git a/python/src/bind_ClusterFinder.hpp b/python/src/bind_ClusterFinder.hpp index da479dc5..c13d3305 100644 --- a/python/src/bind_ClusterFinder.hpp +++ b/python/src/bind_ClusterFinder.hpp @@ -6,6 +6,8 @@ #include "aare/ClusterVector.hpp" #include "aare/NDView.hpp" #include "aare/Pedestal.hpp" + +#include "module_config.hpp" #include "np_helper.hpp" #include @@ -15,7 +17,6 @@ #include namespace py = pybind11; -using pd_type = double; using namespace aare; @@ -48,6 +49,8 @@ void define_ClusterFinder(py::module &m, const std::string &typestr) { }) .def("clear_pedestal", &ClusterFinder::clear_pedestal) + .def("update_threshold", + &ClusterFinder::update_threshold) .def_property_readonly( "pedestal", [](ClusterFinder &self) { diff --git a/python/src/bind_ClusterFinderMT.hpp b/python/src/bind_ClusterFinderMT.hpp index 4bb81012..252cc2f8 100644 --- a/python/src/bind_ClusterFinderMT.hpp +++ b/python/src/bind_ClusterFinderMT.hpp @@ -6,6 +6,8 @@ #include "aare/ClusterVector.hpp" #include "aare/NDView.hpp" #include "aare/Pedestal.hpp" + +#include "module_config.hpp" #include "np_helper.hpp" #include @@ -15,7 +17,6 @@ #include namespace py = pybind11; -using pd_type = double; using namespace aare; @@ -31,9 +32,10 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) { py::class_>( m, class_name.c_str()) - .def(py::init, pd_type, size_t, size_t>(), + .def(py::init, pd_type, size_t, size_t, size_t>(), py::arg("image_size"), py::arg("n_sigma") = 5.0, - py::arg("capacity") = 2048, py::arg("n_threads") = 3) + py::arg("capacity") = 2048, py::arg("n_threads") = 3, + py::arg("queue_depth") = 16) .def("push_pedestal_frame", [](ClusterFinderMT &self, py::array_t frame) { @@ -46,7 +48,6 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) { py::array_t frame, uint64_t frame_number) { auto view = make_view_2d(frame); self.find_clusters(view, frame_number); - return; }, py::arg(), py::arg("frame_number") = 0) .def_property_readonly( @@ -56,6 +57,8 @@ void define_ClusterFinderMT(py::module &m, const std::string &typestr) { }) .def("clear_pedestal", &ClusterFinderMT::clear_pedestal) + .def("update_threshold", + &ClusterFinderMT::update_threshold) .def("sync", &ClusterFinderMT::sync) .def("stop", &ClusterFinderMT::stop) .def("start", &ClusterFinderMT::start) diff --git a/python/src/bind_ClusterVector.hpp b/python/src/bind_ClusterVector.hpp index a3d59f57..8c863183 100644 --- a/python/src/bind_ClusterVector.hpp +++ b/python/src/bind_ClusterVector.hpp @@ -15,7 +15,6 @@ #include namespace py = pybind11; -using pd_type = double; using namespace aare; diff --git a/python/src/bind_FastPedestal.hpp b/python/src/bind_FastPedestal.hpp new file mode 100644 index 00000000..c1911e52 --- /dev/null +++ b/python/src/bind_FastPedestal.hpp @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include "aare/FastPedestal.hpp" +#include "np_helper.hpp" + +#include +#include +#include +#include + +namespace py = pybind11; + +template +void define_fast_pedestal_bindings(py::module &m, const std::string &name) { + + py::class_>( + m, name.c_str(), + "Maintain a per-pixel running mean, population variance, and " + "standard deviation.", + py::buffer_protocol()) + .def(py::init(), py::arg("rows"), + py::arg("cols"), py::arg("n_samples"), + "Construct an empty pedestal. It becomes ready after n_samples " + "calls to add_init_frame().") + .def(py::init(), py::arg("rows"), py::arg("cols"), + "Construct an empty pedestal with n_samples=1000.") + + .def( + "mean", + [](FastPedestal &self) { + auto mean = new NDArray{}; + *mean = self.mean(); + return return_image_data(mean); + }, + "Return a copy of the cached mean. The pedestal must be ready.") + .def( + "var", + [](FastPedestal &self) { + auto variance = new NDArray{}; + *variance = self.variance(); + return return_image_data(variance); + }, + "Return the population variance, normalized by n_samples, as a " + "NumPy array. The pedestal must be ready.") + .def( + "std", + [](FastPedestal &self) { + auto standard_deviation = new NDArray{}; + *standard_deviation = self.std(); + return return_image_data(standard_deviation); + }, + "Return the population standard deviation as a NumPy array. The " + "pedestal must be ready.") + .def( + "view", + [](py::object self_py) { + return py::module_::import("numpy").attr("asarray")(self_py); + }, + "Return a non-owning, non-writable NumPy view of the cached mean. " + "The pedestal must be ready.") + + // We need to buffer protocol to allow for numpy operations using the + // pedestal mean + .def_buffer([](FastPedestal &self) { + auto mean = self.view(); + return py::buffer_info( + const_cast(mean.data()), sizeof(SUM_TYPE), + py::format_descriptor::format(), 2, + {static_cast(mean.shape(0)), + static_cast(mean.shape(1))}, + {static_cast(mean.strides()[0] * sizeof(SUM_TYPE)), + static_cast(mean.strides()[1] * + sizeof(SUM_TYPE))}, + true); + }) + // Subtracting a FastPedestal from a NumPy array + .def( + "__array_ufunc__", + [](py::object self, py::object ufunc, const std::string &method, + py::args inputs, py::kwargs kwargs) -> py::object { + if (method != "__call__" || inputs.size() != 2 || + inputs[1].ptr() != self.ptr() || + py::cast(ufunc.attr("__name__")) != + "subtract") { + return py::reinterpret_borrow( + Py_NotImplemented); + } + + auto mean = + py::module_::import("builtins").attr("memoryview")(self); + return ufunc(inputs[0], mean, **kwargs); + }, + "Support subtracting a FastPedestal from a NumPy array.") + .def("clear", py::overload_cast<>(&FastPedestal::clear), + "Reset all statistics and initialization state to zero.") + .def_property_readonly("rows", &FastPedestal::rows, + "Number of image rows.") + .def_property_readonly("cols", &FastPedestal::cols, + "Number of image columns.") + .def_property_readonly("cur_samples", + &FastPedestal::cur_samples, + "Number of initialization frames accumulated. " + "Steady-state pushes do not change it.") + .def_property_readonly( + "ready", &FastPedestal::ready, + "Whether n_samples initialization frames have been accumulated.") + .def_property_readonly( + "n_samples", &FastPedestal::n_samples, + "Initialization frame count and steady-state update-weight " + "denominator.") + .def( + "clone", + [](FastPedestal &pedestal) { + return FastPedestal(pedestal); + }, + "Return an independent copy of the pedestal and its state.") + .def( + "push_ema", + [](FastPedestal &pedestal, + py::array_t &frame) { + pedestal.push_ema(make_view_2d(frame)); + }, + py::arg("frame").noconvert(), + "Update exponential moving average. The pedstal must " + "already be ready for this update.") + .def( + "add_init_frame", + [](FastPedestal &pedestal, + py::array_t &frame) { + pedestal.add_init_frame(make_view_2d(frame)); + }, + py::arg("frame").noconvert(), + "Accumulate one uint16 initialization frame. Call exactly " + "n_samples times to make the pedestal ready.") + .def_static( + "from_file", + [](const std::filesystem::path &filename, uint32_t n_samples, + uint32_t skip_first) { + return FastPedestal::template from_file( + filename, n_samples, skip_first); + }, + py::arg("filename"), py::arg("n_samples") = 1000, + py::arg("skip_first") = 0, + "Create a pedestal from a uint16 file. Skip skip_first frames, use " + "the next n_samples for initialization, then apply every remaining " + "frame as a steady-state update."); +} diff --git a/python/src/bind_PedestalTrackingPixelHistogram.hpp b/python/src/bind_PedestalTrackingPixelHistogram.hpp index 79842732..35b8b24a 100644 --- a/python/src/bind_PedestalTrackingPixelHistogram.hpp +++ b/python/src/bind_PedestalTrackingPixelHistogram.hpp @@ -153,9 +153,13 @@ void define_pedestal_tracking_pixel_histogram_bindings(py::module &m) { Args: file_path: Path to the file to fill from max_frames: Maximum number of frames to fill from the file (default: -1) + reader_threads: Number of parallel file reader workers (default: 2) + reader_chunk_size: Frames claimed by each reader worker per batch (default: 4) )", py::call_guard(), py::arg("fname"), - py::arg("max_frames") = -1, py::arg("verbose") = false) + py::arg("max_frames") = -1, py::arg("verbose") = false, + py::arg("reader_threads") = std::size_t{2}, + py::arg("reader_chunk_size") = std::size_t{4}) .def("process_pedestal_file", &PedestalTrackingPixelHistogram::process_pedestal_file, R"( diff --git a/python/src/module.cpp b/python/src/module.cpp index 52c830b9..d53baf4f 100644 --- a/python/src/module.cpp +++ b/python/src/module.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 // Files with bindings to the different classes +#include "module_config.hpp" + // New style file naming #include "bind_Cluster.hpp" #include "bind_ClusterCollector.hpp" @@ -11,6 +13,7 @@ #include "bind_ClusterVector.hpp" #include "bind_Defs.hpp" #include "bind_Eta.hpp" +#include "bind_FastPedestal.hpp" #include "bind_Interpolator.hpp" #include "bind_MultiThreadedFileReader.hpp" #include "bind_PedestalTrackingPixelHistogram.hpp" @@ -30,6 +33,7 @@ #include "var_cluster.hpp" // Pybind stuff +#include #include #include @@ -75,6 +79,10 @@ PYBIND11_MODULE(_aare, m) { define_pedestal_tracking_pixel_histogram_bindings(m); define_pedestal_bindings(m, "Pedestal_d"); define_pedestal_bindings(m, "Pedestal_f"); + define_pedestal_bindings(m, "Pedestal_i16"); + define_fast_pedestal_bindings(m, "FastPedestal_d"); + define_fast_pedestal_bindings(m, "FastPedestal_f"); + define_fast_pedestal_bindings(m, "FastPedestal_i16"); define_fit_bindings(m); define_interpolation_bindings(m); define_jungfrau_data_file_io_bindings(m); @@ -106,6 +114,7 @@ PYBIND11_MODULE(_aare, m) { DEFINE_BINDINGS_CLUSTERFINDER(int, 3, 3, uint16_t, i); DEFINE_BINDINGS_CLUSTERFINDER(double, 3, 3, uint16_t, d); DEFINE_BINDINGS_CLUSTERFINDER(float, 3, 3, uint16_t, f); + DEFINE_BINDINGS_CLUSTERFINDER(int16_t, 3, 3, uint16_t, i16); DEFINE_BINDINGS_CLUSTERFINDER(int, 5, 5, uint16_t, i); DEFINE_BINDINGS_CLUSTERFINDER(double, 5, 5, uint16_t, d); diff --git a/python/src/module_config.hpp b/python/src/module_config.hpp new file mode 100644 index 00000000..e2f4e8a7 --- /dev/null +++ b/python/src/module_config.hpp @@ -0,0 +1,5 @@ +#pragma once +#include + +// Configure module wide pedestal type for cluster finding +using pd_type = double; \ No newline at end of file diff --git a/python/src/pedestal.hpp b/python/src/pedestal.hpp index 238848b7..229f3adf 100644 --- a/python/src/pedestal.hpp +++ b/python/src/pedestal.hpp @@ -52,6 +52,12 @@ void define_pedestal_bindings(py::module &m, const std::string &name) { *std = self.std(); return return_image_data(std); }) + .def("cached_std", + [](Pedestal &self) { + auto standard_deviation = new NDArray{}; + *standard_deviation = self.cached_std(); + return return_image_data(standard_deviation); + }) .def( "__array_ufunc__", [](py::object self, py::object ufunc, const std::string &method, diff --git a/python/tests/test_Cluster.py b/python/tests/test_Cluster.py index cafb118e..fece7273 100644 --- a/python/tests/test_Cluster.py +++ b/python/tests/test_Cluster.py @@ -103,15 +103,18 @@ def test_max_sum(): def test_cluster_finder(): """Test ClusterFinder""" + shape = [100,100] + cf = _aare.ClusterFinder_Cluster3x3i(shape) - clusterfinder = _aare.ClusterFinder_Cluster3x3i([100,100]) + #Push 1000 frames to the pedestal + for i in range(1000): + frame = np.random.normal(loc = 100, scale = 5, size = shape).astype(np.uint16) + cf.push_pedestal_frame(frame) + cf.update_threshold() + frame = np.zeros(shape=shape, dtype=np.uint16) + cf.find_clusters(frame) - #frame = np.random.rand(100,100) - frame = np.zeros(shape=[100,100]) - - clusterfinder.find_clusters(frame) - - clusters = clusterfinder.steal_clusters(False) #conversion does not work + clusters = cf.steal_clusters(False) #conversion does not work assert clusters.size == 0 diff --git a/python/tests/test_FastPedestal.py b/python/tests/test_FastPedestal.py new file mode 100644 index 00000000..24c115fc --- /dev/null +++ b/python/tests/test_FastPedestal.py @@ -0,0 +1,145 @@ +import numpy as np +import pytest + +from aare import ( + FastPedestal, + FastPedestal_d, + FastPedestal_f, + FastPedestal_i16, +) + + +@pytest.mark.parametrize( + ("dtype", "pedestal_type"), + [ + (np.float64, FastPedestal_d), + (np.float32, FastPedestal_f), + (np.int16, FastPedestal_i16), + ], +) +def test_fast_pedestal_factory(dtype, pedestal_type): + pedestal = FastPedestal(2, 3, n_samples=4, dtype=dtype) + + assert isinstance(pedestal, pedestal_type) + assert pedestal.rows == 2 + assert pedestal.cols == 3 + assert pedestal.n_samples == 4 + + +def test_fast_pedestal_factory_defaults_to_double(): + assert isinstance(FastPedestal(2, 3), FastPedestal_d) + + +def test_fast_pedestal_factory_rejects_unbound_dtype(): + with pytest.raises(ValueError, match="Unsupported dtype for FastPedestal"): + FastPedestal(2, 3, dtype=np.int32) + + +@pytest.mark.parametrize( + ("kwargs", "expected_n_samples"), + [ + ({"rows": 2, "cols": 3}, 1000), + ({"rows": 2, "cols": 3, "n_samples": 4}, 4), + ], +) +def test_fast_pedestal_binding_accepts_constructor_keywords( + kwargs, expected_n_samples +): + pedestal = FastPedestal_d(**kwargs) + + assert pedestal.rows == 2 + assert pedestal.cols == 3 + assert pedestal.n_samples == expected_n_samples + + +@pytest.mark.parametrize( + ("dtype", "pedestal_type", "expected_dtype"), + [ + (np.float64, FastPedestal_d, np.float64), + (np.float32, FastPedestal_f, np.float32), + (np.int16, FastPedestal_i16, np.int16), + ], +) +def test_fast_pedestal_factory_from_file( + tmp_path, dtype, pedestal_type, expected_dtype +): + frames = np.array( + [[[100, 100]], [[2, 4]], [[4, 6]], [[5, 7]]], dtype=np.uint16 + ) + filename = tmp_path / "frames.npy" + np.save(filename, frames) + + pedestal = FastPedestal.from_file( + filename, n_samples=2, skip_first=1, dtype=dtype + ) + + assert isinstance(pedestal, pedestal_type) + assert pedestal.ready + assert pedestal.cur_samples == 2 + assert pedestal.mean().dtype == expected_dtype + np.testing.assert_array_equal(pedestal.mean(), [[4, 6]]) + + +def test_fast_pedestal_factory_from_file_rejects_unbound_dtype(): + with pytest.raises(ValueError, match="Unsupported dtype for FastPedestal"): + FastPedestal.from_file("unused.npy", dtype=np.int32) + + +def test_fast_pedestal_from_file_rejects_skip_beyond_end(tmp_path): + filename = tmp_path / "frames.npy" + np.save(filename, np.zeros((1, 1, 1), dtype=np.uint16)) + + with pytest.raises(RuntimeError, match="less frames"): + FastPedestal.from_file(filename, n_samples=1, skip_first=2) + + +@pytest.mark.parametrize( + ("pedestal_type", "expected_dtype"), + [(FastPedestal_d, np.float64), (FastPedestal_f, np.float32)], +) +def test_fast_pedestal_initialization(pedestal_type, expected_dtype): + pedestal = pedestal_type(2, 3, 2) + first = np.array([[2, 4, 6], [8, 10, 12]], dtype=np.uint16) + second = np.array([[4, 6, 8], [10, 12, 14]], dtype=np.uint16) + + pedestal.add_init_frame(first) + pedestal.add_init_frame(second) + + + expected_mean = np.array( + [[3, 5, 7], [9, 11, 13]], dtype=expected_dtype + ) + np.testing.assert_array_equal(pedestal.mean(), expected_mean) + np.testing.assert_array_equal(pedestal.std(), np.ones((2, 3))) + + +def test_fast_pedestal_steady_state_push_ema(): + pedestal = FastPedestal_d(1, 2, 2) + pedestal.add_init_frame(np.array([[2, 4]], dtype=np.uint16)) + pedestal.add_init_frame(np.array([[4, 6]], dtype=np.uint16)) + + + pedestal.push_ema(np.array([[6, 8]], dtype=np.uint16)) + + np.testing.assert_array_equal(pedestal.mean(), [[4.5, 6.5]]) + + +def test_fast_pedestal_exposes_read_only_buffer_and_subtraction(): + pedestal = FastPedestal_d(1, 2, 1) + pedestal.add_init_frame(np.array([[2, 4]], dtype=np.uint16)) + + + view = np.asarray(pedestal) + result = np.array([[12, 14]], dtype=np.uint16) - pedestal + + np.testing.assert_array_equal(view, [[2, 4]]) + np.testing.assert_array_equal(result, [[10, 10]]) + assert np.shares_memory(view, pedestal.view()) + assert not view.flags.writeable + + +def test_fast_pedestal_rejects_wrong_shape(): + pedestal = FastPedestal_d(2, 3) + + with pytest.raises(RuntimeError, match="shape"): + pedestal.add_init_frame(np.zeros((2, 2), dtype=np.uint16)) diff --git a/src/ClusterFinderMT.test.cpp b/src/ClusterFinderMT.test.cpp index e8904814..fae8b247 100644 --- a/src/ClusterFinderMT.test.cpp +++ b/src/ClusterFinderMT.test.cpp @@ -21,14 +21,21 @@ class ClusterFinderMTWrapper public: ClusterFinderMTWrapper(Shape<2> image_size, PEDESTAL_TYPE nSigma = 5.0, - size_t capacity = 2000, size_t n_threads = 3) + size_t capacity = 2000, size_t n_threads = 3, + size_t queue_depth = 16) : ClusterFinderMT( - image_size, nSigma, capacity, n_threads) {} + image_size, nSigma, capacity, n_threads, queue_depth) {} size_t get_m_input_queues_size() const { return this->m_input_queues.size(); } + size_t get_m_frame_pools_size() const { return this->m_frame_pools.size(); } + + size_t get_frame_pool_depth(size_t thread_index) const { + return this->m_frame_pools[thread_index]->size(); + } + size_t get_m_output_queues_size() const { return this->m_output_queues.size(); } @@ -100,3 +107,73 @@ TEST_CASE("multithreaded cluster finder", "[.with-data]") { auto clustervec = clustercollector.steal_clusters(); // CHECK(clustervec.size() == ) //dont know how many clusters to expect } + +TEST_CASE("frame buffers are recycled when pushing more frames than the pool " + "holds", + "[.files]") { + using ClusterType = Cluster; + + const size_t n_threads = 2; + const size_t queue_depth = 4; + const size_t n_frames = 5 * queue_depth; + const Shape<2> image_size{10, 10}; + + ClusterFinderMTWrapper cf(image_size, 5, 200, n_threads, + queue_depth); + + CHECK(cf.get_m_frame_pools_size() == n_threads); + for (size_t i = 0; i < n_threads; ++i) { + CHECK(cf.get_frame_pool_depth(i) == queue_depth); + } + + NDArray frame(image_size, 0); + + // More frames than the pool holds, so this only completes if the workers + // return the buffers to the free list. + for (size_t i = 0; i < n_frames; ++i) { + cf.find_clusters(frame.view(), i); + } + + cf.stop(); + + CHECK(cf.m_input_queues_are_empty() == true); +} + +TEST_CASE("cluster collector accepts finders with a matching cluster type") { + using ClusterType = Cluster; + using Finder = ClusterFinderMTWrapper; + using OtherFinder = + ClusterFinderMTWrapper, uint16_t, float>; + + static_assert( + std::is_constructible_v, Finder *>); + static_assert( + !std::is_constructible_v, OtherFinder *>); + + Finder cf({10, 10}); + cf.stop(); + + ClusterCollector collector(&cf); + collector.stop(); + + CHECK(collector.steal_clusters().empty()); +} + +TEST_CASE("cluster collector drains queued clusters when stopped") { + using ClusterType = Cluster; + ProducerConsumerQueue> source(4); + + for (uint64_t frame_number = 1; frame_number <= 3; ++frame_number) { + REQUIRE(source.write(ClusterVector(4, frame_number))); + } + + ClusterCollector collector(&source); + collector.stop(); + + auto clusters = collector.steal_clusters(); + REQUIRE(clusters.size() == 3); + CHECK(source.isEmpty()); + for (size_t i = 0; i < clusters.size(); ++i) { + CHECK(clusters[i].frame_number() == static_cast(i + 1)); + } +} diff --git a/src/ClusterVector.test.cpp b/src/ClusterVector.test.cpp index 33b0b98c..2c098e14 100644 --- a/src/ClusterVector.test.cpp +++ b/src/ClusterVector.test.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 #include "aare/ClusterVector.hpp" #include +#include #include #include @@ -21,6 +22,29 @@ TEST_CASE("After pushing back one element the ClusterVector is not empty") { REQUIRE(!cv.empty()); } +TEST_CASE("ClusterVector move constructor preserves contents") { + ClusterVector source(4, 42); + source.push_back(C1{1, 2, {3, 4, 5, 6}}); + + ClusterVector moved(std::move(source)); + + REQUIRE(moved.size() == 1); + CHECK(moved.frame_number() == 42); + CHECK(moved[0].data[3] == 6); +} + +TEST_CASE("ClusterVector move assignment preserves contents") { + ClusterVector source(4, 42); + source.push_back(C1{1, 2, {3, 4, 5, 6}}); + ClusterVector moved(1); + + moved = std::move(source); + + REQUIRE(moved.size() == 1); + CHECK(moved.frame_number() == 42); + CHECK(moved[0].data[3] == 6); +} + TEST_CASE("item_size return the size of the cluster stored") { ClusterVector cv(4); CHECK(cv.item_size() == sizeof(C1)); @@ -275,4 +299,4 @@ TEST_CASE("Gain Map Calculation Index Map") { CHECK(index_map_x == clustertestdata.index_map_x); CHECK(index_map_y == clustertestdata.index_map_y); -} \ No newline at end of file +} diff --git a/src/hist/PedestalTrackingPixelHistogram.cpp b/src/hist/PedestalTrackingPixelHistogram.cpp index 0695c2f3..f02004ab 100644 --- a/src/hist/PedestalTrackingPixelHistogram.cpp +++ b/src/hist/PedestalTrackingPixelHistogram.cpp @@ -1,11 +1,16 @@ #include "aare/hist/PedestalTrackingPixelHistogram.hpp" #include "aare/File.hpp" +#include "aare/MultiThreadedFileReader.hpp" #include +#include #include #include #include +#include +#include #include +#include #include #include @@ -202,17 +207,8 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { switch (kind) { case WorkKind::PushPedestal: { - // Accumulate raw frame values into this thread's pedestal - // shard. Uses the pixel-level push_no_update which only - // touches m_sum/m_sum2/m_cur_samples (no m_mean writes). - for (int local_row = 0; local_row < local_rows; ++local_row) { - const auto row = static_cast(first_row + local_row); - for (ssize_t col = 0; col < image->shape(1); ++col) { - my_pedestal.template push_no_update( - static_cast(local_row), - static_cast(col), (*image)(row, col)); - } - } + auto frame = image->sub_view(first_row, first_row + local_rows); + my_pedestal.add_init_frame(frame); break; } case WorkKind::UpdateMean: { @@ -220,7 +216,6 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { // thread's shard. Also refresh the cached per-pixel std so // FillWithThreshold can read it without recomputing on the // hot path. - my_pedestal.update_mean(); auto &my_std = partial_std_[thread_id]; for (int local_row = 0; local_row < local_rows; ++local_row) { for (int col = 0; col < cols_; ++col) { @@ -240,59 +235,63 @@ void PedestalTrackingPixelHistogram::worker_loop(int thread_id) { // tracking gate entirely. The [xmin, xmax) histogram gate // lives inside PixelHistogramImpl::fill. const auto n_sigma = n_sigma_.load(std::memory_order_relaxed); - if (n_sigma <= AxisType{0.0}) { - // Fill without pedestal tracking. - for (const auto &frame : *images) { - const auto cols = frame.shape(1); - for (int local_row = 0; local_row < local_rows; - ++local_row) { - const auto row = - static_cast(first_row + local_row); - for (ssize_t col = 0; col < cols; ++col) { - const FrameType raw = frame(row, col); - const AxisType val = - static_cast(raw) - - static_cast(my_pedestal.mean( - static_cast(local_row), - static_cast(col))); - my_hist.fill_unchecked(local_row, - static_cast(col), val); - } - } - } - break; - } else { - // Do pedestal tracking. Duplicated code for clean hot path. + constexpr std::size_t pixel_tile_size = 256; + const auto local_pixels = static_cast(local_rows) * + static_cast(cols_); + const auto global_pixel_begin = + static_cast(first_row) * + static_cast(cols_); + const auto &frames = *images; - auto &my_std = partial_std_[thread_id]; - for (const auto &frame : *images) { - const auto cols = frame.shape(1); - for (int local_row = 0; local_row < local_rows; - ++local_row) { - const auto row = - static_cast(first_row + local_row); - for (ssize_t col = 0; col < cols; ++col) { - const FrameType raw = frame(row, col); + // Compile the shared traversal once for each mode. if constexpr + // removes the tracking gate entirely from the histogram-only hot + // path, while keeping the runtime mode decision outside the + // per-pixel loops. + const auto fill_tiles = [&](auto tracking, + const AxisType *std_data) { + constexpr bool track_pedestal = decltype(tracking)::value; + + // Each worker retains exclusive ownership of its row shard. + // Pixel tiling keeps a bounded part of that shard's + // [pixel x bin] storage hot while input remains contiguous. + for (std::size_t tile_begin = 0; tile_begin < local_pixels; + tile_begin += pixel_tile_size) { + const auto tile_end = + std::min(tile_begin + pixel_tile_size, local_pixels); + for (const auto &frame : frames) { + const auto *input = + frame.data() + global_pixel_begin + tile_begin; + for (std::size_t local_pixel = tile_begin; + local_pixel < tile_end; ++local_pixel, ++input) { + const FrameType raw = *input; const AxisType val = static_cast(raw) - - static_cast(my_pedestal.mean( - static_cast(local_row), - static_cast(col))); - my_hist.fill_unchecked(local_row, - static_cast(col), val); - const AxisType sigma = my_std(local_row, col); - if (sigma > AxisType{0.0} && - std::abs(static_cast(val)) < - n_sigma * sigma) { - my_pedestal.template push( - static_cast(local_row), - static_cast(col), raw); + my_pedestal.mean_unchecked( + static_cast(local_pixel)); + my_hist.fill_flat_unchecked(local_pixel, val); + + if constexpr (track_pedestal) { + const AxisType sigma = std_data[local_pixel]; + if (sigma > AxisType{0.0} && + std::abs(val) < n_sigma * sigma) { + my_pedestal.push_ema_unchecked( + local_pixel, raw); + } } } } } - break; + }; + + if (n_sigma <= AxisType{0.0}) { + fill_tiles(std::false_type{}, nullptr); + } else { + // Frames stay chronological for every pixel. Accepted raw + // values update that pixel's sums and cached mean immediately; + // pixels are otherwise independent. + fill_tiles(std::true_type{}, partial_std_[thread_id].data()); } + break; } } @@ -395,6 +394,11 @@ void PedestalTrackingPixelHistogram::fill_async(NDArray &&image) { "constructor shape"); } + // ProducerConsumerQueue is SPSC. Serialising this short producer-side + // operation also prevents fill_async from racing fill_from_file's direct + // batch dispatch. + std::lock_guard ingestion_lock(ingestion_mutex_); + // SPSC backpressure: spin with a short sleep until a slot frees up. // The std::move only consumes `image` on the iteration that succeeds // (placement-new inside write() runs only when the slot is free). @@ -467,35 +471,42 @@ PedestalTrackingPixelHistogram::bin_edges() const { } void PedestalTrackingPixelHistogram::fill_from_file( - const std::filesystem::path &fname, ssize_t max_frames, bool verbose) { + const std::filesystem::path &fname, ssize_t max_frames, bool verbose, + std::size_t reader_threads, std::size_t reader_chunk_size) { constexpr std::size_t progress_interval = 66; auto last = std::chrono::steady_clock::now(); - const auto completed_start = - completed_async_fills_.load(std::memory_order_acquire); std::size_t last_reported = 0; - const auto completed_for_this_file = [&]() { - return completed_async_fills_.load(std::memory_order_acquire) - - completed_start; - }; - const auto wait_for_completed = [&](std::size_t target) { - while (completed_for_this_file() < target) { - std::this_thread::sleep_for(async_wait_); - } - }; + if (max_frames < -1) { + throw std::invalid_argument( + "PedestalTrackingPixelHistogram max_frames must be -1 or " + "non-negative"); + } - File f(fname); - // check that row col matches constructor - if (f.rows() != static_cast(rows_) || - f.cols() != static_cast(cols_)) { + // Preserve the old max_frames behaviour: values beyond EOF are clamped + // rather than rejected by MultiThreadedFileReader. + const auto source_total_frames = File(fname).total_frames(); + const auto n_frames = max_frames == -1 + ? source_total_frames + : std::min(static_cast(max_frames), + source_total_frames); + + experimental::MultiThreadedFileReader reader(fname, reader_threads, + reader_chunk_size, n_frames); + if (reader.rows() != static_cast(rows_) || + reader.cols() != static_cast(cols_)) { throw std::invalid_argument("PedestalTrackingPixelHistogram: Frame in " "file {} has shape ({}, {}) does not match " "constructor shape"); } + if (reader.dtype() != Dtype::UINT16 || + reader.bytes_per_frame() != static_cast(rows_) * + static_cast(cols_) * + sizeof(FrameType)) { + throw std::invalid_argument( + "PedestalTrackingPixelHistogram requires uint16 file frames"); + } - const ssize_t total_frames = f.total_frames(); - const ssize_t n_frames = - max_frames == -1 ? total_frames : std::min(max_frames, total_frames); const auto print_progress = [&](std::size_t done) { const auto now = std::chrono::steady_clock::now(); const double dt = std::chrono::duration(now - last).count(); @@ -503,30 +514,89 @@ void PedestalTrackingPixelHistogram::fill_from_file( const double fps = dt > 0.0 ? static_cast(done_in_interval) / dt : 0.0; - fmt::print( - "\rProgress: {}/{} ({:.1f}%) {:.1f} FPS ", done, n_frames, - 100.0 * static_cast(done) / static_cast(n_frames), - fps); + fmt::print("\rProgress: {}/{} ({:.1f}%) {:.1f} FPS ", done, + n_frames, + n_frames == 0 ? 100.0 + : 100.0 * static_cast(done) / + static_cast(n_frames), + fps); std::fflush(stdout); last = now; last_reported = done; }; - for (ssize_t i = 0; i < n_frames; ++i) { - aare::NDArray frame({rows_, cols_}); - f.read_into(reinterpret_cast(frame.data())); - fill_async(std::move(frame)); + using ReadBuffer = NDArray; + const auto read_into_buffer = [&reader](ReadBuffer &buffer) { + const auto frame_count = reader.next_read_frames(); + const auto frames_read = reader.read_into(buffer.buffer()); + if (frames_read != frame_count) { + throw std::runtime_error( + "MultiThreadedFileReader returned an incomplete batch"); + } + return frames_read; + }; - if (verbose && (i + 1) % progress_interval == 0) { - wait_for_completed(static_cast(i + 1)); - print_progress(static_cast(i + 1)); + const auto fill_batch = [this](ReadBuffer &buffer, + std::size_t frame_count) { + std::vector> views; + views.reserve(frame_count); + auto batch_view = buffer.view(); + for (std::size_t i = 0; i < frame_count; ++i) { + views.push_back(batch_view(i)); + } + std::lock_guard fill_lock(fill_mutex_); + dispatch_fill_batch_(views); + }; + + // Exclude other queue producers for the duration. Drain anything already + // submitted before bypassing the coordinator with direct batch dispatch. + std::lock_guard ingestion_lock(ingestion_mutex_); + flush(); + + std::size_t completed = 0; + if (n_frames != 0) { + // Allocate the maximum wave size once per buffer. The last read may + // contain fewer frames; its returned count limits the views passed to + // the histogram workers, leaving the unused tail untouched. + const auto buffer_capacity = reader.next_read_frames(); + std::array buffers{ + ReadBuffer({static_cast(buffer_capacity), rows_, cols_}), + ReadBuffer({static_cast(buffer_capacity), rows_, cols_})}; + + std::size_t current_index = 0; + auto current_frames = read_into_buffer(buffers[current_index]); + while (true) { + const bool has_next = completed + current_frames < n_frames; + const std::size_t next_index = current_index ^ std::size_t{1}; + + // MultiThreadedFileReader::read_into is blocking. Run the next + // read on a dedicated prefetch task while the current batch is + // processed by the histogram worker pool. + std::future next; + if (has_next) { + next = std::async(std::launch::async, read_into_buffer, + std::ref(buffers[next_index])); + } + + fill_batch(buffers[current_index], current_frames); + completed += current_frames; + + if (verbose && (completed - last_reported >= progress_interval || + completed == n_frames)) { + print_progress(completed); + } + + if (!has_next) { + break; + } + current_frames = next.get(); + current_index = next_index; } } - flush(); + if (verbose) { - const auto done = completed_for_this_file(); - if (done > last_reported) { - print_progress(done); + if (completed > last_reported || n_frames == 0) { + print_progress(completed); } fmt::print("\n\n"); std::fflush(stdout); diff --git a/src/hist/PedestalTrackingPixelHistogram.test.cpp b/src/hist/PedestalTrackingPixelHistogram.test.cpp new file mode 100644 index 00000000..2c9f1590 --- /dev/null +++ b/src/hist/PedestalTrackingPixelHistogram.test.cpp @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: MPL-2.0 +#include "aare/hist/PedestalTrackingPixelHistogram.hpp" + +#include "aare/File.hpp" +#include "aare/Frame.hpp" +#include "aare/NumpyFile.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using aare::FileConfig; +using aare::Frame; +using aare::NumpyFile; +using aare::PedestalTrackingPixelHistogram; + +namespace { + +class TemporaryHistogramFile { + public: + using Generator = + std::function; + + TemporaryHistogramFile(std::size_t rows = 2, std::size_t cols = 3, + std::size_t frames = 10, Generator generator = {}) { + const auto unique = + std::chrono::steady_clock::now().time_since_epoch().count(); + path_ = std::filesystem::temp_directory_path() / + ("aare-pedestal-hist-" + std::to_string(unique) + ".npy"); + + FileConfig config; + config.dtype = aare::Dtype::UINT16; + config.rows = rows; + config.cols = cols; + NumpyFile file(path_, "w", config); + for (std::size_t frame_index = 0; frame_index < frames; ++frame_index) { + Frame frame(rows, cols, config.dtype); + auto image = frame.view(); + for (ssize_t row = 0; row < image.shape(0); ++row) { + for (ssize_t col = 0; col < image.shape(1); ++col) { + image(row, col) = + generator ? generator(frame_index, + static_cast(row), + static_cast(col)) + : static_cast(frame_index); + } + } + file.write(frame); + } + } + + ~TemporaryHistogramFile() { std::filesystem::remove(path_); } + + TemporaryHistogramFile(const TemporaryHistogramFile &) = delete; + TemporaryHistogramFile &operator=(const TemporaryHistogramFile &) = delete; + + const std::filesystem::path &path() const { return path_; } + + private: + std::filesystem::path path_; +}; + +} // namespace + +TEST_CASE("Pedestal tracking histogram fills ordered multi-reader batches", + "[PedestalTrackingPixelHistogram]") { + TemporaryHistogramFile file; + PedestalTrackingPixelHistogram histogram(2, 3, 10, 0.0f, 10.0f, 2, 4, 0.0f); + + // Two reader workers claiming two frames each produces a full batch of + // four followed by a partial batch of three. + histogram.fill_from_file(file.path(), 7, false, 2, 2); + const auto values = histogram.values(); + + for (ssize_t row = 0; row < 2; ++row) { + for (ssize_t col = 0; col < 3; ++col) { + for (ssize_t bin = 0; bin < 10; ++bin) { + CHECK(values(row, col, bin) == (bin < 7 ? 1 : 0)); + } + } + } +} + +TEST_CASE("Pedestal tracking file fill handles limits and reader options", + "[PedestalTrackingPixelHistogram]") { + TemporaryHistogramFile file; + PedestalTrackingPixelHistogram histogram(2, 3, 10, 0.0f, 10.0f, 2, 4, 0.0f); + + CHECK_NOTHROW(histogram.fill_from_file(file.path(), 0, false, 2, 2)); + CHECK_THROWS_AS(histogram.fill_from_file(file.path(), -2, false, 2, 2), + std::invalid_argument); + CHECK_THROWS_AS(histogram.fill_from_file(file.path(), -1, false, 0, 2), + std::invalid_argument); + CHECK_THROWS_AS(histogram.fill_from_file(file.path(), -1, false, 2, 0), + std::invalid_argument); + + // Preserve the previous API's clamp-at-EOF behaviour. + histogram.fill_from_file(file.path(), 100, false, 2, 3); + const auto values = histogram.values(); + for (ssize_t row = 0; row < 2; ++row) { + for (ssize_t col = 0; col < 3; ++col) { + for (ssize_t bin = 0; bin < 10; ++bin) { + CHECK(values(row, col, bin) == 1); + } + } + } +} + +TEST_CASE("Pedestal tracking file fill validates frame metadata", + "[PedestalTrackingPixelHistogram]") { + TemporaryHistogramFile wrong_shape(3, 3, 1); + PedestalTrackingPixelHistogram histogram(2, 3, 10, 0.0f, 10.0f, 1, 4, 0.0f); + + CHECK_THROWS_AS( + histogram.fill_from_file(wrong_shape.path(), -1, false, 2, 1), + std::invalid_argument); +} + +TEST_CASE("Histogram-only fill crosses a pixel tile without changing pedestal", + "[PedestalTrackingPixelHistogram]") { + constexpr int rows = 1; + constexpr int cols = 513; + constexpr std::size_t pedestal_samples = 1000; + + const auto baseline = [](std::size_t col) { + return static_cast(100 + col % 17); + }; + TemporaryHistogramFile file( + rows, cols, 2, + [baseline](std::size_t frame, std::size_t, std::size_t col) { + return static_cast(baseline(col) + frame + 1); + }); + + // Negative n_sigma exercises the complete histogram-only condition; zero + // is covered by the ordered batch tests above. + PedestalTrackingPixelHistogram histogram(rows, cols, 4, 0.0f, 4.0f, 1, 6, + -1.0f); + + for (std::size_t seed = 0; seed < pedestal_samples; ++seed) { + aare::NDArray frame({rows, cols}); + const int offset = seed % 2 == 0 ? -1 : 1; + for (ssize_t col = 0; col < cols; ++col) { + frame(0, col) = static_cast( + static_cast(baseline(static_cast(col))) + + offset); + } + histogram.push_pedestal_no_update(frame.view()); + } + histogram.update_mean(); + const auto mean_before = histogram.pedestal_mean(); + + // One two-frame batch traverses the 512-pixel tile and its one-pixel tail. + histogram.fill_from_file(file.path(), -1, false, 1, 2); + + const auto values = histogram.values(); + for (ssize_t col = 0; col < cols; ++col) { + for (ssize_t bin = 0; bin < 4; ++bin) { + CHECK(values(0, col, bin) == ((bin == 1 || bin == 2) ? 1 : 0)); + } + } + + const auto mean_after = histogram.pedestal_mean(); + CHECK( + std::equal(mean_before.begin(), mean_before.end(), mean_after.begin())); +} + +TEST_CASE("Pedestal tracking threshold is strict and rejects zero sigma", + "[PedestalTrackingPixelHistogram]") { + constexpr std::size_t pedestal_samples = 1000; + PedestalTrackingPixelHistogram histogram(1, 3, 8, -4.0f, 4.0f, 1, 4, 2.0f); + + // Pixels 0 and 1 have mean 100 and population sigma 1. Pixel 2 has the + // same mean and zero sigma. + for (std::size_t seed = 0; seed < pedestal_samples; ++seed) { + aare::NDArray frame({1, 3}); + const auto noisy = static_cast(seed % 2 == 0 ? 99 : 101); + frame(0, 0) = noisy; + frame(0, 1) = noisy; + frame(0, 2) = 100; + histogram.push_pedestal_no_update(frame.view()); + } + histogram.update_mean(); + + const auto mean_before = histogram.pedestal_mean(); + REQUIRE(mean_before(0, 0) == 100.0f); + REQUIRE(mean_before(0, 1) == 100.0f); + REQUIRE(mean_before(0, 2) == 100.0f); + + aare::NDArray frame({1, 3}); + frame(0, 0) = 102; // residual == 2 * sigma: excluded + frame(0, 1) = 101; // residual < 2 * sigma: included + frame(0, 2) = 101; // sigma == 0: excluded + histogram.fill_async(std::move(frame)); + histogram.flush(); + + const auto mean_after = histogram.pedestal_mean(); + CHECK(mean_after(0, 0) == 100.0f); + CHECK(mean_after(0, 1) == static_cast(100001.0 * (1.0 / 1000.0))); + CHECK(mean_after(0, 2) == 100.0f); + + // Histogramming uses each residual before a possible EMA update. + const auto values = histogram.values(); + for (ssize_t bin = 0; bin < 8; ++bin) { + CHECK(values(0, 0, bin) == (bin == 6 ? 1 : 0)); + CHECK(values(0, 1, bin) == (bin == 5 ? 1 : 0)); + CHECK(values(0, 2, bin) == (bin == 5 ? 1 : 0)); + } +} + +TEST_CASE("Tiled pedestal tracking matches chronological single-frame fills", + "[PedestalTrackingPixelHistogram]") { + constexpr int rows = 5; + constexpr int cols = 257; + constexpr std::size_t frames = 40; + constexpr int bins = 32; + constexpr float xmin = -16.0f; + constexpr float xmax = 16.0f; + + const auto baseline = [](std::size_t row, std::size_t col) { + return static_cast(100 + (row + col) % 5); + }; + const auto sample = [baseline](std::size_t frame, std::size_t row, + std::size_t col) { + const int offsets[] = {1, -1, 8, 0}; + return static_cast(static_cast(baseline(row, col)) + + offsets[frame % 4]); + }; + + TemporaryHistogramFile file(rows, cols, frames, sample); + PedestalTrackingPixelHistogram tiled(rows, cols, bins, xmin, xmax, 2, 16, + 2.0f); + PedestalTrackingPixelHistogram chronological(rows, cols, bins, xmin, xmax, + 2, 16, 2.0f); + + // Seed a non-zero cached standard deviation. Five rows split over two + // workers make both row shards cross the 512-pixel tile boundary. Forty + // data frames exercise chronological processing across a sizable batch. + for (std::size_t seed = 0; seed < 1000; ++seed) { + aare::NDArray frame({rows, cols}); + const int offset = seed % 2 == 0 ? -2 : 2; + for (ssize_t row = 0; row < rows; ++row) { + for (ssize_t col = 0; col < cols; ++col) { + frame(row, col) = static_cast( + static_cast(baseline(row, col)) + offset); + } + } + tiled.push_pedestal_no_update(frame.view()); + chronological.push_pedestal_no_update(frame.view()); + } + tiled.update_mean(); + chronological.update_mean(); + + // One forty-frame reader wave exercises tiled batch processing. + tiled.fill_from_file(file.path(), -1, false, 2, 20); + + // The reference path establishes the same result one chronological frame + // at a time, without any cross-frame traversal reordering. + for (std::size_t frame_index = 0; frame_index < frames; ++frame_index) { + aare::NDArray frame({rows, cols}); + for (ssize_t row = 0; row < rows; ++row) { + for (ssize_t col = 0; col < cols; ++col) { + frame(row, col) = + sample(frame_index, static_cast(row), + static_cast(col)); + } + } + chronological.fill_async(std::move(frame)); + chronological.flush(); + } + + const auto tiled_values = tiled.values(); + const auto chronological_values = chronological.values(); + REQUIRE(tiled_values.shape() == chronological_values.shape()); + CHECK(std::equal(tiled_values.begin(), tiled_values.end(), + chronological_values.begin())); + + const auto tiled_mean = tiled.pedestal_mean(); + const auto chronological_mean = chronological.pedestal_mean(); + REQUIRE(tiled_mean.shape() == chronological_mean.shape()); + CHECK(std::equal(tiled_mean.begin(), tiled_mean.end(), + chronological_mean.begin())); +} diff --git a/src/hist/PixelHistogramImpl.test.cpp b/src/hist/PixelHistogramImpl.test.cpp index 3cc28424..f3526c17 100644 --- a/src/hist/PixelHistogramImpl.test.cpp +++ b/src/hist/PixelHistogramImpl.test.cpp @@ -72,6 +72,17 @@ TEST_CASE("Fill a small histogram from an NDArray") { REQUIRE(v(1, 1, 4) == 1); } +TEST_CASE("Flat pixel filling uses pixel-major histogram storage") { + aare::PixelHistogramImpl hist(2, 3, 4, 0.0f, 4.0f); + + hist.fill_flat_unchecked(0, 0.5f); + hist.fill_flat_unchecked(4, 2.5f); + + const auto values = hist.view(); + CHECK(values(0, 0, 0) == 1); + CHECK(values(1, 1, 2) == 1); +} + TEST_CASE("Check that pixel histogram does not overflow") { int rows = 1; int cols = 1;