spatial-graph 0.0.1__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. spatial_graph-0.0.3/.github/workflows/ci.yml +107 -0
  2. spatial_graph-0.0.3/.github/workflows/docs.yaml +29 -0
  3. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/.gitignore +5 -0
  4. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/.pre-commit-config.yaml +3 -3
  5. spatial_graph-0.0.3/Dockerfile +38 -0
  6. spatial_graph-0.0.3/PKG-INFO +187 -0
  7. spatial_graph-0.0.3/README.md +162 -0
  8. spatial_graph-0.0.1/README.md → spatial_graph-0.0.3/docs/index.md +23 -45
  9. spatial_graph-0.0.3/examples/basic_usage.py +141 -0
  10. spatial_graph-0.0.3/examples/query_nearest_vispy.py +122 -0
  11. spatial_graph-0.0.3/mkdocs.yml +44 -0
  12. spatial_graph-0.0.3/pyproject.toml +104 -0
  13. spatial_graph-0.0.3/src/spatial_graph/__init__.py +24 -0
  14. spatial_graph-0.0.3/src/spatial_graph/_dtypes.py +141 -0
  15. spatial_graph-0.0.3/src/spatial_graph/_graph/__init__.py +3 -0
  16. spatial_graph-0.0.3/src/spatial_graph/_graph/cgraph.pyi +388 -0
  17. spatial_graph-0.0.3/src/spatial_graph/_graph/graph.py +391 -0
  18. {spatial_graph-0.0.1/spatial_graph/graph → spatial_graph-0.0.3/src/spatial_graph/_graph}/wrapper_template.pyx +6 -3
  19. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/__init__.py +2 -3
  20. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/line_rtree.py +28 -32
  21. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/rtree.py +117 -5
  22. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/wrapper_template.pyx +2 -2
  23. spatial_graph-0.0.1/spatial_graph/spatial_graph.py → spatial_graph-0.0.3/src/spatial_graph/_spatial_graph.py +47 -27
  24. spatial_graph-0.0.3/src/spatial_graph/_util.py +101 -0
  25. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/tests/test_attributes.py +6 -6
  26. spatial_graph-0.0.3/tests/test_bench.py +85 -0
  27. spatial_graph-0.0.3/tests/test_dtype.py +39 -0
  28. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/tests/test_graph.py +21 -32
  29. spatial_graph-0.0.3/tests/test_invalid_inputs.py +50 -0
  30. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/tests/test_rtree.py +2 -1
  31. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/tests/test_spatial_graph.py +18 -25
  32. spatial_graph-0.0.1/.cruft.json +0 -20
  33. spatial_graph-0.0.1/.github/workflows/ci.yaml +0 -80
  34. spatial_graph-0.0.1/PKG-INFO +0 -130
  35. spatial_graph-0.0.1/pyproject.toml +0 -32
  36. spatial_graph-0.0.1/spatial_graph/__init__.py +0 -15
  37. spatial_graph-0.0.1/spatial_graph/dtypes.py +0 -130
  38. spatial_graph-0.0.1/spatial_graph/graph/__init__.py +0 -3
  39. spatial_graph-0.0.1/spatial_graph/graph/graph.py +0 -226
  40. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/LICENSE +0 -0
  41. {spatial_graph-0.0.1/spatial_graph/graph → spatial_graph-0.0.3/src/spatial_graph/_graph}/src/LICENSE.txt +0 -0
  42. {spatial_graph-0.0.1/spatial_graph/graph → spatial_graph-0.0.3/src/spatial_graph/_graph}/src/graph_lite.h +0 -0
  43. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/point_rtree.py +0 -0
  44. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/src/ARCHITECTURE.md +0 -0
  45. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/src/LICENSE +0 -0
  46. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/src/config.h +0 -0
  47. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/src/rtree.c +0 -0
  48. {spatial_graph-0.0.1/spatial_graph/rtree → spatial_graph-0.0.3/src/spatial_graph/_rtree}/src/rtree.h +0 -0
  49. {spatial_graph-0.0.1 → spatial_graph-0.0.3}/tests/test_assert.py +0 -0
