-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from MarekSuchanek/speedup
Speedup via Cython
- Loading branch information
Showing
9 changed files
with
201 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,3 +90,13 @@ ENV/ | |
|
||
# PyCharm | ||
.idea | ||
|
||
# MI-PYT tests | ||
tests2 | ||
|
||
# Own | ||
maze.c | ||
maze.cpp | ||
maze.html | ||
generate.py | ||
random.csv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
from random import randint | ||
|
||
if __name__ == '__main__': | ||
w = 1000 | ||
h = 1000 | ||
min_val = -1000 | ||
max_val = 2000 | ||
for i in range(h): | ||
for j in range(w-1): | ||
if randint(0,10) < 8: | ||
print(randint(min_val, max_val), end=',') | ||
else: | ||
print(1, end=',') | ||
print(randint(min_val, max_val)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# distutils: language=c++ | ||
import numpy as np | ||
cimport numpy as np | ||
import cython | ||
from libcpp cimport bool | ||
from libcpp.queue cimport queue | ||
from libcpp.vector cimport vector | ||
from libcpp.pair cimport pair | ||
from libcpp.map cimport map | ||
|
||
cdef struct coords: | ||
int x | ||
int y | ||
|
||
cdef struct qitem: | ||
int x | ||
int y | ||
int distance | ||
|
||
|
||
@cython.boundscheck(False) | ||
@cython.wraparound(False) | ||
cdef bool directions2reachable(np.ndarray[np.int8_t, ndim=2] directions, int w, int h): | ||
with cython.nogil: | ||
for x in range(w): | ||
for y in range(h): | ||
with cython.gil: | ||
if directions[x, y] == ord(b' '): | ||
return False | ||
return True | ||
|
||
|
||
@cython.boundscheck(False) | ||
@cython.wraparound(False) | ||
cpdef flood(np.ndarray[np.int8_t, ndim=2] maze, int w, int h): | ||
cdef int x, y, i | ||
cdef queue[qitem] q | ||
cdef np.ndarray[np.int32_t, ndim=2] distances | ||
cdef np.ndarray[np.int8_t, ndim=2] directions | ||
distances = np.full((w, h), -1, dtype='int32') | ||
directions = np.full((w, h), b' ', dtype=('a', 1)) | ||
|
||
with cython.nogil: | ||
for x in range(w): | ||
for y in range(h): | ||
with cython.gil: | ||
if maze[x, y] < 0: | ||
directions[x, y] = b'#' | ||
elif maze[x, y] == 1: | ||
q.push(qitem(x, y, 0)) | ||
distances[x, y] = 0 | ||
directions[x, y] = b'X' | ||
|
||
cdef coords *dir_offsets = [coords(1, 0), coords(-1, 0), coords(0, 1), coords(0, -1)] | ||
cdef np.int8_t *dir_chars = [b'^', b'v', b'<', b'>'] | ||
while not q.empty(): | ||
item = q.front() | ||
q.pop() | ||
for i in range(4): | ||
offset = dir_offsets[i] | ||
x = item.x + offset.x | ||
y = item.y + offset.y | ||
if 0 <= x < w and 0 <= y < h: | ||
if maze[x, y] >= 0 and distances[x, y] == -1: | ||
distances[x, y] = item.distance+1 | ||
directions[x, y] = dir_chars[i] | ||
q.push(qitem(x, y, item.distance+1)) | ||
|
||
return distances, directions, directions2reachable(directions, w, h) | ||
|
||
|
||
@cython.boundscheck(False) | ||
@cython.wraparound(False) | ||
cpdef build_path(np.ndarray[np.int8_t, ndim=2] directions, int row, int column): | ||
if directions[row, column] == b'#' or directions[row, column] == b' ': | ||
raise NoPathExistsException | ||
cdef vector[pair[int,int]] path | ||
cdef map[char,coords] dirs | ||
dirs.insert(pair[char,coords](b'v', coords(1, 0))) | ||
dirs.insert(pair[char,coords](b'^', coords(-1, 0))) | ||
dirs.insert(pair[char,coords](b'>', coords(0, 1))) | ||
dirs.insert(pair[char,coords](b'<', coords(0, -1))) | ||
path.push_back(pair[int,int](row, column)) | ||
while directions[row, column] != b'X': | ||
d = dirs[directions[row, column]] | ||
row += d.x | ||
column += d.y | ||
path.push_back(pair[int,int](row, column)) | ||
return path | ||
|
||
|
||
cdef class NoPathExistsException(Exception): | ||
pass | ||
|
||
|
||
class MazeAnalysis: | ||
|
||
def __init__(self, maze): | ||
maze = np.atleast_2d(maze.astype('int8')) # fix matrix type & dims | ||
self.distances, self.directions, self.is_reachable = flood(maze, *maze.shape) | ||
|
||
def path(self, row, column): | ||
return build_path(self.directions, row, column) | ||
|
||
|
||
cpdef analyze(maze): | ||
return MazeAnalysis(maze) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
numpy>==1.11.2 | ||
numpy>=1.11.2 | ||
py>=1.4.31 | ||
pytest>=3.0.4 | ||
Cython>=0.25.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from setuptools import setup, find_packages | ||
from Cython.Build import cythonize | ||
import numpy | ||
|
||
with open('README.md') as f: | ||
long_description = ''.join(f.readlines()) | ||
|
||
setup( | ||
name='maze', | ||
version=0.2, | ||
keywords='maze analysis matrix cython', | ||
description='Simple python maze analyzer for finding shortest path', | ||
long_description=long_description, | ||
author='Marek Suchánek', | ||
author_email='[email protected]', | ||
license='MIT', | ||
packages=find_packages(), | ||
ext_modules=cythonize( | ||
'maze.pyx', | ||
language_level=3, | ||
include_dirs=[numpy.get_include()], | ||
language="c++" | ||
), | ||
include_dirs=[numpy.get_include()], | ||
install_requires=[ | ||
'Cython>=0.25.1', | ||
'numpy>=1.11.2', | ||
'py>=1.4.31', | ||
], | ||
setup_requires=[ | ||
'pytest-runner' | ||
], | ||
tests_require=[ | ||
'pytest', | ||
], | ||
classifiers=[ | ||
'License :: OSI Approved :: MIT License', | ||
'Programming Language :: Python', | ||
'Programming Language :: Python :: Implementation :: CPython', | ||
'Programming Language :: Python :: 3', | ||
'Programming Language :: Python :: 3.5', | ||
'Topic :: Scientific/Engineering :: Mathematics', | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters