Skip to content

Commit 19e28e4

Browse files
authored
Add CPP implementation for functions in LDS (#543)
1 parent 0649f8b commit 19e28e4

File tree

6 files changed

+208
-36
lines changed

6 files changed

+208
-36
lines changed

pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <Python.h>
22
#include "quick_sort.hpp"
3-
#include "n2_square_sort.hpp"
3+
#include "quadratic_time_sort.hpp"
4+
#include "misc_algorithms.hpp"
45

56
static PyMethodDef algorithms_PyMethodDef[] = {
67
{"quick_sort", (PyCFunction) quick_sort,
@@ -9,6 +10,10 @@ static PyMethodDef algorithms_PyMethodDef[] = {
910
METH_VARARGS | METH_KEYWORDS, ""},
1011
{"selection_sort", (PyCFunction) selection_sort,
1112
METH_VARARGS | METH_KEYWORDS, ""},
13+
{"insertion_sort", (PyCFunction) insertion_sort,
14+
METH_VARARGS | METH_KEYWORDS, ""},
15+
{"is_ordered", (PyCFunction) is_ordered,
16+
METH_VARARGS | METH_KEYWORDS, ""},
1217
{NULL, NULL, 0, NULL} /* Sentinel */
1318
};
1419

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_MISC_ALGORITHMS
2+
#define LINEAR_DATA_STRUCTURES_ALGORITHMS_MISC_ALGORITHMS
3+
4+
#define PY_SSIZE_T_CLEAN
5+
#include <Python.h>
6+
#include "../arrays/OneDimensionalArray.hpp"
7+
#include "../arrays/DynamicOneDimensionalArray.hpp"
8+
#include "../../../../utils/_backend/cpp/utils.hpp"
9+
10+
// is_ordered
11+
static bool is_ordered_impl(PyObject* array, size_t lower, size_t upper,
12+
PyObject* comp) {
13+
for (size_t i = lower + 1; i < upper + 1; i++) {
14+
PyObject* i_PyObject = PyLong_FromSize_t(i);
15+
PyObject* i1_PyObject = PyLong_FromSize_t(i-1);
16+
PyObject* i_item = PyObject_GetItem(array, i_PyObject);
17+
PyObject* i1_item = PyObject_GetItem(array, i1_PyObject);
18+
if (i_item == Py_None || i1_item == Py_None) continue;
19+
if( _comp(i_item, i1_item, comp) == 1 ) {
20+
printf("%d--\n", i);
21+
return false;
22+
}
23+
}
24+
return true;
25+
}
26+
27+
static PyObject* is_ordered(PyObject* self, PyObject* args, PyObject* kwds) {
28+
PyObject *args0 = NULL, *start = NULL, *end = NULL;
29+
PyObject *comp = NULL, *res = NULL;
30+
size_t lower, upper;
31+
args0 = PyObject_GetItem(args, PyZero);
32+
int is_DynamicOneDimensionalArray = _check_type(args0, &DynamicOneDimensionalArrayType);
33+
int is_OneDimensionalArray = _check_type(args0, &OneDimensionalArrayType);
34+
if( !is_DynamicOneDimensionalArray && !is_OneDimensionalArray ) {
35+
raise_exception_if_not_array(args0);
36+
return NULL;
37+
}
38+
comp = PyObject_GetItem(kwds, PyUnicode_FromString("comp"));
39+
if( comp == NULL ) {
40+
PyErr_Clear();
41+
}
42+
start = PyObject_GetItem(kwds, PyUnicode_FromString("start"));
43+
if( start == NULL ) {
44+
PyErr_Clear();
45+
lower = 0;
46+
} else {
47+
lower = PyLong_AsSize_t(start);
48+
}
49+
end = PyObject_GetItem(kwds, PyUnicode_FromString("end"));
50+
if( end == NULL ) {
51+
PyErr_Clear();
52+
upper = PyObject_Length(args0) - 1;
53+
} else {
54+
upper = PyLong_AsSize_t(end);
55+
}
56+
57+
bool _res = is_ordered_impl(args0, lower, upper, comp);
58+
res = PyBool_FromLong(_res);
59+
Py_INCREF(res);
60+
return res;
61+
}
62+
63+
64+
#endif

pydatastructs/linear_data_structures/_backend/cpp/algorithms/n2_square_sort.hpp renamed to pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_N2_SQUARE_SORT_HPP
2-
#define LINEAR_DATA_STRUCTURES_ALGORITHMS_N2_SQUARE_SORT_HPP
1+
#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_QUADRATIC_TIME_SORT_HPP
2+
#define LINEAR_DATA_STRUCTURES_ALGORITHMS_QUADRATIC_TIME_SORT_HPP
33

44
#define PY_SSIZE_T_CLEAN
55
#include <Python.h>
@@ -127,4 +127,62 @@ static PyObject* selection_sort(PyObject* self, PyObject* args, PyObject* kwds)
127127
return args0;
128128
}
129129

130+
131+
// Insertion Sort
132+
static PyObject* insertion_sort_impl(PyObject* array, size_t lower, size_t upper,
133+
PyObject* comp) {
134+
for (size_t i = lower + 1; i < upper + 1; i++) {
135+
PyObject* i_PyObject = PyLong_FromSize_t(i);
136+
PyObject* temp = PyObject_GetItem(array, i_PyObject);
137+
size_t j = i;
138+
while (j > lower && _comp(PyObject_GetItem(array, PyLong_FromSize_t(j-1)),
139+
temp, comp) != 1) {
140+
PyObject_SetItem(array, PyLong_FromSize_t(j),
141+
PyObject_GetItem(array, PyLong_FromSize_t(j-1)));
142+
j -= 1;
143+
}
144+
PyObject_SetItem(array, PyLong_FromSize_t(j), temp);
145+
}
146+
return array;
147+
}
148+
149+
static PyObject* insertion_sort(PyObject* self, PyObject* args, PyObject* kwds) {
150+
PyObject *args0 = NULL, *start = NULL, *end = NULL;
151+
PyObject *comp = NULL, *pick_pivot_element = NULL;
152+
size_t lower, upper;
153+
args0 = PyObject_GetItem(args, PyZero);
154+
int is_DynamicOneDimensionalArray = _check_type(args0, &DynamicOneDimensionalArrayType);
155+
int is_OneDimensionalArray = _check_type(args0, &OneDimensionalArrayType);
156+
if( !is_DynamicOneDimensionalArray && !is_OneDimensionalArray ) {
157+
raise_exception_if_not_array(args0);
158+
return NULL;
159+
}
160+
comp = PyObject_GetItem(kwds, PyUnicode_FromString("comp"));
161+
if( comp == NULL ) {
162+
PyErr_Clear();
163+
}
164+
start = PyObject_GetItem(kwds, PyUnicode_FromString("start"));
165+
if( start == NULL ) {
166+
PyErr_Clear();
167+
lower = 0;
168+
} else {
169+
lower = PyLong_AsSize_t(start);
170+
}
171+
end = PyObject_GetItem(kwds, PyUnicode_FromString("end"));
172+
if( end == NULL ) {
173+
PyErr_Clear();
174+
upper = PyObject_Length(args0) - 1;
175+
} else {
176+
upper = PyLong_AsSize_t(end);
177+
}
178+
179+
args0 = insertion_sort_impl(args0, lower, upper, comp);
180+
if( is_DynamicOneDimensionalArray ) {
181+
PyObject_CallMethod(args0, "_modify", "O", Py_True);
182+
}
183+
Py_INCREF(args0);
184+
return args0;
185+
}
186+
187+
130188
#endif

pydatastructs/linear_data_structures/algorithms.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -907,8 +907,9 @@ def is_ordered(array, **kwargs):
907907
>>> is_ordered(arr1, start=0, end=1, comp=lambda u, v: u > v)
908908
False
909909
"""
910-
raise_if_backend_is_not_python(
911-
is_ordered, kwargs.get('backend', Backend.PYTHON))
910+
backend = kwargs.pop("backend", Backend.PYTHON)
911+
if backend == Backend.CPP:
912+
return _algorithms.is_ordered(array, **kwargs)
912913
lower = kwargs.get('start', 0)
913914
upper = kwargs.get('end', len(array) - 1)
914915
comp = kwargs.get("comp", lambda u, v: u <= v)
@@ -1504,8 +1505,9 @@ def insertion_sort(array, **kwargs):
15041505
15051506
.. [1] https://en.wikipedia.org/wiki/Insertion_sort
15061507
"""
1507-
raise_if_backend_is_not_python(
1508-
insertion_sort, kwargs.get('backend', Backend.PYTHON))
1508+
backend = kwargs.pop("backend", Backend.PYTHON)
1509+
if backend == Backend.CPP:
1510+
return _algorithms.insertion_sort(array, **kwargs)
15091511
start = kwargs.get('start', 0)
15101512
end = kwargs.get('end', len(array) - 1)
15111513
comp = kwargs.get('comp', lambda u, v: u <= v)

pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import random, timeit, functools, os, pytest
22
from pydatastructs import (OneDimensionalArray, Backend,
3-
DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort)
3+
DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort,
4+
insertion_sort, is_ordered)
45