@@ -0,0 +1,107 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+ tags: [v*]
9
+ workflow_dispatch:
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ lint:
17
+ name: Lint
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - name: Run pre-commit
22
+ run: pipx run pre-commit run --all-files
23
+
24
+ test:
25
+ runs-on: ${{ matrix.os }}
26
+ strategy:
27
+ fail-fast: false
28
+ matrix:
29
+ os: [ubuntu-latest, windows-latest, macos-latest]
30
+ python-version: ["3.9", "3.11", "3.13"]
31
+
32
+ steps:
33
+ - uses: actions/checkout@v2
34
+ - uses: astral-sh/setup-uv@v6
35
+ with:
36
+ python-version: ${{ matrix.python-version }}
37
+ enable-cache: true
38
+ cache-dependency-glob: "**/pyproject.toml"
39
+ - name: Test with coverage
40
+ run: uv run pytest -v --cov=spatial_graph --cov-report=xml
41
+ - uses: codecov/codecov-action@v5
42
+ with:
43
+ token: ${{ secrets.CODECOV_TOKEN }}
44
+
45
+ benchmarks:
46
+ runs-on: ubuntu-latest
47
+ env:
48
+ UV_NO_SYNC: "1"
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: astral-sh/setup-uv@v6
52
+ with:
53
+ python-version: "3.13"
54
+ enable-cache: true
55
+
56
+ - name: install
57
+ run: uv sync --no-dev --group test-codspeed
58
+
59
+ - name: Run benchmarks
60
+ uses: CodSpeedHQ/action@v3
61
+ with:
62
+ run: uv run pytest -W ignore --codspeed -v --color=yes
63
+
64
+ deploy:
65
+ name: Deploy
66
+ needs: test
67
+ if: success() && startsWith(github.ref, 'refs/tags/') && github.event_name != 'schedule'
68
+ runs-on: ubuntu-latest
69
+
70
+ permissions:
71
+ id-token: write
72
+ contents: write
73
+
74
+ steps:
75
+ - uses: actions/checkout@v4
76
+ with:
77
+ fetch-depth: 0
78
+ - uses: astral-sh/setup-uv@v6
79
+ with:
80
+ python-version: ${{ matrix.python-version }}
81
+ enable-cache: true
82
+ cache-dependency-glob: "**/pyproject.toml"
83
+
84
+ - name: 👷 Build
85
+ run: uv build
86
+
87
+ - name: 🚢 Publish to PyPI
88
+ uses: pypa/gh-action-pypi-publish@release/v1
89
+ with:
90
+ password: ${{ secrets.PYPI_API_TOKEN }}
91
+
92
+ - uses: softprops/action-gh-release@v2
93
+ with:
94
+ generate_release_notes: true
95
+ files: "./dist/*"
96
+
97
+ docker-test:
98
+ runs-on: ubuntu-latest
99
+
100
+ steps:
101
+ - name: Checkout code
102
+ uses: actions/checkout@v4
103
+
104
+ - name: Build and test Docker image
105
+ run: |
106
+ docker build -t spatial_graph .
107
+ docker run --rm spatial_graph
@@ -0,0 +1,29 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions:
13
+ contents: write
14
+
15
+ jobs:
16
+ docs:
17
+ name: Deploy Documentation
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ with:
22
+ fetch-depth: 0
23
+ - name: Configure Git Credentials
24
+ run: |
25
+ git config user.name github-actions[bot]
26
+ git config user.email github-actions[bot]@users.noreply.github.com
27
+ - uses: astral-sh/setup-uv@v6
28
+ - name: Deploy documentation
29
+ run: uv run --group docs mkdocs gh-deploy --strict --force
@@ -11,3 +11,8 @@ build
11
11
  dist
12
12
  .vscode
13
13
  docs/_build/
14
+ coverage.xml
15
+ site
16
+
17
+ # remove me to enforce synchrony across development environments
18
+ uv.lock
@@ -15,13 +15,13 @@ repos:
15
15
  - id: check-added-large-files
