|
| 1 | +import sys |
| 2 | + |
| 3 | +import numpy as np |
| 4 | +import pytest |
| 5 | + |
| 6 | +from spatial_graph import SpatialGraph |
| 7 | + |
| 8 | +# either run this file directly or with pytest --codspeed |
| 9 | +if all(x not in {"--codspeed", "tests/test_bench.py"} for x in sys.argv): |
| 10 | + pytest.skip( |
| 11 | + "use 'pytest tests/test_bench.py' to run benchmark", allow_module_level=True |
| 12 | + ) |
| 13 | + |
| 14 | + |
| 15 | +def _make_graph( |
| 16 | + ndims=3, |
| 17 | + node_dtype="uint64", |
| 18 | + node_attr_dtypes=None, |
| 19 | + edge_attr_dtypes=None, |
| 20 | + directed=False, |
| 21 | + n_nodes=100_000, |
| 22 | +): |
| 23 | + """Helper to create a SpatialGraph instance with default parameters.""" |
| 24 | + if node_attr_dtypes is None: |
| 25 | + node_attr_dtypes = {"position": "double[3]"} |
| 26 | + if edge_attr_dtypes is None: |
| 27 | + edge_attr_dtypes = {"score": "float32"} |
| 28 | + |
| 29 | + graph = SpatialGraph( |
| 30 | + ndims=ndims, |
| 31 | + node_dtype=node_dtype, |
| 32 | + node_attr_dtypes=node_attr_dtypes, |
| 33 | + edge_attr_dtypes=edge_attr_dtypes, |
| 34 | + position_attr="position", |
| 35 | + directed=directed, |
| 36 | + ) |
| 37 | + nodes = np.arange(n_nodes, dtype="uint64") |
| 38 | + positions = np.random.random((n_nodes, ndims)) |
| 39 | + graph.add_nodes(nodes, position=positions) |
| 40 | + |
| 41 | + return graph |
| 42 | + |
| 43 | + |
| 44 | +@pytest.mark.parametrize("num_queries", [100]) |
| 45 | +@pytest.mark.parametrize("k", [1000, 10000]) |
| 46 | +@pytest.mark.parametrize("n_nodes", [100_000, 1_000_000]) |
| 47 | +def test_bench_query_nearest_nodes(n_nodes: int, k: int, num_queries: int, benchmark): |
| 48 | + """Benchmark query_nearest_nodes.""" |
| 49 | + graph = _make_graph(n_nodes=n_nodes) |
| 50 | + query_points = np.random.random((num_queries, 3)) |
| 51 | + |
| 52 | + def _run(): |
| 53 | + for i in range(num_queries): |
| 54 | + # Query nearest nodes |
| 55 | + closest, distances = graph.query_nearest_nodes( |
| 56 | + query_points[i], k=k, return_distances=True |
| 57 | + ) |
| 58 | + positions = graph.node_attrs[closest].position |
| 59 | + return closest, distances, positions |
| 60 | + |
| 61 | + closest, distances, positions = benchmark(_run) |
| 62 | + |
| 63 | + # Verify results |
| 64 | + assert len(distances) == len(closest) |
| 65 | + assert positions.shape[1] == 3 |
| 66 | + |
| 67 | + |
| 68 | +@pytest.mark.parametrize("n_nodes", [100_000, 1_000_000]) |
| 69 | +def test_roi_query_performance(n_nodes, benchmark): |
| 70 | + """Benchmark ROI (region of interest) queries.""" |
| 71 | + large_graph = _make_graph(n_nodes=n_nodes) |
| 72 | + # Define a ROI that should contain a reasonable number of nodes |
| 73 | + roi = np.array([[0.25, 0.25, 0.25], [0.75, 0.75, 0.75]]) |
| 74 | + |
| 75 | + nodes_in_roi = benchmark(lambda: large_graph.query_nodes_in_roi(roi)) |
| 76 | + |
| 77 | + # Verify results |
| 78 | + assert len(nodes_in_roi) > 0 |
| 79 | + assert len(nodes_in_roi) < n_nodes # Should be subset |
| 80 | + |
| 81 | + # Verify nodes are actually in ROI |
| 82 | + positions = large_graph.node_attrs[nodes_in_roi].position |
| 83 | + assert np.all(positions >= roi[0]) |
| 84 | + assert np.all(positions <= roi[1]) |
0 commit comments