56
def _test_common_sort(sort, **kwargs):
67
cpp = Backend.CPP
@@ -44,3 +45,38 @@ def test_bubble_sort():
4445
@pytest.mark.xfail
4546
def test_selection_sort():
4647
_test_common_sort(selection_sort, size=2000)
48+
49+
50+
@pytest.mark.xfail
51+
def test_insertion_sort():
52+
_test_common_sort(insertion_sort, size=2000)
53+
54+
55+
@pytest.mark.xfail
56+
def test_is_ordered():
57+
cpp = Backend.CPP
58+
repeat = 2
59+
number = 2
60+
61+
size = int(os.environ.get("PYDATASTRUCTS_BENCHMARK_SIZE", "4000"))
62+
63+
def _common(array_type, dtype, *args, **kwargs):
64+
array = array_type(dtype, *args, **kwargs)
65+
66+
timer_python = timeit.Timer(functools.partial(is_ordered, array))
67+
python_backend = min(timer_python.repeat(repeat, number))
68+
69+
backend_dict = {"backend": cpp}
70+
timer_cpp = timeit.Timer(functools.partial(is_ordered, array,
71+
**backend_dict))
72+
cpp_backend = min(timer_cpp.repeat(repeat, number))
73+
74+
assert cpp_backend < python_backend
75+
76+
# Case 1: int
77+
data = [random.randint(0, 2 * size) for _ in range(size)]
78+
_common(OneDimensionalArray, int, data, backend=cpp)
79+
80+
# Case 3: float
81+
data = [random.random() * 2 * size for _ in range(size)]
82+
_common(OneDimensionalArray, float, data, backend=cpp)

