pyastar2d 1.1.1__cp39-abi3-macosx_11_0_arm64.whl

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.
pyastar2d/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from pyastar2d.astar_wrapper import Heuristic, astar_path
2
+
3
+ __all__ = ['astar_path', 'Heuristic']
Binary file
@@ -0,0 +1,77 @@
1
+ import ctypes
2
+ from enum import IntEnum
3
+ from typing import Optional, Tuple
4
+
5
+ import numpy as np
6
+
7
+ import pyastar2d.astar
8
+
9
+ # Define array types
10
+ ndmat_f_type = np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags='C_CONTIGUOUS')
11
+ ndmat_i2_type = np.ctypeslib.ndpointer(dtype=np.int32, ndim=2, flags='C_CONTIGUOUS')
12
+
13
+ # Define input/output types
14
+ pyastar2d.astar.restype = ndmat_i2_type # Nx2 (i, j) coordinates or None
15
+ pyastar2d.astar.argtypes = [
16
+ ndmat_f_type, # weights
17
+ ctypes.c_int, # height
18
+ ctypes.c_int, # width
19
+ ctypes.c_int, # start index in flattened grid
20
+ ctypes.c_int, # goal index in flattened grid
21
+ ctypes.c_bool, # allow diagonal
22
+ ctypes.c_int, # heuristic_override
23
+ ]
24
+
25
+
26
+ class Heuristic(IntEnum):
27
+ """The supported heuristics."""
28
+
29
+ DEFAULT = 0
30
+ ORTHOGONAL_X = 1
31
+ ORTHOGONAL_Y = 2
32
+
33
+
34
+ def astar_path(
35
+ weights: np.ndarray,
36
+ start: Tuple[int, int],
37
+ goal: Tuple[int, int],
38
+ allow_diagonal: bool = False,
39
+ heuristic_override: Heuristic = Heuristic.DEFAULT,
40
+ ) -> Optional[np.ndarray]:
41
+ """
42
+ Run astar algorithm on 2d weights.
43
+
44
+ param np.ndarray weights: A grid of weights e.g. np.ones((10, 10), dtype=np.float32)
45
+ param Tuple[int, int] start: (i, j)
46
+ param Tuple[int, int] goal: (i, j)
47
+ param bool allow_diagonal: Whether to allow diagonal moves
48
+ param Heuristic heuristic_override: Override heuristic, see Heuristic(IntEnum)
49
+
50
+ """
51
+ assert (
52
+ weights.dtype == np.float32
53
+ ), f'weights must have np.float32 data type, but has {weights.dtype}'
54
+ # For the heuristic to be valid, each move must cost at least 1.
55
+ if weights.min(axis=None) < 1.0:
56
+ raise ValueError('Minimum cost to move must be 1, but got %f' % (weights.min(axis=None)))
57
+ # Ensure start is within bounds.
58
+ if start[0] < 0 or start[0] >= weights.shape[0] or start[1] < 0 or start[1] >= weights.shape[1]:
59
+ raise ValueError(f'Start of {start} lies outside grid.')
60
+ # Ensure goal is within bounds.
61
+ if goal[0] < 0 or goal[0] >= weights.shape[0] or goal[1] < 0 or goal[1] >= weights.shape[1]:
62
+ raise ValueError(f'Goal of {goal} lies outside grid.')
63
+
64
+ height, width = weights.shape
65
+ start_idx = np.ravel_multi_index(start, (height, width))
66
+ goal_idx = np.ravel_multi_index(goal, (height, width))
67
+
68
+ path = pyastar2d.astar.astar(
69
+ weights.flatten(),
70
+ height,
71
+ width,
72
+ start_idx,
73
+ goal_idx,
74
+ allow_diagonal,
75
+ int(heuristic_override),
76
+ )
77
+ return path
@@ -0,0 +1,202 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyastar2d
3
+ Version: 1.1.1
4
+ Summary: A simple implementation of the A* algorithm for path-finding on a two-dimensional grid.
5
+ Home-page: https://github.com/hjweide/pyastar2d
6
+ Author: Hendrik Weideman
7
+ Author-email: hjweide@gmail.com
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: imageio
12
+ Requires-Dist: numpy>=2.0.0
13
+ Dynamic: author
14
+ Dynamic: author-email
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: license-file
19
+ Dynamic: requires-dist
20
+ Dynamic: requires-python
21
+ Dynamic: summary
22
+
23
+ [![Build Status](https://github.com/hjweide/pyastar2d/actions/workflows/python-publish.yml/badge.svg)](https://github.com/hjweide/pyastar2d/actions/workflows/python-publish.yml)
24
+ [![PyPI version](https://badge.fury.io/py/pyastar2d.svg)](https://badge.fury.io/py/pyastar2d)
25
+ # PyAstar2D
26
+ This is a very simple C++ implementation of the A\* algorithm for pathfinding
27
+ on a two-dimensional grid. The solver itself is implemented in C++, but is
28
+ callable from Python. This combines the speed of C++ with the convenience of
29
+ Python.
30
+
31
+ I have not done any formal benchmarking, but the solver finds the solution to a
32
+ 1802 by 1802 maze in 0.29s and a 4008 by 4008 maze in 0.83s when running on my
33
+ nine-year-old Intel(R) Core(TM) i7-2630QM CPU @ 2.00GHz. See [Example
34
+ Results](#example-results) for more details.
35
+
36
+ See `src/cpp/astar.cpp` for the core C++ implementation of the A\* shortest
37
+ path search algorithm, `src/pyastar2d/astar_wrapper.py` for the Python wrapper
38
+ and `examples/example.py` for example usage.
39
+
40
+ When determining legal moves, 4-connectivity is the default, but it is possible
41
+ to set `allow_diagonal=True` for 8-connectivity.
42
+
43
+ ## Installation
44
+ Instructions for installing `pyastar2d` are given below.
45
+
46
+ ### From PyPI
47
+ The easiest way to install `pyastar2d` is directly from the Python package index:
48
+ ```
49
+ pip install pyastar2d
50
+ ```
51
+
52
+ ### From source
53
+ You can also install `pyastar2d` by cloning this repository and building it
54
+ yourself. If running on Linux or MacOS, simply run
55
+ ```bash
56
+ pip install .
57
+ ````
58
+ from the root directory. If you are using Windows you may have to install Cython manually first:
59
+ ```bash
60
+ pip install Cython
61
+ pip install .
62
+ ```
63
+ To check that everything worked, run the example:
64
+ ```bash
65
+ python examples/example.py
66
+ ```
67
+
68
+ ### As a dependency
69
+ Include `pyastar2d` in your `requirements.txt` to install from `pypi`, or add
70
+ this line to `requirements.txt`:
71
+ ```
72
+ pyastar2d @ git+git://github.com/hjweide/pyastar2d.git@master#egg=pyastar2d
73
+ ```
74
+
75
+ ## Usage
76
+ A simple example is given below:
77
+ ```python
78
+ import numpy as np
79
+ import pyastar2d
80
+ # The minimum cost must be 1 for the heuristic to be valid.
81
+ # The weights array must have np.float32 dtype to be compatible with the C++ code.
82
+ weights = np.array([[1, 3, 3, 3, 3],
83
+ [2, 1, 3, 3, 3],
84
+ [2, 2, 1, 3, 3],
85
+ [2, 2, 2, 1, 3],
86
+ [2, 2, 2, 2, 1]], dtype=np.float32)
87
+ # The start and goal coordinates are in matrix coordinates (i, j).
88
+ path = pyastar2d.astar_path(weights, (0, 0), (4, 4), allow_diagonal=True)
89
+ print(path)
90
+ # The path is returned as a numpy array of (i, j) coordinates.
91
+ array([[0, 0],
92
+ [1, 1],
93
+ [2, 2],
94
+ [3, 3],
95
+ [4, 4]])
96
+ ```
97
+ Note that all grid points are represented as `(i, j)` coordinates. An example
98
+ of using `pyastar2d` to solve a maze is given in `examples/maze_solver.py`.
99
+
100
+ ## Example Results
101
+ <a name="example-results"></a>
102
+ To test the implementation, I grabbed two nasty mazes from Wikipedia. They are
103
+ included in the ```mazes``` directory, but are originally from here:
104
+ [Small](https://upload.wikimedia.org/wikipedia/commons/c/cf/MAZE.png) and
105
+ [Large](https://upload.wikimedia.org/wikipedia/commons/3/32/MAZE_2000x2000_DFS.png).
106
+ I load the ```.png``` files as grayscale images, and set the white pixels to 1
107
+ (open space) and the black pixels to `INF` (walls).
108
+
109
+ To run the examples specify the input and output files using the `--input` and
110
+ `--output` flags. For example, the following commands will solve the small and
111
+ large mazes:
112
+ ```
113
+ python examples/maze_solver.py --input mazes/maze_small.png --output solns/maze_small.png
114
+ python examples/maze_solver.py --input mazes/maze_large.png --output solns/maze_large.png
115
+ ```
116
+
117
+ ### Small Maze (1802 x 1802):
118
+ ```bash
119
+ time python examples/maze_solver.py --input mazes/maze_small.png --output solns/maze_small.png
120
+ Loaded maze of shape (1802, 1802) from mazes/maze_small.png
121
+ Found path of length 10032 in 0.292794s
122
+ Plotting path to solns/maze_small.png
123
+ Done
124
+
125
+ real 0m1.214s
126
+ user 0m1.526s
127
+ sys 0m0.606s
128
+ ```
129
+ The solution found for the small maze is shown below:
130
+ <img src="https://github.com/hjweide/pyastar2d/raw/master/solns/maze_small_soln.png" alt="Maze Small Solution" style="width: 100%"/>
131
+
132
+ ### Large Maze (4002 x 4002):
133
+ ```bash
134
+ time python examples/maze_solver.py --input mazes/maze_large.png --output solns/maze_large.png
135
+ Loaded maze of shape (4002, 4002) from mazes/maze_large.png
136
+ Found path of length 783737 in 0.829181s
137
+ Plotting path to solns/maze_large.png
138
+ Done
139
+
140
+ real 0m29.385s
141
+ user 0m29.563s
142
+ sys 0m0.728s
143
+ ```
144
+ The solution found for the large maze is shown below:
145
+ <img src="https://github.com/hjweide/pyastar2d/raw/master/solns/maze_large_soln.png" alt="Maze Large Solution" style="width: 100%"/>
146
+
147
+ ## Motivation
148
+ I recently needed an implementation of the A* algorithm in Python to find the
149
+ shortest path between two points in a cost matrix representing an image.
150
+ Normally I would simply use [networkx](https://networkx.github.io/), but for
151
+ graphs with millions of nodes the overhead incurred to construct the graph can
152
+ be expensive. Considering that I was only interested in graphs that may be
153
+ represented as two-dimensional grids, I decided to implement it myself using
154
+ this special structure of the graph to make various optimizations.
155
+ Specifically, the graph is represented as a one-dimensional array because there
156
+ is no need to store the neighbors. Additionally, the lookup tables for
157
+ previously-explored nodes (their costs and paths) are also stored as
158
+ one-dimensional arrays. The implication of this is that checking the lookup
159
+ table can be done in O(1), at the cost of using O(n) memory. Alternatively, we
160
+ could store only the nodes we traverse in a hash table to reduce the memory
161
+ usage. Empirically I found that replacing the one-dimensional array with a
162
+ hash table (`std::unordered_map`) was about five times slower.
163
+
164
+ ## Tests
165
+ The default installation does not include the dependencies necessary to run the
166
+ tests. To install these, first run
167
+ ```bash
168
+ pip install -r requirements-dev.txt
169
+ ```
170
+ before running
171
+ ```bash
172
+ pytest
173
+ ```
174
+ The tests are fairly basic but cover some of the
175
+ more common pitfalls. Pull requests for more extensive tests are welcome.
176
+
177
+ ## Code Formatting
178
+
179
+ It's recommended that you use `pre-commit` to ensure linting procedures are
180
+ run on any code you write. See [pre-commit.com](https://pre-commit.com/) for
181
+ more information.
182
+
183
+ Reference [pre-commit's installation instructions](https://pre-commit.com/#install)
184
+ for software installation on your OS/platform. After you have the software
185
+ installed, run `pre-commit install` on the command line. Now every time you commit
186
+ to this project's code base the linter procedures will automatically run over the
187
+ changed files. To run pre-commit on files preemtively from the command line use:
188
+
189
+ ```bash
190
+ pip install -r requirements-dev.txt
191
+ pre-commit run --all-files
192
+ ```
193
+
194
+ The code base has been formatted by [Black](https://black.readthedocs.io/en/stable/).
195
+ Furthermore, try to conform to `PEP8`. You should set up your preferred editor to
196
+ use `flake8` as its Python linter, but pre-commit will ensure compliance before a
197
+ git commit is completed. This will use the `flake8` configuration within
198
+ `.flake8`, which ignores several errors and stylistic considerations.
199
+
200
+ ## References
201
+ 1. [A\* search algorithm on Wikipedia](https://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode)
202
+ 2. [Pathfinding with A* on Red Blob Games](http://www.redblobgames.com/pathfinding/a-star/introduction.html)
@@ -0,0 +1,8 @@
1
+ pyastar2d-1.1.1.dist-info/RECORD,,
2
+ pyastar2d-1.1.1.dist-info/WHEEL,sha256=pHFP9OVYdvAmGoLnGMVqoQS_7dTsRftlr9dZNNewBVc,135
3
+ pyastar2d-1.1.1.dist-info/top_level.txt,sha256=sirwTJoF4I2DthdhahGwb3hQxZRT8XbOhsNRdorJmXM,10
4
+ pyastar2d-1.1.1.dist-info/METADATA,sha256=UA3EC6qXmJEdQF9nh9066wnmOfY_cbmyDIjlJZMNxLY,8068
5
+ pyastar2d-1.1.1.dist-info/licenses/LICENSE,sha256=XKV3_2Un6l3Wj3fAb8Q_olyi5s3AfI_NqG98vbLGqgs,1073
6
+ pyastar2d/__init__.py,sha256=_llpDHJfo_fzVW08m991vBF7Xeo_TfsdY3lbEPAmPog,97
7
+ pyastar2d/astar.abi3.so,sha256=PDEUxBhYRW6I3oxM51sUKx_6nlApo03I6cevqJhPy0o,54608
8
+ pyastar2d/astar_wrapper.py,sha256=tMXFGWQnq5wXSZs4LiaCfUBYUkoke7jTOvEEcxBURLU,2505
@@ -0,0 +1,6 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: false
4
+ Tag: cp39-abi3-macosx_11_0_arm64
5
+ Generator: delocate 0.13.0
6
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Hendrik Weideman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ pyastar2d