Source code for nvalchemiops.torch.neighbors

# SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""PyTorch neighbor list API.

This module provides the main entry point for PyTorch users of the neighbor list API.
"""

from __future__ import annotations

from typing import Any

import torch

from nvalchemiops.neighbors.base_dispatch import (
    NEIGHBOR_LIST_STRATEGIES,
    neighbor_list_strategy_run_args,
)
from nvalchemiops.neighbors.cell_list._grid_selection import _validate_grid_policy
from nvalchemiops.torch.neighbors._compiled_pair_fn import (
    CompiledPairFn,
    compile_pair_fn,
)
from nvalchemiops.torch.neighbors._dispatch import (
    _auto_method_from_geometry,
    _reject_unsupported_cluster_tile_combo,
    _squeeze_single_system_cell_pbc,
    broadcast_shared_cell_for_batch,
    estimate_neighbor_list_costs,
    suggest_neighbor_list_method,
)

# Batch cell list functions
from nvalchemiops.torch.neighbors.batch_cell_list import (
    batch_cell_list,
    estimate_batch_cell_list_sizes,
)

# Batched cluster-pair tile functions
from nvalchemiops.torch.neighbors.batch_cluster_tile import (
    batch_cluster_tile_neighbor_list,
)

# Batch naive functions
from nvalchemiops.torch.neighbors.batch_naive import (
    batch_naive_neighbor_list,
)

# Batch naive dual cutoff functions
from nvalchemiops.torch.neighbors.batch_naive_dual_cutoff import (
    batch_naive_neighbor_list_dual_cutoff,
)

# Unbatched cell list functions
from nvalchemiops.torch.neighbors.cell_list import (
    cell_list,
    estimate_cell_list_sizes,
)

# Unbatched cluster-pair tile functions
from nvalchemiops.torch.neighbors.cluster_tile import (
    cluster_tile_neighbor_list,
)

# Unbatched naive functions
from nvalchemiops.torch.neighbors.naive import (
    naive_neighbor_list,
)

# Unbatched naive dual cutoff functions
from nvalchemiops.torch.neighbors.naive_dual_cutoff import (
    naive_neighbor_list_dual_cutoff,
)

# Utility functions
from nvalchemiops.torch.neighbors.neighbor_utils import (
    NeighborOverflowError,
    TileBufferOverflow,
    prepare_batch_idx_ptr,
    synthesize_cell_for_batch,
    synthesize_cell_for_ss,
)
from nvalchemiops.torch.neighbors.prepared_neighbor_list import (
    NeighborListState,
    _execute_prepared_neighbor_list,
    prepare_neighbor_list,
)


[docs] def neighbor_list( positions: torch.Tensor, cutoff: float | None = None, cell: torch.Tensor | None = None, pbc: torch.Tensor | None = None, batch_idx: torch.Tensor | None = None, batch_ptr: torch.Tensor | None = None, cutoff2: float | None = None, half_fill: bool = False, fill_value: int | None = None, return_neighbor_list: bool = False, method: str | None = None, wrap_positions: bool = True, *, grid_policy: str = "configured", state: NeighborListState | None = None, **kwargs: Any, ): """Compute a neighbor list or execute a prepared Torch route. Without ``state``, this entry point selects and executes an algorithm from the supplied geometry and options. With a state returned by :func:`prepare_neighbor_list`, it bypasses repeated selection and sizing, uses state-owned route storage, and returns the ordinary route tuple. Prepared state is the recommended high-level interface for repeated Torch execution. Parameters ---------- positions : torch.Tensor, shape (total_atoms, 3) Concatenated atomic coordinates for all systems in Cartesian space. Each row represents one atom's (x, y, z) position. Unwrapped (box-crossing) coordinates are supported when PBC is used; the kernel wraps positions internally. cutoff : float, optional Cutoff distance for neighbor detection in Cartesian units. Must be positive. Atoms within this distance are considered neighbors. May be omitted when ``state`` is supplied. cell : torch.Tensor, shape (3, 3) or (num_systems, 3, 3), optional Cell matrix defining the simulation box. pbc : torch.Tensor, shape (3,) or (num_systems, 3), dtype=torch.bool, optional Periodic boundary condition flags for each dimension. batch_idx : torch.Tensor, shape (total_atoms,), dtype=torch.int32, optional System index for each atom. Must be **sorted by system** (i.e., atoms in system 0 first, then system 1, and so on). Interleaved layouts are not supported by ``cluster_tile`` / ``batch_cluster_tile`` and will silently emit cross-system pairs. For ``cell_list`` / ``naive`` methods, interleaved layouts work but ``batch_ptr`` will still be derived assuming a contiguous layout. batch_ptr : torch.Tensor, shape (num_systems + 1,), dtype=torch.int32, optional Cumulative atom counts defining system boundaries. cutoff2 : float, optional Second cutoff distance for neighbor detection in Cartesian units. Must be positive. Naive dual-cutoff routes require ``cutoff2 >= cutoff``; cluster-tile routes accept either ordering. Atoms within this distance are considered neighbors. half_fill : bool, optional If True, only store half of the neighbor relationships to avoid double counting. Another half could be reconstructed by swapping source and target indices and inverting unit shifts. fill_value : int | None, optional Value to fill the neighbor matrix with. Default is total_atoms. return_neighbor_list : bool, optional - default = False If True, convert the neighbor matrix to a neighbor list (idx_i, idx_j) format by creating a mask over the fill_value, which can incur a performance penalty. We recommend using the neighbor matrix format, and only convert to a neighbor list format if absolutely necessary. method : str | None, optional Method to use for neighbor list computation. Choices: "naive", "cell_list", "cluster_tile", "batch_naive", "batch_cell_list", "batch_cluster_tile", "naive_dual_cutoff", "batch_naive_dual_cutoff". If None, a default method is chosen by comparing estimated work from per-system atom counts and cell (or bounding-box) volumes and can select cluster-tile when the CUDA, float32, fully-periodic, contiguous-batch, and output-option guards allow it. Method names that do not start with ``batch_`` refer to single-system algorithms. When ``batch_idx`` or ``batch_ptr`` (batch metadata) is supplied, those explicit method names are treated as aliases for the corresponding ``batch_*`` methods. For example, ``method="naive"`` is dispatched as ``method="batch_naive"`` when batch metadata is provided. When only ``batch_idx`` is provided (no ``batch_ptr`` or 3-D ``cell``), auto-selection computes ``batch_idx.max() + 1`` (and a ``bincount``) which triggers a device-to-host synchronization. To avoid this, pass ``batch_ptr``, a 3-D ``cell`` array, or specify ``method`` explicitly. wrap_positions : bool, default=True If True, wrap input positions into the primary cell before neighbor search. Set to False when positions are already wrapped (e.g. by a preceding integration step) to save two GPU kernel launches per call. Only applies to naive methods; cell list methods handle wrapping internally. grid_policy : {"configured", "adaptive"}, default "configured" Grid-sizing policy for supported pair-centric cell-list paths. ``"adaptive"`` opts into geometry/population-based grid selection; ``"configured"`` preserves the existing grid-sizing rule. Other neighbor methods retain their grid behavior. state : NeighborListState, optional State returned by :func:`prepare_neighbor_list`. Its fixed configuration takes precedence. Prepared execution accepts positions, an applicable current cell, selective ``rebuild_flags``, and current ``pair_params`` as runtime inputs and returns the ordinary route tuple. Prepared states use configured sizing; adaptive ``grid_policy`` is available only for calls without ``state``. **kwargs : Any, optional Additional keyword arguments to pass to the method. max_neighbors : int, optional Maximum number of neighbors per atom. Can be provided to aid in allocation for both naive and cell list methods. max_neighbors2 : int, optional Maximum number of neighbors per atom within cutoff2. Can be provided to aid in allocation for naive dual cutoff method. max_tiles_per_group : int, optional Capacity factor for the intermediate tile-pair buffer used by cluster-tile methods. For batched Torch calls, a system with ``g_i`` row groups contributes ``g_i * min(g_i, max_tiles_per_group)`` entries to one pooled buffer. Increasing the value uses more memory and accommodates more candidate tile pairs. Eager calls estimate the value when it is ``None``. See :ref:`cluster-tile-buffer-capacity` for sizing details. neighbor_matrix : torch.Tensor, optional Pre-allocated tensor of shape (num_rows, max_neighbors) for neighbor indices, where ``num_rows`` is ``total_atoms`` normally and ``len(target_indices)`` for partial lists. Can be provided to avoid reallocation for both naive and cell list methods. neighbor_matrix_shifts : torch.Tensor, optional Pre-allocated tensor of shape (num_rows, max_neighbors, 3) for shift vectors. Can be provided to avoid reallocation for both naive and cell list methods. num_neighbors : torch.Tensor, optional Pre-allocated tensor of shape (num_rows,) for neighbor counts. Can be provided to avoid reallocation for both naive and cell list methods. shift_range_per_dimension : torch.Tensor, optional Pre-allocated tensor of shape (1, 3) for shift range in each dimension. Can be provided to avoid reallocation for naive methods. num_shifts_per_system : torch.Tensor, optional Pre-computed tensor of shape (num_systems,) for the number of periodic shifts per system. Can be provided to avoid recomputation for naive methods. max_shifts_per_system : int, optional Maximum per-system shift count. Can be provided to avoid recomputation for naive methods. cells_per_dimension : torch.Tensor, optional Pre-allocated tensor of shape (3,) for number of cells in x, y, z directions. Can be provided to avoid reallocation for cell list construction. neighbor_search_radius : torch.Tensor, optional Pre-allocated tensor of shape (3,) for radius of neighboring cells to search in each dimension. Can be provided to avoid reallocation for cell list construction. atom_periodic_shifts : torch.Tensor, optional Pre-allocated tensor of shape (total_atoms, 3) for periodic boundary crossings for each atom. Can be provided to avoid reallocation for cell list construction. atom_to_cell_mapping : torch.Tensor, optional Pre-allocated tensor of shape (total_atoms, 3) for cell coordinates for each atom. Can be provided to avoid reallocation for cell list construction. atoms_per_cell_count : torch.Tensor, optional Pre-allocated tensor of shape (max_total_cells,) for number of atoms in each cell. Can be provided to avoid reallocation for cell list construction. cell_atom_start_indices : torch.Tensor, optional Pre-allocated tensor of shape (max_total_cells,) for starting index in cell_atom_list for each cell. Can be provided to avoid reallocation for cell list construction. cell_atom_list : torch.Tensor, optional Pre-allocated tensor of shape (total_atoms,) for flattened list of atom indices organized by cell. Can be provided to avoid reallocation for cell list construction. max_atoms_per_system : int, optional Maximum number of atoms per system. Used in batch naive implementation with PBC for full-row launch sizing. Compact partial paths, including geometry and pair-output paths, ignore this bound and do not require it. Full-row calls infer it when omitted, which may synchronize. target_indices : torch.Tensor, optional Select the central atoms for a partial neighbor list. Matrix outputs use ``len(target_indices)`` compact rows; in COO output the first row holds compact row ids. Supported by naive and cell-list methods; not by cluster_tile. Repeated and empty valid targets are supported. Topology-only naive partial calls may use CUDA ``method="naive_tile"`` explicitly. ``method="naive"`` lets the direct naive family choose its strategy automatically; ``method=None`` uses calibrated method selection. Explicit tile rejects CPU. Distances, vectors, and pair-function outputs are scalar-only and reject explicit tile. Eager calls reject out-of-bounds indices; under ``torch.compile`` callers must prevalidate them. return_distances : bool, default=False Also return per-pair distances ``|r_ij|`` in matrix layout ``(num_rows, max_neighbors)``, where ``num_rows`` is ``total_atoms`` normally and ``len(target_indices)`` for partial lists, differentiable w.r.t. positions (and cell). See the user guide for layout notes. return_vectors : bool, default=False Also return per-pair displacement vectors ``r_ij`` in matrix layout ``(num_rows, max_neighbors, 3)``, differentiable w.r.t. positions (and cell). rebuild_flags : torch.Tensor, optional Boolean flags selecting which systems to re-enumerate; systems whose flag is ``False`` keep their previous output (per-system skip for the batched methods, whole-list flag for single-system methods). pair_offsets, pair_counts : torch.Tensor, optional Fixed segmented COO metadata for explicit ``method="cluster_tile"`` with ``return_neighbor_list=True`` and ``rebuild_flags``. Single-system tensors have shapes ``(2,)`` and ``(1,)`` int32 and are returned with the fixed-capacity COO buffers. return_state : bool, default=False With ``rebuild_flags`` and an explicit ``method="cluster_tile"`` or ``method="batch_cluster_tile"``, append reusable tile state to the result. See the selected method's return contract for exact tensors. pair_fn : warp.Function or CompiledPairFn, optional Inline Warp pair potential evaluated as neighbors are enumerated; requires ``pair_params`` and fills ``pair_energies`` / ``pair_forces``. Forward-only (not differentiable). Pass ``compile_pair_fn(pair_fn)`` to supported method-specific functions before ``torch.compile(fullgraph=True)`` to use fixed-shape matrix outputs in compiled regions. Prepared callbacks remain eager-only. See ``examples/neighbors/06_pair_outputs_lj.py``. pair_params, pair_energies, pair_forces : torch.Tensor, optional Per-atom parameter table and per-pair energy / force output buffers consumed and filled by ``pair_fn``. Returns ------- results : tuple of torch.Tensor Variable-length tuple depending on input parameters. The return pattern follows: **Single cutoff:** - No PBC, matrix format: ``(neighbor_matrix, num_neighbors)`` - No PBC, list format: ``(neighbor_list, neighbor_ptr)`` - With PBC, matrix format: ``(neighbor_matrix, num_neighbors, neighbor_matrix_shifts)`` - With PBC, list format: ``(neighbor_list, neighbor_ptr, neighbor_list_shifts)`` **Dual cutoff:** - No PBC, matrix format: ``(neighbor_matrix1, num_neighbors1, neighbor_matrix2, num_neighbors2)`` - No PBC, list format: ``(neighbor_list1, neighbor_ptr1, neighbor_list2, neighbor_ptr2)`` - With PBC, matrix format: ``(neighbor_matrix1, num_neighbors1, neighbor_matrix_shifts1, neighbor_matrix2, num_neighbors2, neighbor_matrix_shifts2)`` - With PBC, list format: ``(neighbor_list1, neighbor_ptr1, neighbor_list_shifts1, neighbor_list2, neighbor_ptr2, neighbor_list_shifts2)`` **Components returned:** - **neighbor_data** (tensor): Neighbor indices, format depends on ``return_neighbor_list``: - If ``return_neighbor_list=False`` (default): Returns ``neighbor_matrix`` with shape (num_rows, max_neighbors), dtype int32, where ``num_rows`` is ``total_atoms`` normally and ``len(target_indices)`` for partial lists. Row ``r`` contains neighbors for atom ``r`` or ``target_indices[r]`` respectively. - If ``return_neighbor_list=True``: Returns ``neighbor_list`` with shape (2, num_pairs), dtype int32, in COO format [central_rows, neighbor_atoms]. With ``target_indices``, central rows are compact row ids. - **num_neighbor_data** (tensor): Information about the number of neighbors for each atom, format depends on ``return_neighbor_list``: - If ``return_neighbor_list=False`` (default): Returns ``num_neighbors`` with shape (num_rows,), dtype int32. Count of neighbors found for each atom. - If ``return_neighbor_list=True``: Returns ``neighbor_ptr`` with shape (num_rows + 1,), dtype int32. CSR-style pointer arrays where ``neighbor_ptr_data[i]`` to ``neighbor_ptr_data[i+1]`` gives the range of neighbors for row i in the flattened neighbor list. - **neighbor_shift_data** (tensor, optional): Periodic shift vectors, only when ``pbc`` is provided: format depends on ``return_neighbor_list``: - If ``return_neighbor_list=False`` (default): Returns ``neighbor_matrix_shifts`` with shape (num_rows, max_neighbors, 3), dtype int32. - If ``return_neighbor_list=True``: Returns ``unit_shifts`` with shape (num_pairs, 3), dtype int32. When ``cutoff2`` is provided, the pattern repeats for the second cutoff with interleaved components (neighbor_data2, num_neighbor_data2, neighbor_shift_data2) appended to the tuple. Explicit cluster-tile methods append their documented tile-state suffix when ``return_state=True``. Single-system selective COO calls to explicit ``method="cluster_tile"`` return ``(neighbor_list, pair_offsets, pair_counts, neighbor_list_shifts)`` instead of the compact COO pointer tuple. With ``return_state=True``, ``(num_tiles, tile_row_group, tile_col_group)`` is appended. Batched selective calls to explicit ``method="batch_cluster_tile"`` append ``(tile_offsets, tile_counts, num_tiles, tile_row_group, tile_col_group, tile_system)`` when ``return_state=True``. The resulting matrix tuple has nine tensors (or twelve with ``cutoff2``); segmented COO returns ``(neighbor_list, pair_offsets, pair_counts, neighbor_list_shifts, tile_offsets, tile_counts, num_tiles, tile_row_group, tile_col_group, tile_system)``. Examples -------- Single cutoff, matrix format, with PBC:: >>> nm, num, shifts = neighbor_list(pos, 5.0, cell=cell, pbc=pbc) Single cutoff, list format, no PBC:: >>> nlist, ptr = neighbor_list(pos, 5.0, return_neighbor_list=True) Dual cutoff, matrix format, with PBC:: >>> nm1, num1, sh1, nm2, num2, sh2 = neighbor_list( ... pos, 2.5, cutoff2=5.0, cell=cell, pbc=pbc ... ) See Also -------- naive_neighbor_list : Direct access to naive :math:`O(N^2)` algorithm cell_list : Direct access to cell list :math:`O(N)` algorithm batch_naive_neighbor_list : Batched naive algorithm batch_cell_list : Batched cell list algorithm prepare_neighbor_list : Prepare managed repeated execution """ _validate_grid_policy(grid_policy) if state is not None: if grid_policy == "adaptive": raise ValueError( "grid_policy='adaptive' is not supported with state; " "prepared states use configured sizing" ) return _execute_prepared_neighbor_list(positions, cell, state, kwargs=kwargs) if cutoff is None: raise ValueError("cutoff is required when state is not provided") if cell is not None and pbc is None: raise ValueError( "`pbc` is required when `cell` is provided. " "Pass a boolean tensor of shape (3,) or (num_systems, 3), " "e.g. pbc=torch.tensor([True, True, True])." ) if batch_ptr is not None and batch_ptr.shape[0] < 2: raise ValueError("batch_ptr must have length at least 2") use_pair_fn_option = bool(kwargs.pop("use_pair_fn", False)) selected_atom_centric_path = str(kwargs.pop("atom_centric_path", "auto")) target_indices = kwargs.get("target_indices") return_vectors = bool(kwargs.get("return_vectors", False)) return_distances = bool(kwargs.get("return_distances", False)) use_pair_fn = ( use_pair_fn_option or kwargs.get("pair_fn") is not None or kwargs.get("pair_params") is not None or kwargs.get("pair_energies") is not None or kwargs.get("pair_forces") is not None ) rebuild_flags = kwargs.get("rebuild_flags") return_state = bool(kwargs.get("return_state", False)) if return_state and method is None: raise ValueError( "return_state=True requires an explicit cluster_tile method " "(method='cluster_tile' or method='batch_cluster_tile')" ) selected_naive_strategy = "auto" selected_cell_strategy = "auto" def _apply_auto_suboptions( naive_strategy: str, cell_strategy: str, path: str ) -> None: nonlocal selected_naive_strategy, selected_cell_strategy nonlocal selected_atom_centric_path if selected_naive_strategy == "auto" and naive_strategy != "auto": selected_naive_strategy = naive_strategy if selected_cell_strategy == "auto" and cell_strategy != "auto": selected_cell_strategy = cell_strategy if selected_atom_centric_path == "auto" and path != "auto": selected_atom_centric_path = path if method is None: total_atoms = positions.shape[0] has_batch_inputs = batch_idx is not None or batch_ptr is not None if has_batch_inputs: batch_idx, batch_ptr = prepare_batch_idx_ptr( batch_idx, batch_ptr, total_atoms, positions.device ) num_systems = batch_ptr.shape[0] - 1 elif cell is not None and cell.ndim == 3: num_systems = cell.shape[0] else: num_systems = 1 strategy_name = _auto_method_from_geometry( positions, max( float(cutoff), float(cutoff2) if cutoff2 is not None else float(cutoff) ), cell, pbc, batch_idx if has_batch_inputs else None, batch_ptr if has_batch_inputs else None, num_systems, cutoff2=cutoff2, half_fill=half_fill, return_neighbor_list=return_neighbor_list, target_indices=target_indices, return_vectors=return_vectors, return_distances=return_distances, use_pair_fn=use_pair_fn, rebuild_flags=rebuild_flags, wrap_positions=wrap_positions, ) method, auto_native, auto_cell, auto_path = neighbor_list_strategy_run_args( strategy_name ) if cutoff2 is not None and method in ("naive", "cell_list"): method = "naive_dual_cutoff" _apply_auto_suboptions(auto_native, auto_cell, auto_path) if has_batch_inputs and num_systems > 1: method = "batch_" + method elif has_batch_inputs: cell, pbc = _squeeze_single_system_cell_pbc(cell, pbc) else: if batch_idx is not None or batch_ptr is not None: # Route explicit single-system method names through the matching # batch method when batch metadata is provided. if not method.startswith("batch_"): method = "batch_" + method base = method[len("batch_") :] if method.startswith("batch_") else method if base in NEIGHBOR_LIST_STRATEGIES: # Fine-grained strategy name (e.g. from suggest/report): decompose # to the base method plus its sub-options, honoring batch_ prefix. method, fg_native, fg_cell, fg_path = neighbor_list_strategy_run_args( method ) _apply_auto_suboptions(fg_native, fg_cell, fg_path) if return_state and method not in ("cluster_tile", "batch_cluster_tile"): raise ValueError( "return_state=True is supported only by explicit cluster_tile methods" ) match method: case "naive": return naive_neighbor_list( positions, cutoff, pbc=pbc, cell=cell, half_fill=half_fill, fill_value=fill_value, return_neighbor_list=return_neighbor_list, wrap_positions=wrap_positions, strategy=selected_naive_strategy, **kwargs, ) case "cell_list": if cell is None: positions, cell, pbc = synthesize_cell_for_ss(positions, cutoff) return cell_list( positions, cutoff, cell, pbc, half_fill=half_fill, fill_value=fill_value, return_neighbor_list=return_neighbor_list, strategy=selected_cell_strategy, grid_policy=grid_policy, atom_centric_path=selected_atom_centric_path, **kwargs, ) case "batch_naive": return batch_naive_neighbor_list( positions, cutoff, pbc=pbc, cell=cell, batch_idx=batch_idx, batch_ptr=batch_ptr, half_fill=half_fill, fill_value=fill_value, return_neighbor_list=return_neighbor_list, wrap_positions=wrap_positions, strategy=selected_naive_strategy, **kwargs, ) case "batch_cell_list": if batch_idx is None or batch_ptr is None: batch_idx, batch_ptr = prepare_batch_idx_ptr( batch_idx, batch_ptr, positions.shape[0], positions.device ) if cell is None: positions, cell, pbc = synthesize_cell_for_batch( positions, batch_idx, batch_ptr, cutoff ) return batch_cell_list( positions, cutoff, cell, pbc, batch_idx, batch_ptr=batch_ptr, half_fill=half_fill, fill_value=fill_value, return_neighbor_list=return_neighbor_list, strategy=selected_cell_strategy, grid_policy=grid_policy, atom_centric_path=selected_atom_centric_path, **kwargs, ) case "cluster_tile": # format="tile" is reachable only via cluster_tile_neighbor_list directly. # Reject before any cell handling (mirrors the JAX dispatch): cluster_tile is # PBC-implicit, so a missing cell / non-periodic input must error rather than # synthesize a tiny box and force PBC (which would emit spurious wrap-around pairs). _reject_unsupported_cluster_tile_combo(pbc, half_fill) if cell is None: raise ValueError("cell is required for method='cluster_tile'") return cluster_tile_neighbor_list( positions, cutoff, cell, fill_value=fill_value, format="coo" if return_neighbor_list else "matrix", cutoff2=cutoff2, **kwargs, ) case "batch_cluster_tile": # Reject before any cell handling (mirrors the JAX dispatch); see the # single-system case above for why cluster_tile must not synthesize a cell. _reject_unsupported_cluster_tile_combo(pbc, half_fill) if batch_idx is None or batch_ptr is None: batch_idx, batch_ptr = prepare_batch_idx_ptr( batch_idx, batch_ptr, positions.shape[0], positions.device ) if cell is None: raise ValueError("cell is required for method='batch_cluster_tile'") cell = broadcast_shared_cell_for_batch(cell, batch_ptr.shape[0] - 1) return batch_cluster_tile_neighbor_list( positions, cutoff, cell, batch_ptr, fill_value=fill_value, format="coo" if return_neighbor_list else "matrix", cutoff2=cutoff2, **kwargs, ) case "naive_dual_cutoff": return naive_neighbor_list_dual_cutoff( positions, cutoff, cutoff2, pbc=pbc, cell=cell, half_fill=half_fill, fill_value=fill_value, return_neighbor_list=return_neighbor_list, wrap_positions=wrap_positions, **kwargs, ) case "batch_naive_dual_cutoff": return batch_naive_neighbor_list_dual_cutoff( positions, cutoff, cutoff2, pbc=pbc, cell=cell, batch_idx=batch_idx, batch_ptr=batch_ptr, half_fill=half_fill, fill_value=fill_value, return_neighbor_list=return_neighbor_list, wrap_positions=wrap_positions, **kwargs, ) case _: raise ValueError(f"Invalid method: {method}")
__all__ = [ # High-level API "neighbor_list", "estimate_neighbor_list_costs", "suggest_neighbor_list_method", "CompiledPairFn", "compile_pair_fn", "NeighborOverflowError", "TileBufferOverflow", "NeighborListState", "prepare_neighbor_list", # Unbatched algorithms "cell_list", "naive_neighbor_list", "naive_neighbor_list_dual_cutoff", "cluster_tile_neighbor_list", "estimate_cell_list_sizes", # Batched algorithms "batch_cell_list", "batch_naive_neighbor_list", "batch_naive_neighbor_list_dual_cutoff", "batch_cluster_tile_neighbor_list", "estimate_batch_cell_list_sizes", ]