pydatastructs/linear_data_structures/tests/test_algorithms.py

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def test_selection_sort():
125125

126126
def test_insertion_sort():
127127
_test_common_sort(insertion_sort)
128+
_test_common_sort(insertion_sort, backend=Backend.CPP)
128129

129130
def test_matrix_multiply_parallel():
130131
ODA = OneDimensionalArray
@@ -176,34 +177,40 @@ def test_longest_common_sequence():
176177
assert str(output) == '[]'
177178

178179
def test_is_ordered():
179-
ODA = OneDimensionalArray
180-
DODA = DynamicOneDimensionalArray
181-
182-
expected_result = True
183-
arr = ODA(int, [1, 2, 5, 6])
184-
output = is_ordered(arr)
185-
assert output == expected_result
186-
187-
expected_result = False
188-
arr1 = ODA(int, [4, 3, 2, 1])
189-
output = is_ordered(arr1)
190-
assert output == expected_result
191-
192-
expected_result = True
193-
arr2 = ODA(int, [6, 1, 2, 3, 4, 5])
194-
output = is_ordered(arr2, start=1, end=5)
195-
assert output == expected_result
196-
197-
expected_result = True
198-
arr3 = ODA(int, [0, -1, -2, -3, -4, 4])
199-
output = is_ordered(arr3, start=1, end=4, comp=lambda u, v: u > v)
200-
assert output == expected_result
201-
202-
expected_result = True
203-
arr4 = DODA(int, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
204-
arr4.delete(0)
205-
output = is_ordered(arr4)
206-
assert output == expected_result
180+
def _test_inner_ordered(*args, **kwargs):
181+
ODA = OneDimensionalArray
182+
DODA = DynamicOneDimensionalArray
183+
184+
expected_result = True
185+
arr = ODA(int, [1, 2, 5, 6])
186+
output = is_ordered(arr, **kwargs)
187+
assert output == expected_result
188+
189+
expected_result = False
190+
arr1 = ODA(int, [4, 3, 2, 1])
191+
output = is_ordered(arr1, **kwargs)
192+
assert output == expected_result
193+
194+
expected_result = True
195+
arr2 = ODA(int, [6, 1, 2, 3, 4, 5])
196+
output = is_ordered(arr2, start=1, end=5, **kwargs)
197+
assert output == expected_result
198+
199+
expected_result = True
200+
arr3 = ODA(int, [0, -1, -2, -3, -4, 4])
201+
output = is_ordered(arr3, start=1, end=4,
202+
comp=lambda u, v: u > v, **kwargs)
203+
assert output == expected_result
204+
205+
expected_result = True
206+
arr4 = DODA(int, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
207+
arr4.delete(0)
208+
output = is_ordered(arr4, **kwargs)
209+
assert output == expected_result
210+
211+
_test_inner_ordered()
212+
_test_inner_ordered(backend=Backend.CPP)
213+
207214

208215
def test_upper_bound():
209216
ODA = OneDimensionalArray

0 commit comments

Comments
 (0)