diff --git a/CHANGELOG.md b/CHANGELOG.md index d20097b..d9c31e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `gather_columns`. - Tests that launch `mpiexec -n 2` and `-n 4` jobs, a tutorial ("Moving markers between ranks"), guides and an example. +- Opt-in scope-profiler regions (`mpiparticles.migrate`, `mpiparticles.compact`, ...) tagged + `"mpiparticles"`: off unless the application enables the tag, for example with + `scope_profiler.setup(enable_tags=["mpiparticles"])`. scope-profiler (>= 0.7.5) is a new + dependency. ### Changed diff --git a/docs/src/content/docs/guides/index.md b/docs/src/content/docs/guides/index.md index a9dc394..8976782 100644 --- a/docs/src/content/docs/guides/index.md +++ b/docs/src/content/docs/guides/index.md @@ -10,3 +10,5 @@ sidebar: - [Ids](/mpiparticles/guides/ids/): globally unique int64 ids with a rank stride. - [Decomposition and migration](/mpiparticles/guides/migration/): the `Decomposition` protocol, `BoxDecomposition`, and what `migrate` sends. +- [Profiling](/mpiparticles/guides/profiling/): the opt-in scope-profiler regions and how to turn + them on. diff --git a/docs/src/content/docs/guides/profiling.md b/docs/src/content/docs/guides/profiling.md new file mode 100644 index 0000000..df48e68 --- /dev/null +++ b/docs/src/content/docs/guides/profiling.md @@ -0,0 +1,29 @@ +--- +title: Profiling +description: The scope-profiler regions mpiparticles ships, off unless the application turns them on. +sidebar: + order: 4 +--- + +## Opt-in profiling regions + +mpiparticles times its expensive operations with [scope-profiler](https://github.com/max-models/scope-profiler) +regions named `mpiparticles.`: `migrate`, `keep_owned`, `gather_columns`, and the store's +`reserve` (only when the storage grows), `compact`, `sort_by_key`, `snapshot` and `restore`. They are +tagged `"mpiparticles"`, an opt-in tag, so they stay off (and record nothing) when an application +profiles its own code, until it asks for them: + +```python +import scope_profiler as sp + +sp.setup(enable_tags=["mpiparticles"]) +``` + +`session(enable_tags=...)` and `ProfilingOptions(enable_tags=...)` take the same argument. + +The same works without touching the code: `enable_tags = ["mpiparticles"]` in the TOML +configuration, `SCOPE_PROFILER_ENABLE_TAGS=mpiparticles` in the environment, or +`scope-profiler run --enable-tag mpiparticles script.py`. `"*"` enables every opt-in tag. + +A region that is off costs a few tenths of a microsecond per call, so only operations over all +markers are instrumented, never per-marker work. diff --git a/pyproject.toml b/pyproject.toml index 5162957..88ce1ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ dependencies = [ "maybempi>=0.1.2", "mpiarray>=0.2.1", "numpy", + "scope-profiler>=0.7.5", ] optional-dependencies.dev = [ "jupyterlab", @@ -87,3 +88,4 @@ report.show_missing = true cunumpy = { git = "https://github.com/max-models/cunumpy", branch = "devel" } maybempi = { git = "https://github.com/max-models/maybempi", branch = "devel" } mpiarray = { git = "https://github.com/max-models/mpiarray", branch = "devel" } +scope-profiler = { git = "https://github.com/max-models/scope-profiler", branch = "devel" } diff --git a/src/mpiparticles/__init__.py b/src/mpiparticles/__init__.py index 3943473..1f6b3a0 100644 --- a/src/mpiparticles/__init__.py +++ b/src/mpiparticles/__init__.py @@ -6,6 +6,10 @@ moves the markers that left a rank's subdomain to their owners. Without an MPI launcher everything runs serially, with no MPI calls. +The expensive operations are scope-profiler regions named ``mpiparticles.``, +tagged ``"mpiparticles"`` and off by default; enable them with +``scope_profiler.setup(enable_tags=["mpiparticles"])``. + Example ------- >>> import cunumpy as xp @@ -19,12 +23,18 @@ from importlib.metadata import PackageNotFoundError, version +import scope_profiler as sp + +from mpiparticles._profiling import PROFILE_TAGS from mpiparticles.collective import gather_columns, global_count from mpiparticles.decomposition import BoxDecomposition, Decomposition from mpiparticles.exchange import keep_owned, migrate from mpiparticles.ids import IdAllocator from mpiparticles.store import MarkerView, ParticleStore, StoreSnapshot +# The profiling regions are off unless the application enables the tag. +sp.register_opt_in_tag(*PROFILE_TAGS) + try: __version__ = version("mpiparticles") except PackageNotFoundError: # not installed, e.g. run from a source tree diff --git a/src/mpiparticles/_profiling.py b/src/mpiparticles/_profiling.py new file mode 100644 index 0000000..83be85f --- /dev/null +++ b/src/mpiparticles/_profiling.py @@ -0,0 +1,8 @@ +"""The tag of mpiparticles' own scope-profiler regions. + +The regions are opt-in: they record nothing unless the application enables the +tag, for example with ``scope_profiler.setup(enable_tags=["mpiparticles"])``. +""" + +#: Tags of every mpiparticles region; registered as opt-in in ``__init__``. +PROFILE_TAGS = ("mpiparticles",) diff --git a/src/mpiparticles/collective.py b/src/mpiparticles/collective.py index 9806fef..b8e594a 100644 --- a/src/mpiparticles/collective.py +++ b/src/mpiparticles/collective.py @@ -7,7 +7,9 @@ import cunumpy as xp import maybempi import numpy as np +import scope_profiler as sp +from mpiparticles._profiling import PROFILE_TAGS from mpiparticles.store import ParticleStore @@ -23,6 +25,7 @@ def global_count(store: ParticleStore, comm: Any = None) -> int: return int(comm.allreduce(count, op=maybempi.get_mpi().SUM)) +@sp.profile("mpiparticles.gather_columns", tags=PROFILE_TAGS) def gather_columns(array: Any, comm: Any = None, root: int | None = 0) -> Any: """Concatenate component-major ``(ncomp, n)`` or ``(n,)`` arrays over the ranks. diff --git a/src/mpiparticles/exchange.py b/src/mpiparticles/exchange.py index 4495ce1..45009dd 100644 --- a/src/mpiparticles/exchange.py +++ b/src/mpiparticles/exchange.py @@ -4,7 +4,9 @@ import cunumpy as xp import mpiarray as mpa +import scope_profiler as sp +from mpiparticles._profiling import PROFILE_TAGS from mpiparticles._types import Array from mpiparticles.decomposition import Decomposition from mpiparticles.store import ParticleStore @@ -19,6 +21,7 @@ def _leavers(store: ParticleStore, decomposition: Decomposition) -> Array: return xp.nonzero(store.alive[:end] & ~owned)[0] +@sp.profile("mpiparticles.migrate", tags=PROFILE_TAGS) def migrate(store: ParticleStore, decomposition: Decomposition) -> tuple[int, int]: """Send the markers that left this rank's subdomain to their owners. @@ -89,6 +92,7 @@ def migrate(store: ParticleStore, decomposition: Decomposition) -> tuple[int, in return count, received +@sp.profile("mpiparticles.keep_owned", tags=PROFILE_TAGS) def keep_owned(store: ParticleStore, decomposition: Decomposition) -> int: """Drop the markers this rank does not own (not collective). diff --git a/src/mpiparticles/store.py b/src/mpiparticles/store.py index 40f4502..24f4db8 100644 --- a/src/mpiparticles/store.py +++ b/src/mpiparticles/store.py @@ -6,7 +6,9 @@ from typing import Any import cunumpy as xp +import scope_profiler as sp +from mpiparticles._profiling import PROFILE_TAGS from mpiparticles._types import Array from mpiparticles.ids import IdAllocator @@ -202,14 +204,27 @@ def reserve(self, n: int) -> None: capacity = self.capacity if n <= capacity: return - while capacity < n: - capacity *= 2 - old = (self._positions, self._velocities, self._weights, self._alive, self._ids) - self._allocate(capacity) - end = self._length - new = (self._positions, self._velocities, self._weights, self._alive, self._ids) - for target, source in zip(new, old, strict=True): - target[..., :end] = source[..., :end] + with sp.region("mpiparticles.reserve", tags=PROFILE_TAGS): + while capacity < n: + capacity *= 2 + old = ( + self._positions, + self._velocities, + self._weights, + self._alive, + self._ids, + ) + self._allocate(capacity) + end = self._length + new = ( + self._positions, + self._velocities, + self._weights, + self._alive, + self._ids, + ) + for target, source in zip(new, old, strict=True): + target[..., :end] = source[..., :end] # -- adding and removing -------------------------------------------------- @@ -322,6 +337,7 @@ def _keep_first(self, count: int) -> None: self._clear_rows(count, self._length) self._length = count + @sp.profile("mpiparticles.compact", tags=PROFILE_TAGS) def compact(self) -> None: """Move the alive markers to the front, in order, and drop the dead ones.""" end = self._length @@ -362,18 +378,19 @@ def sort_by_key(self, keys: Array) -> None: keys = xp.asarray(keys) if keys.shape != (end,): raise ValueError(f"keys must have shape ({end},), got {keys.shape}") - rows = xp.nonzero(self._alive[:end])[0] - count = int(rows.size) - if count: - _, order = xp.algorithms.sort_by_key(keys[rows]) - rows = rows[order] - # one contiguous row at a time: faster than a 2-D column gather - for array in (self._positions, self._velocities): - for row in range(array.shape[0]): - array[row, :count] = array[row][rows] - for array in (self._weights, self._ids): - array[:count] = array[rows] - self._keep_first(count) + with sp.region("mpiparticles.sort_by_key", tags=PROFILE_TAGS): + rows = xp.nonzero(self._alive[:end])[0] + count = int(rows.size) + if count: + _, order = xp.algorithms.sort_by_key(keys[rows]) + rows = rows[order] + # one contiguous row at a time: faster than a 2-D column gather + for array in (self._positions, self._velocities): + for row in range(array.shape[0]): + array[row, :count] = array[row][rows] + for array in (self._weights, self._ids): + array[:count] = array[rows] + self._keep_first(count) # -- alive markers -------------------------------------------------------- @@ -409,6 +426,7 @@ def set_active_weights(self, weights: Array) -> None: # -- checkpointing -------------------------------------------------------- + @sp.profile("mpiparticles.snapshot", tags=PROFILE_TAGS) def snapshot(self) -> StoreSnapshot: """Return a copy of all markers (alive and dead) and the id counter.""" end = self._length @@ -433,16 +451,17 @@ def restore(self, snapshot: StoreSnapshot) -> None: f"snapshot is for ndim={snapshot.ndim}, nvel={snapshot.nvel}, " f"this store has ndim={self._ndim}, nvel={self._nvel}" ) - end = snapshot.length - self.reserve(end) - self._clear_rows(0, self._length) - self._positions[:, :end] = snapshot.positions - self._velocities[:, :end] = snapshot.velocities - self._weights[:end] = snapshot.weights - self._alive[:end] = snapshot.alive - self._ids[:end] = snapshot.ids - self._length = end - self._allocator.next_id = snapshot.next_id + with sp.region("mpiparticles.restore", tags=PROFILE_TAGS): + end = snapshot.length + self.reserve(end) + self._clear_rows(0, self._length) + self._positions[:, :end] = snapshot.positions + self._velocities[:, :end] = snapshot.velocities + self._weights[:end] = snapshot.weights + self._alive[:end] = snapshot.alive + self._ids[:end] = snapshot.ids + self._length = end + self._allocator.next_id = snapshot.next_id # -- plasmacoll's MarkerSet ----------------------------------------------- diff --git a/src/mpiparticles/tests/test_profiling.py b/src/mpiparticles/tests/test_profiling.py new file mode 100644 index 0000000..619d92d --- /dev/null +++ b/src/mpiparticles/tests/test_profiling.py @@ -0,0 +1,42 @@ +"""mpiparticles' scope-profiler regions are opt-in.""" + +import pytest +import scope_profiler as sp + +from mpiparticles import BoxDecomposition, migrate +from mpiparticles.tests.conftest import FakeComm, filled_store + + +@pytest.fixture(autouse=True) +def _fresh_profiler(monkeypatch): + """Keep the profiling state of one test out of the others.""" + monkeypatch.delenv("SCOPE_PROFILER_ENABLE_TAGS", raising=False) + sp.reset() + yield + sp.reset() + + +def _profiled_region_names(**setup_kwargs): + """Return the regions recorded while migrating and compacting a store.""" + sp.setup(output=None, **setup_kwargs) + store = filled_store(5, capacity=4) # grows once + migrate(store, BoxDecomposition(FakeComm(0, 1), [0, 0], [1, 1], [1, 1])) + store.compact() + with sp.region("user.step"): + pass + results = sp.finalize(verbose=False, return_results=True) + return set(results.region_names) + + +def test_regions_are_off_by_default(): + assert _profiled_region_names() == {"user.step"} + + +def test_enable_tags_turns_the_regions_on(): + names = _profiled_region_names(enable_tags=["mpiparticles"]) + assert names == { + "mpiparticles.compact", + "mpiparticles.migrate", + "mpiparticles.reserve", + "user.step", + } diff --git a/uv.lock b/uv.lock index 5f50075..6e4710b 100644 --- a/uv.lock +++ b/uv.lock @@ -1185,6 +1185,66 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] +[[package]] +name = "h5py" +version = "3.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/33/acd0ce6863b6c0d7735007df01815403f5589a21ff8c2e1ee2587a38f548/h5py-3.16.0.tar.gz", hash = "sha256:a0dbaad796840ccaa67a4c144a0d0c8080073c34c76d5a6941d6818678ef2738", size = 446526, upload-time = "2026-03-06T13:49:08.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/6b/231413e58a787a89b316bb0d1777da3c62257e4797e09afd8d17ad3549dc/h5py-3.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e06f864bedb2c8e7c1358e6c73af48519e317457c444d6f3d332bb4e8fa6d7d9", size = 3724137, upload-time = "2026-03-06T13:47:35.242Z" }, + { url = "https://files.pythonhosted.org/packages/74/f9/557ce3aad0fe8471fb5279bab0fc56ea473858a022c4ce8a0b8f303d64e9/h5py-3.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec86d4fffd87a0f4cb3d5796ceb5a50123a2a6d99b43e616e5504e66a953eca3", size = 3090112, upload-time = "2026-03-06T13:47:37.634Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f5/e15b3d0dc8a18e56409a839e6468d6fb589bc5207c917399c2e0706eeb44/h5py-3.16.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:86385ea895508220b8a7e45efa428aeafaa586bd737c7af9ee04661d8d84a10d", size = 4844847, upload-time = "2026-03-06T13:47:39.811Z" }, + { url = "https://files.pythonhosted.org/packages/cb/92/a8851d936547efe30cc0ce5245feac01f3ec6171f7899bc3f775c72030b3/h5py-3.16.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8975273c2c5921c25700193b408e28d6bdd0111c37468b2d4e25dcec4cd1d84d", size = 5065352, upload-time = "2026-03-06T13:47:41.489Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ae/f2adc5d0ca9626db3277a3d87516e124cbc5d0eea0bd79bc085702d04f2c/h5py-3.16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1677ad48b703f44efc9ea0c3ab284527f81bc4f318386aaaebc5fede6bbae56f", size = 4839173, upload-time = "2026-03-06T13:47:43.586Z" }, + { url = "https://files.pythonhosted.org/packages/64/0b/e0c8c69da1d8838da023a50cd3080eae5d475691f7636b35eff20bb6ef20/h5py-3.16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c4dd4cf5f0a4e36083f73172f6cfc25a5710789269547f132a20975bfe2434c", size = 5076216, upload-time = "2026-03-06T13:47:45.315Z" }, + { url = "https://files.pythonhosted.org/packages/66/35/d88fd6718832133c885004c61ceeeb24dbd6397ef877dbed6b3a64d6a286/h5py-3.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:bdef06507725b455fccba9c16529121a5e1fbf56aa375f7d9713d9e8ff42454d", size = 3183639, upload-time = "2026-03-06T13:47:47.041Z" }, + { url = "https://files.pythonhosted.org/packages/ba/95/a825894f3e45cbac7554c4e97314ce886b233a20033787eda755ca8fecc7/h5py-3.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:719439d14b83f74eeb080e9650a6c7aa6d0d9ea0ca7f804347b05fac6fbf18af", size = 3721663, upload-time = "2026-03-06T13:47:49.599Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/38ff88b347c3e346cda1d3fc1b65a7aa75d40632228d8b8a5d7b58508c24/h5py-3.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3f0a0e136f2e95dd0b67146abb6668af4f1a69c81ef8651a2d316e8e01de447", size = 3087630, upload-time = "2026-03-06T13:47:51.249Z" }, + { url = "https://files.pythonhosted.org/packages/98/a8/2594cef906aee761601eff842c7dc598bea2b394a3e1c00966832b8eeb7c/h5py-3.16.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a6fbc5367d4046801f9b7db9191b31895f22f1c6df1f9987d667854cac493538", size = 4823472, upload-time = "2026-03-06T13:47:53.085Z" }, + { url = "https://files.pythonhosted.org/packages/52/a0/c1f604538ff6db22a0690be2dc44ab59178e115f63c917794e529356ab23/h5py-3.16.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fb1720028d99040792bb2fb31facb8da44a6f29df7697e0b84f0d79aff2e9bd3", size = 5027150, upload-time = "2026-03-06T13:47:55.043Z" }, + { url = "https://files.pythonhosted.org/packages/2e/fd/301739083c2fc4fd89950f9bcfce75d6e14b40b0ca3d40e48a8993d1722c/h5py-3.16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:314b6054fe0b1051c2b0cb2df5cbdab15622fb05e80f202e3b6a5eee0d6fe365", size = 4814544, upload-time = "2026-03-06T13:47:56.893Z" }, + { url = "https://files.pythonhosted.org/packages/4c/42/2193ed41ccee78baba8fcc0cff2c925b8b9ee3793305b23e1f22c20bf4c7/h5py-3.16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ffbab2fedd6581f6aa31cf1639ca2cb86e02779de525667892ebf4cc9fd26434", size = 5034013, upload-time = "2026-03-06T13:47:59.01Z" }, + { url = "https://files.pythonhosted.org/packages/f7/20/e6c0ff62ca2ad1a396a34f4380bafccaaf8791ff8fccf3d995a1fc12d417/h5py-3.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:17d1f1630f92ad74494a9a7392ab25982ce2b469fc62da6074c0ce48366a2999", size = 3191673, upload-time = "2026-03-06T13:48:00.626Z" }, + { url = "https://files.pythonhosted.org/packages/f2/48/239cbe352ac4f2b8243a8e620fa1a2034635f633731493a7ff1ed71e8658/h5py-3.16.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b9c49dd58dc44cf70af944784e2c2038b6f799665d0dcbbc812a26e0faa859", size = 2673834, upload-time = "2026-03-06T13:48:02.579Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c0/5d4119dba94093bbafede500d3defd2f5eab7897732998c04b54021e530b/h5py-3.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c5313566f4643121a78503a473f0fb1e6dcc541d5115c44f05e037609c565c4d", size = 3685604, upload-time = "2026-03-06T13:48:04.198Z" }, + { url = "https://files.pythonhosted.org/packages/b0/42/c84efcc1d4caebafb1ecd8be4643f39c85c47a80fe254d92b8b43b1eadaf/h5py-3.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:42b012933a83e1a558c673176676a10ce2fd3759976a0fedee1e672d1e04fc9d", size = 3061940, upload-time = "2026-03-06T13:48:05.783Z" }, + { url = "https://files.pythonhosted.org/packages/89/84/06281c82d4d1686fde1ac6b0f307c50918f1c0151062445ab3b6fa5a921d/h5py-3.16.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ff24039e2573297787c3063df64b60aab0591980ac898329a08b0320e0cf2527", size = 5198852, upload-time = "2026-03-06T13:48:07.482Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/1a19e42cd43cc1365e127db6aae85e1c671da1d9a5d746f4d34a50edb577/h5py-3.16.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:dfc21898ff025f1e8e67e194965a95a8d4754f452f83454538f98f8a3fcb207e", size = 5405250, upload-time = "2026-03-06T13:48:09.628Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/9790c1655eabeb85b92b1ecab7d7e62a2069e53baefd58c98f0909c7a948/h5py-3.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:698dd69291272642ffda44a0ecd6cd3bda5faf9621452d255f57ce91487b9794", size = 5190108, upload-time = "2026-03-06T13:48:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/51/d7/ab693274f1bd7e8c5f9fdd6c7003a88d59bedeaf8752716a55f532924fbb/h5py-3.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b2c02b0a160faed5fb33f1ba8a264a37ee240b22e049ecc827345d0d9043074", size = 5419216, upload-time = "2026-03-06T13:48:13.322Z" }, + { url = "https://files.pythonhosted.org/packages/03/c1/0976b235cf29ead553e22f2fb6385a8252b533715e00d0ae52ed7b900582/h5py-3.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:96b422019a1c8975c2d5dadcf61d4ba6f01c31f92bbde6e4649607885fe502d6", size = 3182868, upload-time = "2026-03-06T13:48:15.759Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/866b7e570b39070f92d47b0ff1800f0f8239b6f9e45f02363d7112336c1f/h5py-3.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:39c2838fb1e8d97bcf1755e60ad1f3dd76a7b2a475928dc321672752678b96db", size = 2653286, upload-time = "2026-03-06T13:48:17.279Z" }, + { url = "https://files.pythonhosted.org/packages/0f/9e/6142ebfda0cb6e9349c091eae73c2e01a770b7659255248d637bec54a88b/h5py-3.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:370a845f432c2c9619db8eed334d1e610c6015796122b0e57aa46312c22617d9", size = 3671808, upload-time = "2026-03-06T13:48:19.737Z" }, + { url = "https://files.pythonhosted.org/packages/b0/65/5e088a45d0f43cd814bc5bec521c051d42005a472e804b1a36c48dada09b/h5py-3.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:42108e93326c50c2810025aade9eac9d6827524cdccc7d4b75a546e5ab308edb", size = 3045837, upload-time = "2026-03-06T13:48:21.854Z" }, + { url = "https://files.pythonhosted.org/packages/da/1e/6172269e18cc5a484e2913ced33339aad588e02ba407fafd00d369e22ef3/h5py-3.16.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:099f2525c9dcf28de366970a5fb34879aab20491589fa89ce2863a84218bb524", size = 5193860, upload-time = "2026-03-06T13:48:24.071Z" }, + { url = "https://files.pythonhosted.org/packages/bd/98/ef2b6fe2903e377cbe870c3b2800d62552f1e3dbe81ce49e1923c53d1c5c/h5py-3.16.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9300ad32dea9dfc5171f94d5f6948e159ed93e4701280b0f508773b3f582f402", size = 5400417, upload-time = "2026-03-06T13:48:25.728Z" }, + { url = "https://files.pythonhosted.org/packages/bc/81/5b62d760039eed64348c98129d17061fdfc7839fc9c04eaaad6dee1004e4/h5py-3.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:171038f23bccddfc23f344cadabdfc9917ff554db6a0d417180d2747fe4c75a7", size = 5185214, upload-time = "2026-03-06T13:48:27.436Z" }, + { url = "https://files.pythonhosted.org/packages/28/c4/532123bcd9080e250696779c927f2cb906c8bf3447df98f5ceb8dcded539/h5py-3.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e420b539fb6023a259a1b14d4c9f6df8cf50d7268f48e161169987a57b737ff", size = 5414598, upload-time = "2026-03-06T13:48:29.49Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d9/a27997f84341fc0dfcdd1fe4179b6ba6c32a7aa880fdb8c514d4dad6fba3/h5py-3.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:18f2bbcd545e6991412253b98727374c356d67caa920e68dc79eab36bf5fedad", size = 3175509, upload-time = "2026-03-06T13:48:31.131Z" }, + { url = "https://files.pythonhosted.org/packages/a5/23/bb8647521d4fd770c30a76cfc6cb6a2f5495868904054e92f2394c5a78ff/h5py-3.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:656f00e4d903199a1d58df06b711cf3ca632b874b4207b7dbec86185b5c8c7d4", size = 2647362, upload-time = "2026-03-06T13:48:33.411Z" }, + { url = "https://files.pythonhosted.org/packages/48/3c/7fcd9b4c9eed82e91fb15568992561019ae7a829d1f696b2c844355d95dd/h5py-3.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9c9d307c0ef862d1cd5714f72ecfafe0a5d7529c44845afa8de9f46e5ba8bd65", size = 3678608, upload-time = "2026-03-06T13:48:35.183Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b7/9366ed44ced9b7ef357ab48c94205280276db9d7f064aa3012a97227e966/h5py-3.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8c1eff849cdd53cbc73c214c30ebdb6f1bb8b64790b4b4fc36acdb5e43570210", size = 3054773, upload-time = "2026-03-06T13:48:37.139Z" }, + { url = "https://files.pythonhosted.org/packages/58/a5/4964bc0e91e86340c2bbda83420225b2f770dcf1eb8a39464871ad769436/h5py-3.16.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:e2c04d129f180019e216ee5f9c40b78a418634091c8782e1f723a6ca3658b965", size = 5198886, upload-time = "2026-03-06T13:48:38.879Z" }, + { url = "https://files.pythonhosted.org/packages/f1/16/d905e7f53e661ce2c24686c38048d8e2b750ffc4350009d41c4e6c6c9826/h5py-3.16.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:e4360f15875a532bc7b98196c7592ed4fc92672a57c0a621355961cafb17a6dd", size = 5404883, upload-time = "2026-03-06T13:48:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f2/58f34cb74af46d39f4cd18ea20909a8514960c5a3e5b92fd06a28161e0a8/h5py-3.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3fae9197390c325e62e0a1aa977f2f62d994aa87aab182abbea85479b791197c", size = 5192039, upload-time = "2026-03-06T13:48:43.117Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ca/934a39c24ce2e2db017268c08da0537c20fa0be7e1549be3e977313fc8f5/h5py-3.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:43259303989ac8adacc9986695b31e35dba6fd1e297ff9c6a04b7da5542139cc", size = 5421526, upload-time = "2026-03-06T13:48:44.838Z" }, + { url = "https://files.pythonhosted.org/packages/3e/14/615a450205e1b56d16c6783f5ccd116cde05550faad70ae077c955654a75/h5py-3.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:fa48993a0b799737ba7fd21e2350fa0a60701e58180fae9f2de834bc39a147ab", size = 3183263, upload-time = "2026-03-06T13:48:47.117Z" }, + { url = "https://files.pythonhosted.org/packages/7b/48/a6faef5ed632cae0c65ac6b214a6614a0b510c3183532c521bdb0055e117/h5py-3.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:1897a771a7f40d05c262fc8f37376ec37873218544b70216872876c627640f63", size = 2663450, upload-time = "2026-03-06T13:48:48.707Z" }, + { url = "https://files.pythonhosted.org/packages/5d/32/0c8bb8aedb62c772cf7c1d427c7d1951477e8c2835f872bc0a13d1f85f86/h5py-3.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:15922e485844f77c0b9d275396d435db3baa58292a9c2176a386e072e0cf2491", size = 3760693, upload-time = "2026-03-06T13:48:50.453Z" }, + { url = "https://files.pythonhosted.org/packages/1d/1f/fcc5977d32d6387c5c9a694afee716a5e20658ac08b3ff24fdec79fb05f2/h5py-3.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:df02dd29bd247f98674634dfe41f89fd7c16ba3d7de8695ec958f58404a4e618", size = 3181305, upload-time = "2026-03-06T13:48:52.221Z" }, + { url = "https://files.pythonhosted.org/packages/f5/a1/af87f64b9f986889884243643621ebbd4ac72472ba8ec8cec891ac8e2ca1/h5py-3.16.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:0f456f556e4e2cebeebd9d66adf8dc321770a42593494a0b6f0af54a7567b242", size = 5074061, upload-time = "2026-03-06T13:48:54.089Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d0/146f5eaff3dc246a9c7f6e5e4f42bd45cc613bce16693bcd4d1f7c958bf5/h5py-3.16.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:3e6cb3387c756de6a9492d601553dffea3fe11b5f22b443aac708c69f3f55e16", size = 5279216, upload-time = "2026-03-06T13:48:56.75Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9d/12a13424f1e604fc7df9497b73c0356fb78c2fb206abd7465ce47226e8fd/h5py-3.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8389e13a1fd745ad2856873e8187fd10268b2d9677877bb667b41aebd771d8b7", size = 5070068, upload-time = "2026-03-06T13:48:59.169Z" }, + { url = "https://files.pythonhosted.org/packages/41/8c/bbe98f813722b4873818a8db3e15aa3e625b59278566905ac439725e8070/h5py-3.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:346df559a0f7dcb31cf8e44805319e2ab24b8957c45e7708ce503b2ec79ba725", size = 5300253, upload-time = "2026-03-06T13:49:02.033Z" }, + { url = "https://files.pythonhosted.org/packages/32/9e/87e6705b4d6890e7cecdf876e2a7d3e40654a2ae37482d79a6f1b87f7b92/h5py-3.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4c6ab014ab704b4feaa719ae783b86522ed0bf1f82184704ed3c9e4e3228796e", size = 3381671, upload-time = "2026-03-06T13:49:04.351Z" }, + { url = "https://files.pythonhosted.org/packages/96/91/9fad90cfc5f9b2489c7c26ad897157bce82f0e9534a986a221b99760b23b/h5py-3.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:faca8fb4e4319c09d83337adc80b2ca7d5c5a343c2d6f1b6388f32cfecca13c1", size = 2740706, upload-time = "2026-03-06T13:49:06.347Z" }, +] + [[package]] name = "httpcore" version = "1.0.9" @@ -2180,6 +2240,7 @@ dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scope-profiler" }, ] [package.optional-dependencies] @@ -2237,6 +2298,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'" }, { name = "pytest-cov", marker = "extra == 'test'" }, { name = "ruff", marker = "extra == 'dev'" }, + { name = "scope-profiler", git = "https://github.com/max-models/scope-profiler?branch=devel" }, ] provides-extras = ["dev", "docs", "mpi", "test"] @@ -3452,6 +3514,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/c9/0f2a4dea06a5e12c1191ee3d96b1e1233797d0d5e18f97a2b909a95a9836/ruff-0.17.1-py3-none-win_arm64.whl", hash = "sha256:42400b9a6ff8515ff27796f07f92e0195be011c86e81e381606f58ea5502fef4", size = 9853542, upload-time = "2026-10-15T15:23:16.05Z" }, ] +[[package]] +name = "scope-profiler" +version = "0.7.5" +source = { git = "https://github.com/max-models/scope-profiler?branch=devel#2abb4ca4fe74166be554d30c3b78af8041f930ae" } +dependencies = [ + { name = "h5py" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, + { name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "tabulate" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] + [[package]] name = "send2trash" version = "2.1.0" @@ -3493,6 +3568,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "tabulate" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, +] + [[package]] name = "terminado" version = "0.18.1"