Source code for modelly.space.continuous.vectorised

"""Whole-population operations on a continuous space.

``neighbors_within`` on the space answers "who is near whom" with one KD-tree.
The two helpers here complete the vectorised step: ``pairs_within`` returns the
same neighbour relation as index pairs, ready for ``np.add.at`` or ``bincount``
aggregation, and ``move_all`` writes every agent's new position in one call.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

if TYPE_CHECKING:
    from modelly.space.continuous.space import (
        ContinuousSpace,
    )


[docs] def pairs_within( space: ContinuousSpace, radius: float ) -> tuple[np.ndarray, np.ndarray]: """Every unordered pair of agents within ``radius``, as two row arrays ``(i, j)``. Rows index ``space.agent_positions`` and ``space.active_agents``; ``i < j`` and each pair appears once. On a torus the tree is periodic. """ from scipy.spatial import cKDTree # noqa: PLC0415 - scipy is heavy positions = space.agent_positions if positions.shape[0] == 0: empty = np.empty(0, dtype=np.intp) return empty, empty if space.torus: positions = np.mod(positions - space.dimensions[:, 0], space.size) tree = cKDTree(positions, boxsize=space.size) else: tree = cKDTree(positions) pairs = tree.query_pairs(radius, output_type="ndarray") return pairs[:, 0], pairs[:, 1]
[docs] def difference_vectors( space: ContinuousSpace, i: np.ndarray, j: np.ndarray ) -> np.ndarray: """The vector from agent ``i`` to agent ``j`` for each pair, torus-corrected.""" positions = space.agent_positions delta = positions[j] - positions[i] if space.torus: size = space.size delta = np.mod(delta + size / 2, size) - size / 2 return delta
[docs] def move_all(space: ContinuousSpace, positions: np.ndarray) -> None: """Set every agent's position at once; wraps on a torus, clips otherwise.""" positions = np.asarray(positions, dtype=float) low = space.dimensions[:, 0] if space.torus: positions = np.mod(positions - low, space.size) + low else: positions = np.clip(positions, low, space.dimensions[:, 1]) space.agent_positions[:] = positions