16
16
 
17
17
  - repo: https://github.com/astral-sh/ruff-pre-commit
18
- rev: v0.7.2
18
+ rev: v0.12.2
19
19
  hooks:
20
- - id: ruff
20
+ - id: ruff-check
21
21
  args: [--fix, --unsafe-fixes]
22
22
  - id: ruff-format
23
23
 
24
24
  - repo: https://github.com/pre-commit/mirrors-mypy
25
- rev: v1.13.0
25
+ rev: v1.16.1
26
26
  hooks:
27
27
  - id: mypy
@@ -0,0 +1,38 @@
1
+ FROM ubuntu:24.04
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive \
4
+ MAMBA_ROOT_PREFIX=/opt/conda \
5
+ PATH=/opt/conda/bin:$PATH
6
+
7
+ RUN apt-get update && \
8
+ apt-get install -y --no-install-recommends \
9
+ git curl tar bzip2 ca-certificates && \
10
+ rm -rf /var/lib/apt/lists/*
11
+
12
+ # auto-detect arch and grab the matching micromamba binary
13
+ RUN set -eux; \
14
+ arch="$(uname -m)"; \
15
+ case "$arch" in \
16
+ x86_64) url_arch=linux-64 ;; \
17
+ aarch64|arm64) url_arch=linux-aarch64 ;; \
18
+ ppc64le) url_arch=linux-ppc64le ;; \
19
+ *) echo "Unsupported arch: $arch"; exit 1 ;; \
20
+ esac; \
21
+ curl -Ls "https://micro.mamba.pm/api/micromamba/$url_arch/latest" \
22
+ | tar -xvj -C /usr/local/bin bin/micromamba; \
23
+ chmod +x /usr/local/bin/bin/micromamba; \
24
+ mv /usr/local/bin/bin/micromamba /usr/local/bin/micromamba; \
25
+ rmdir /usr/local/bin/bin
26
+
27
+
28
+ WORKDIR /app
29
+ COPY . /app
30
+
31
+ RUN micromamba create -y -n test-env -c conda-forge python=3.12 pip compilers
32
+ RUN micromamba run -n test-env pip install -e . --group test
33
+
34
+ SHELL ["micromamba", "run", "-n", "test-env", "/bin/bash", "-o", "pipefail", "-c"]
35
+
36
+
37
+ # when you docker run, this will invoke pytest from inside test-env
38
+ CMD ["micromamba", "run", "-n", "test-env", "pytest", "-q", "-x"]
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: spatial-graph
3
+ Version: 0.0.3
4
+ Summary: A spatial graph datastructure for python.
5
+ Project-URL: homepage, https://github.com/funkelab/spatial_graph
6
+ Project-URL: repository, https://github.com/funkelab/spatial_graph
7
+ Author-email: Jan Funke <funkej@janelia.hhmi.org>, Talley Lambert <talley.lambert@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: ct3>=3.3.3
21
+ Requires-Dist: numpy
22
+ Requires-Dist: setuptools>=75.8.0
23
+ Requires-Dist: witty>=v0.2.1
24
+ Description-Content-Type: text/markdown
25
+
26
+ # spatial-graph
27
+
28
+ [![License](https://img.shields.io/pypi/l/spatial-graph.svg?color=green)](https://github.com/funkelab/spatial_graph/raw/main/LICENSE)
29
+ [![PyPI](https://img.shields.io/pypi/v/spatial-graph.svg?color=green)](https://pypi.org/project/spatial-graph)
30
+ [![Python Version](https://img.shields.io/pypi/pyversions/spatial-graph.svg?color=green)](https://python.org)
31
+ [![CI](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yml/badge.svg)](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yml)
32
+ [![codecov](https://codecov.io/gh/funkelab/spatial_graph/branch/main/graph/badge.svg)](https://codecov.io/gh/funkelab/spatial_graph)
33
+ [![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/funkelab/spatial_graph)
34
+
35
+ `spatial_graph` provides a data structure for directed and undirected graphs,
36
+ where each node has an nD position (in time or space).
37
+
38
+ ## Design Principles
39
+
40
+ ### Goals
41
+
42
+ * support for arbitrary number of dimensions
43
+ * typed node identifiers and attributes
44
+ * any fixed-length type that is supported by `numpy`
45
+ * efficient node/edge queries by
46
+ * ROI
47
+ * kNN (by points / lines)
48
+ * numpy-like interface for efficient:
49
+ * graph population and manipulation
50
+ * query results
51
+ * attribute access
52
+ * minimal memory footprint
53
+ * minimal dependencies
54
+ * `cython` / `witty` / `cheetah3` for runtime compilation
55
+ * numpy for array interfaces
56
+ * PYX API for graph algorithms in C/C++
57
+
58
+ ### Non-Goals
59
+
60
+ * graph algorithms
61
+ * I/O
62
+ * non-typed arguments
63
+ * non-spatial graphs
64
+ * out-of-memory support
65
+ * networkx compatibility
66
+
67
+ ## Python API
68
+
69
+ Graph creation:
70
+
71
+ ```python
72
+ graph = sg.SpatialGraph(
73
+ ndims=3,
74
+ node_dtype="uint64",
75
+ node_attr_dtypes={"position": "double[3]"},
76
+ edge_attr_dtypes={"score": "float32"},
77
+ position_attr="position",
78
+ )
79
+ ```
80
+
81
+ Adding nodes/edges:
82
+
83
+ ```python
84
+ graph.add_nodes(
85
+ np.array([1, 2, 3, 4, 5], dtype="uint64"),
86
+ position=np.array(
87
+ [
88
+ [0.1, 0.1, 0.1],
89
+ [0.2, 0.2, 0.2],
90
+ [0.3, 0.3, 0.3],
91
+ [0.4, 0.4, 0.4],
92
+ [0.5, 0.5, 0.5],
93
+ ],
94
+ dtype="double",
95
+ ),
96
+ )
97
+
98
+ graph.add_edges(
99
+ np.array([[1, 2], [3, 4], [5, 1]], dtype="uint64"),
100
+ score=np.array([0.2, 0.3, 0.4], dtype="float32"),
101
+ )
102
+ ```
103
+
104
+ Query nodes/edges in ROI:
105
+
106
+ ```python
107
+ # nodes/edges will be numpy arrays of dtype uint64 and shape (n,)/(n, 2)
108
+ nodes = graph.query_nodes_in_roi(np.array([[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]))
109
+ edges = graph.query_edges_in_roi(np.array([[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]))
110
+ ```
111
+
112
+ Query nodes/edges by position:
113
+
114
+ ```python
115
+ nodes = graph.query_nearest_nodes(np.array([0.3, 0.3, 0.3]), k=3)
116
+ edges = graph.query_nearest_edges(np.array([0.3, 0.3, 0.3]), k=3)
117
+ ```
118
+
119
+ Access node/edge attributes:
120
+
121
+ ```python
122
+ node_positions = graph.node_attrs[nodes].position
123
+ edge_scores = graph.edge_attrs[edges].score
124
+ ```
125
+
126
+ Delete nodes/edges:
127
+
128
+ ```python
129
+ graph.remove_nodes(nodes[:1000])
130
+ ```
131
+
132
+ ## Implementation Details
133
+
134
+ A `SpatialGraph` consists of three data structures:
135
+
136
+ * The `Graph` itself, holding nodes, edges, and their attributes
137
+ ([graphlite](https://github.com/haasdo95/graphlite)).
138
+ * Two R-trees for spatial node and edge queries (based on
139
+ [rtree.c](https://github.com/tidwall/rtree.c)). We modified the original code
140
+ to also include a fast kNN search.
141
+
142
+ ## Cross-Platform Support
143
+
144
+ `spatial_graph` compiles C/C++ code at runtime, and as such needs access to a
145
+ compiler. If you already have one, great! You can use the PyPI package.
146
+
147
+ If you (or your users) don't have a compiler installed, you either need to
148
+
149
+ 1. Install a compiler. This might be weird for non-technical users.
150
+ 2. Install `spatial_graph` from `conda-forge`, where we include a compiler
151
+ (`clang`) in its dependencies.
152
+
153
+ ### Why is this so complicated?
154
+
155
+ There is no cross-platform C/C++ compiler that we can install using `pip`.
156
+ [`numba`](https://github.com/numba/numba) is maybe the closest to having solved
157
+ that problem: `numba` does compile during runtime even if you don't have a
158
+ compiler locally installed. This works because `numba` is generating LLVM IR,
159
+ an intermediate representation language that LLVM can compile into machine
160
+ code. `numba` depends on [`llvmlite`](https://github.com/numba/llvmlite), which
161
+ provides a subset of the LLVM API, statically linked into the binaries in that
162
+ package. This is just enough to compile the `numba` generated LLVM IR into
163
+ machine code. We can't use this strategy, because we compile general C/C++
164
+ code. Converting that into LLVM IR is exactly what we need a compiler for.
165
+
166
+ ## For Developers
167
+
168
+ To create a new release, tag the current commit with a
169
+ version number and push it to the `upstream` remote:
170
+
171
+ ```bash
172
+ git tag -a "vX.Y.Z" -m "vX.Y.Z"
173
+ git push upstream --follow-tags
174
+ ```
175
+
176
+ This will trigger the CI workflow, which will build the package and upload it to PyPI.
177
+
178
+ ### Testing in a conda environment
179
+
180
+ To simulate a naive user environment, with *no* assumptions made about the
181
+ availability of a C/C++ compiler, you can run the included Dockerfile
182
+ (where the key part of the conda env is the `compilers` package):
183
+
184
+ ```bash
185
+ docker build -t spatial_graph .
186
+ docker run --rm spatial_graph
187
+ ```
@@ -0,0 +1,162 @@
1
+ # spatial-graph
2
+
3
+ [![License](https://img.shields.io/pypi/l/spatial-graph.svg?color=green)](https://github.com/funkelab/spatial_graph/raw/main/LICENSE)
4
+ [![PyPI](https://img.shields.io/pypi/v/spatial-graph.svg?color=green)](https://pypi.org/project/spatial-graph)
5
+ [![Python Version](https://img.shields.io/pypi/pyversions/spatial-graph.svg?color=green)](https://python.org)
6
+ [![CI](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yml/badge.svg)](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yml)
7
+ [![codecov](https://codecov.io/gh/funkelab/spatial_graph/branch/main/graph/badge.svg)](https://codecov.io/gh/funkelab/spatial_graph)
8
+ [![CodSpeed](https://img.shields.io/endpoint?url=https://codspeed.io/badge.json)](https://codspeed.io/funkelab/spatial_graph)
9
+
10
+ `spatial_graph` provides a data structure for directed and undirected graphs,
11
+ where each node has an nD position (in time or space).
12
+
13
+ ## Design Principles
14
+
15
+ ### Goals
16
+
17
+ * support for arbitrary number of dimensions
18
+ * typed node identifiers and attributes
19
+ * any fixed-length type that is supported by `numpy`
20
+ * efficient node/edge queries by
21
+ * ROI
22
+ * kNN (by points / lines)
23
+ * numpy-like interface for efficient:
24
+ * graph population and manipulation
25
+ * query results
26
+ * attribute access
27
+ * minimal memory footprint
28
+ * minimal dependencies
29
+ * `cython` / `witty` / `cheetah3` for runtime compilation
30
+ * numpy for array interfaces
31
+ * PYX API for graph algorithms in C/C++
32
+
33
+ ### Non-Goals
34
+
35
+ * graph algorithms
36
+ * I/O
37
+ * non-typed arguments
38
+ * non-spatial graphs
39
+ * out-of-memory support
40
+ * networkx compatibility
41
+
42
+ ## Python API
43
+
44
+ Graph creation:
45
+
46
+ ```python
47
+ graph = sg.SpatialGraph(
48
+ ndims=3,
49
+ node_dtype="uint64",
50
+ node_attr_dtypes={"position": "double[3]"},
51
+ edge_attr_dtypes={"score": "float32"},
52
+ position_attr="position",
53
+ )
54
+ ```
55
+
56
+ Adding nodes/edges:
57
+
58
+ ```python
59
+ graph.add_nodes(
60
+ np.array([1, 2, 3, 4, 5], dtype="uint64"),
61
+ position=np.array(
62
+ [
63
+ [0.1, 0.1, 0.1],
64
+ [0.2, 0.2, 0.2],
65
+ [0.3, 0.3, 0.3],
66
+ [0.4, 0.4, 0.4],
67
+ [0.5, 0.5, 0.5],
68
+ ],
69
+ dtype="double",
70
+ ),
71
+ )
72
+
73
+ graph.add_edges(
74
+ np.array([[1, 2], [3, 4], [5, 1]], dtype="uint64"),
75
+ score=np.array([0.2, 0.3, 0.4], dtype="float32"),
76
+ )
77
+ ```
78
+
79
+ Query nodes/edges in ROI:
80
+
81
+ ```python
82
+ # nodes/edges will be numpy arrays of dtype uint64 and shape (n,)/(n, 2)
83
+ nodes = graph.query_nodes_in_roi(np.array([[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]))
84
+ edges = graph.query_edges_in_roi(np.array([[0.0, 0.0, 0.0], [0.25, 0.25, 0.25]]))
85
+ ```
86
+
87
+ Query nodes/edges by position:
88
+
89
+ ```python
90
+ nodes = graph.query_nearest_nodes(np.array([0.3, 0.3, 0.3]), k=3)
91
+ edges = graph.query_nearest_edges(np.array([0.3, 0.3, 0.3]), k=3)
92
+ ```
93
+
94
+ Access node/edge attributes:
95
+
96
+ ```python
97
+ node_positions = graph.node_attrs[nodes].position
98
+ edge_scores = graph.edge_attrs[edges].score
99
+ ```
100
+
101
+ Delete nodes/edges:
102
+
103
+ ```python
104
+ graph.remove_nodes(nodes[:1000])
105
+ ```
106
+
107
+ ## Implementation Details
108
+
109
+ A `SpatialGraph` consists of three data structures:
110
+
111
+ * The `Graph` itself, holding nodes, edges, and their attributes
112
+ ([graphlite](https://github.com/haasdo95/graphlite)).
113
+ * Two R-trees for spatial node and edge queries (based on
114
+ [rtree.c](https://github.com/tidwall/rtree.c)). We modified the original code
115
+ to also include a fast kNN search.
116
+
117
+ ## Cross-Platform Support
118
+
119
+ `spatial_graph` compiles C/C++ code at runtime, and as such needs access to a
120
+ compiler. If you already have one, great! You can use the PyPI package.
121
+
122
+ If you (or your users) don't have a compiler installed, you either need to
123
+
124
+ 1. Install a compiler. This might be weird for non-technical users.
125
+ 2. Install `spatial_graph` from `conda-forge`, where we include a compiler
126
+ (`clang`) in its dependencies.
127
+
128
+ ### Why is this so complicated?
129
+
130
+ There is no cross-platform C/C++ compiler that we can install using `pip`.
131
+ [`numba`](https://github.com/numba/numba) is maybe the closest to having solved
132
+ that problem: `numba` does compile during runtime even if you don't have a
133
+ compiler locally installed. This works because `numba` is generating LLVM IR,
134
+ an intermediate representation language that LLVM can compile into machine
135
+ code. `numba` depends on [`llvmlite`](https://github.com/numba/llvmlite), which
136
+ provides a subset of the LLVM API, statically linked into the binaries in that
137
+ package. This is just enough to compile the `numba` generated LLVM IR into
138
+ machine code. We can't use this strategy, because we compile general C/C++
139
+ code. Converting that into LLVM IR is exactly what we need a compiler for.
140
+
141
+ ## For Developers
142
+
143
+ To create a new release, tag the current commit with a
144
+ version number and push it to the `upstream` remote:
145
+
146
+ ```bash
147
+ git tag -a "vX.Y.Z" -m "vX.Y.Z"
148
+ git push upstream --follow-tags
149
+ ```
150
+
151
+ This will trigger the CI workflow, which will build the package and upload it to PyPI.
152
+
153
+ ### Testing in a conda environment
154
+
155
+ To simulate a naive user environment, with *no* assumptions made about the
156
+ availability of a C/C++ compiler, you can run the included Dockerfile
157
+ (where the key part of the conda env is the `compilers` package):
158
+
159
+ ```bash
160
+ docker build -t spatial_graph .
161
+ docker run --rm spatial_graph
162
+ ```
@@ -1,44 +1,28 @@
1
- # spatial_graph
1
+ # spatial-graph
2
2
 
3
- [![CI](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yaml/badge.svg)](https://github.com/funkelab/spatial_graph/actions/workflows/ci.yaml)
4
-
5
- `spatial_graph` provides a data structure for directed and undirected graphs,
3
+ `spatial-graph` provides a data structure for directed and undirected graphs,
6
4
  where each node has an nD position (in time or space).
7
5
 
8
- Design Principles
9
- =================
10
-
11
- Goals
12
- -----
13
-
14
- * support for arbitrary number of dimensions
15
- * typed node identifiers and attributes
16
- * any fixed-length type that is supported by `numpy`
17
- * efficient node/edge queries by
18
- * ROI
19
- * kNN (by points / lines)
20
- * numpy-like interface for efficient:
21
- * graph population and manipulation
22
- * query results
23
- * attribute access
24
- * minimal memory footprint
25
- * minimal dependencies
26
- * `cython` / `witty` / `cheetah3` for runtime compilation
27
- * numpy for array interfaces
28
- * PYX API for graph algorithms in C/C++
29
-
30
- Non-Goals
31
- ---------
32
-
33
- * graph algorithms
34
- * I/O
35
- * non-typed arguments
36
- * non-spatial graphs
37
- * out-of-memory support
38
- * networkx compatibility
39
-
40
- Python API
41
- ==========
6
+ It leverages well-in-time compiled C++ code for efficient graph operations,
7
+ coupled with an rtree implementation for fast spatial queries.
8
+
9
+ ## Goals
10
+
11
+ - Support for arbitrary number of dimensions
12
+ - Typed node identifiers and attributes
13
+ - Any fixed-length type that is supported by `numpy`
14
+ - Efficient node/edge queries by
15
+ - ROI
16
+ - kNN (by points / lines)
17
+ - numpy-like interface for efficient:
18
+ - Graph population and manipulation
19
+ - Query results
20
+ - Attribute access
21
+ - Minimal memory footprint
22
+ - Minimal dependencies
23
+ - PYX API for graph algorithms in C/C++
24
+
25
+ ## Basic Usage
42
26
 
43
27
  Graph creation:
44
28
 
@@ -49,7 +33,6 @@ graph = sg.SpatialGraph(
49
33
  node_attr_dtypes={"position": "double[3]"},
50
34
  edge_attr_dtypes={"score": "float32"},
51
35
  position_attr="position",
52
- directed=False,
53
36
  )
54
37
  ```
55
38
 
@@ -104,9 +87,4 @@ Delete nodes/edges:
104
87
  graph.remove_nodes(nodes[:1000])
105
88
  ```
106
89
 
107
- Implementation Details
108
- ======================
109
-
110
- A `SpatialGraph` consists of three data structures:
111
- * The `Graph` itself, holding nodes, edges, and their attributes ([graphlite](https://github.com/haasdo95/graphlite)).
112
- * Two R-trees for spatial node and edge queries (based on [rtree.c](https://github.com/tidwall/rtree.c)).
90
+ See the [API documentation](./reference) for more details.