From 783a11311d317085b7021895ef0e10c5cfe53c9c Mon Sep 17 00:00:00 2001 From: Sakshi Oza <125877202+sak-codes@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:47:38 +0530 Subject: [PATCH 1/4] Rename file --- .../_backend/cpp/algorithms/algorithms.cpp | 2 +- .../{n2_square_sort.hpp => quadratic_time_sort.hpp} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename pydatastructs/linear_data_structures/_backend/cpp/algorithms/{n2_square_sort.hpp => quadratic_time_sort.hpp} (97%) diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp index bda75afc0..f05ea59c2 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp @@ -1,6 +1,6 @@ #include #include "quick_sort.hpp" -#include "n2_square_sort.hpp" +#include "quadratic_time_sort.hpp" static PyMethodDef algorithms_PyMethodDef[] = { {"quick_sort", (PyCFunction) quick_sort, diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/n2_square_sort.hpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp similarity index 97% rename from pydatastructs/linear_data_structures/_backend/cpp/algorithms/n2_square_sort.hpp rename to pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp index f6e99b305..fe44cf360 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/n2_square_sort.hpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp @@ -1,5 +1,5 @@ -#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_N2_SQUARE_SORT_HPP -#define LINEAR_DATA_STRUCTURES_ALGORITHMS_N2_SQUARE_SORT_HPP +#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_QUADRATIC_TIME_SORT_HPP +#define LINEAR_DATA_STRUCTURES_ALGORITHMS_QUADRATIC_TIME_SORT_HPP #define PY_SSIZE_T_CLEAN #include From 9ff1122bb0c57d9869364ccb74f9acee40076bd7 Mon Sep 17 00:00:00 2001 From: Sakshi Oza <125877202+sak-codes@users.noreply.github.com> Date: Fri, 21 Jul 2023 18:50:26 +0530 Subject: [PATCH 2/4] Add CPP insertion sort --- .../_backend/cpp/algorithms/algorithms.cpp | 2 + .../cpp/algorithms/quadratic_time_sort.hpp | 58 +++++++++++++++++++ .../linear_data_structures/algorithms.py | 5 +- .../tests/benchmarks/test_algorithms.py | 8 ++- .../tests/test_algorithms.py | 1 + 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp index f05ea59c2..e74f57f2f 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp @@ -9,6 +9,8 @@ static PyMethodDef algorithms_PyMethodDef[] = { METH_VARARGS | METH_KEYWORDS, ""}, {"selection_sort", (PyCFunction) selection_sort, METH_VARARGS | METH_KEYWORDS, ""}, + {"insertion_sort", (PyCFunction) insertion_sort, + METH_VARARGS | METH_KEYWORDS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp index fe44cf360..04e7fd119 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/quadratic_time_sort.hpp @@ -127,4 +127,62 @@ static PyObject* selection_sort(PyObject* self, PyObject* args, PyObject* kwds) return args0; } + +// Insertion Sort +static PyObject* insertion_sort_impl(PyObject* array, size_t lower, size_t upper, + PyObject* comp) { + for (size_t i = lower + 1; i < upper + 1; i++) { + PyObject* i_PyObject = PyLong_FromSize_t(i); + PyObject* temp = PyObject_GetItem(array, i_PyObject); + size_t j = i; + while (j > lower && _comp(PyObject_GetItem(array, PyLong_FromSize_t(j-1)), + temp, comp) != 1) { + PyObject_SetItem(array, PyLong_FromSize_t(j), + PyObject_GetItem(array, PyLong_FromSize_t(j-1))); + j -= 1; + } + PyObject_SetItem(array, PyLong_FromSize_t(j), temp); + } + return array; +} + +static PyObject* insertion_sort(PyObject* self, PyObject* args, PyObject* kwds) { + PyObject *args0 = NULL, *start = NULL, *end = NULL; + PyObject *comp = NULL, *pick_pivot_element = NULL; + size_t lower, upper; + args0 = PyObject_GetItem(args, PyZero); + int is_DynamicOneDimensionalArray = _check_type(args0, &DynamicOneDimensionalArrayType); + int is_OneDimensionalArray = _check_type(args0, &OneDimensionalArrayType); + if( !is_DynamicOneDimensionalArray && !is_OneDimensionalArray ) { + raise_exception_if_not_array(args0); + return NULL; + } + comp = PyObject_GetItem(kwds, PyUnicode_FromString("comp")); + if( comp == NULL ) { + PyErr_Clear(); + } + start = PyObject_GetItem(kwds, PyUnicode_FromString("start")); + if( start == NULL ) { + PyErr_Clear(); + lower = 0; + } else { + lower = PyLong_AsSize_t(start); + } + end = PyObject_GetItem(kwds, PyUnicode_FromString("end")); + if( end == NULL ) { + PyErr_Clear(); + upper = PyObject_Length(args0) - 1; + } else { + upper = PyLong_AsSize_t(end); + } + + args0 = insertion_sort_impl(args0, lower, upper, comp); + if( is_DynamicOneDimensionalArray ) { + PyObject_CallMethod(args0, "_modify", "O", Py_True); + } + Py_INCREF(args0); + return args0; +} + + #endif diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index ad1c9f1d4..821ada917 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -1504,8 +1504,9 @@ def insertion_sort(array, **kwargs): .. [1] https://en.wikipedia.org/wiki/Insertion_sort """ - raise_if_backend_is_not_python( - insertion_sort, kwargs.get('backend', Backend.PYTHON)) + backend = kwargs.pop("backend", Backend.PYTHON) + if backend == Backend.CPP: + return _algorithms.insertion_sort(array, **kwargs) start = kwargs.get('start', 0) end = kwargs.get('end', len(array) - 1) comp = kwargs.get('comp', lambda u, v: u <= v) diff --git a/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py b/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py index d0b9f8cd0..53320bf75 100644 --- a/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py @@ -1,6 +1,7 @@ import random, timeit, functools, os, pytest from pydatastructs import (OneDimensionalArray, Backend, - DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort) + DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort, + insertion_sort) def _test_common_sort(sort, **kwargs): cpp = Backend.CPP @@ -44,3 +45,8 @@ def test_bubble_sort(): @pytest.mark.xfail def test_selection_sort(): _test_common_sort(selection_sort, size=2000) + + +@pytest.mark.xfail +def test_insertion_sort(): + _test_common_sort(insertion_sort, size=2000) diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 1b36d9e34..6af96e78e 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -125,6 +125,7 @@ def test_selection_sort(): def test_insertion_sort(): _test_common_sort(insertion_sort) + _test_common_sort(insertion_sort, backend=Backend.CPP) def test_matrix_multiply_parallel(): ODA = OneDimensionalArray From a1f594d2949a5871aecdd9d0f01421c18a0b681a Mon Sep 17 00:00:00 2001 From: Sakshi Oza <125877202+sak-codes@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:43:05 +0530 Subject: [PATCH 3/4] CPP for is_ordered --- .../_backend/cpp/algorithms/algorithms.cpp | 3 + .../cpp/algorithms/misc_algorithms.hpp | 64 +++++++++++++++++++ .../linear_data_structures/algorithms.py | 5 +- .../tests/test_algorithms.py | 62 ++++++++++-------- 4 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 pydatastructs/linear_data_structures/_backend/cpp/algorithms/misc_algorithms.hpp diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp index e74f57f2f..37f331732 100644 --- a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/algorithms.cpp @@ -1,6 +1,7 @@ #include #include "quick_sort.hpp" #include "quadratic_time_sort.hpp" +#include "misc_algorithms.hpp" static PyMethodDef algorithms_PyMethodDef[] = { {"quick_sort", (PyCFunction) quick_sort, @@ -11,6 +12,8 @@ static PyMethodDef algorithms_PyMethodDef[] = { METH_VARARGS | METH_KEYWORDS, ""}, {"insertion_sort", (PyCFunction) insertion_sort, METH_VARARGS | METH_KEYWORDS, ""}, + {"is_ordered", (PyCFunction) is_ordered, + METH_VARARGS | METH_KEYWORDS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pydatastructs/linear_data_structures/_backend/cpp/algorithms/misc_algorithms.hpp b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/misc_algorithms.hpp new file mode 100644 index 000000000..40b21e807 --- /dev/null +++ b/pydatastructs/linear_data_structures/_backend/cpp/algorithms/misc_algorithms.hpp @@ -0,0 +1,64 @@ +#ifndef LINEAR_DATA_STRUCTURES_ALGORITHMS_MISC_ALGORITHMS +#define LINEAR_DATA_STRUCTURES_ALGORITHMS_MISC_ALGORITHMS + +#define PY_SSIZE_T_CLEAN +#include +#include "../arrays/OneDimensionalArray.hpp" +#include "../arrays/DynamicOneDimensionalArray.hpp" +#include "../../../../utils/_backend/cpp/utils.hpp" + +// is_ordered +static bool is_ordered_impl(PyObject* array, size_t lower, size_t upper, + PyObject* comp) { + for (size_t i = lower + 1; i < upper + 1; i++) { + PyObject* i_PyObject = PyLong_FromSize_t(i); + PyObject* i1_PyObject = PyLong_FromSize_t(i-1); + PyObject* i_item = PyObject_GetItem(array, i_PyObject); + PyObject* i1_item = PyObject_GetItem(array, i1_PyObject); + if (i_item == Py_None || i1_item == Py_None) continue; + if( _comp(i_item, i1_item, comp) == 1 ) { + printf("%d--\n", i); + return false; + } + } + return true; +} + +static PyObject* is_ordered(PyObject* self, PyObject* args, PyObject* kwds) { + PyObject *args0 = NULL, *start = NULL, *end = NULL; + PyObject *comp = NULL, *res = NULL; + size_t lower, upper; + args0 = PyObject_GetItem(args, PyZero); + int is_DynamicOneDimensionalArray = _check_type(args0, &DynamicOneDimensionalArrayType); + int is_OneDimensionalArray = _check_type(args0, &OneDimensionalArrayType); + if( !is_DynamicOneDimensionalArray && !is_OneDimensionalArray ) { + raise_exception_if_not_array(args0); + return NULL; + } + comp = PyObject_GetItem(kwds, PyUnicode_FromString("comp")); + if( comp == NULL ) { + PyErr_Clear(); + } + start = PyObject_GetItem(kwds, PyUnicode_FromString("start")); + if( start == NULL ) { + PyErr_Clear(); + lower = 0; + } else { + lower = PyLong_AsSize_t(start); + } + end = PyObject_GetItem(kwds, PyUnicode_FromString("end")); + if( end == NULL ) { + PyErr_Clear(); + upper = PyObject_Length(args0) - 1; + } else { + upper = PyLong_AsSize_t(end); + } + + bool _res = is_ordered_impl(args0, lower, upper, comp); + res = PyBool_FromLong(_res); + Py_INCREF(res); + return res; +} + + +#endif diff --git a/pydatastructs/linear_data_structures/algorithms.py b/pydatastructs/linear_data_structures/algorithms.py index 821ada917..2b6ee08bd 100644 --- a/pydatastructs/linear_data_structures/algorithms.py +++ b/pydatastructs/linear_data_structures/algorithms.py @@ -907,8 +907,9 @@ def is_ordered(array, **kwargs): >>> is_ordered(arr1, start=0, end=1, comp=lambda u, v: u > v) False """ - raise_if_backend_is_not_python( - is_ordered, kwargs.get('backend', Backend.PYTHON)) + backend = kwargs.pop("backend", Backend.PYTHON) + if backend == Backend.CPP: + return _algorithms.is_ordered(array, **kwargs) lower = kwargs.get('start', 0) upper = kwargs.get('end', len(array) - 1) comp = kwargs.get("comp", lambda u, v: u <= v) diff --git a/pydatastructs/linear_data_structures/tests/test_algorithms.py b/pydatastructs/linear_data_structures/tests/test_algorithms.py index 6af96e78e..90f195d25 100644 --- a/pydatastructs/linear_data_structures/tests/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/test_algorithms.py @@ -177,34 +177,40 @@ def test_longest_common_sequence(): assert str(output) == '[]' def test_is_ordered(): - ODA = OneDimensionalArray - DODA = DynamicOneDimensionalArray - - expected_result = True - arr = ODA(int, [1, 2, 5, 6]) - output = is_ordered(arr) - assert output == expected_result - - expected_result = False - arr1 = ODA(int, [4, 3, 2, 1]) - output = is_ordered(arr1) - assert output == expected_result - - expected_result = True - arr2 = ODA(int, [6, 1, 2, 3, 4, 5]) - output = is_ordered(arr2, start=1, end=5) - assert output == expected_result - - expected_result = True - arr3 = ODA(int, [0, -1, -2, -3, -4, 4]) - output = is_ordered(arr3, start=1, end=4, comp=lambda u, v: u > v) - assert output == expected_result - - expected_result = True - arr4 = DODA(int, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) - arr4.delete(0) - output = is_ordered(arr4) - assert output == expected_result + def _test_inner_ordered(*args, **kwargs): + ODA = OneDimensionalArray + DODA = DynamicOneDimensionalArray + + expected_result = True + arr = ODA(int, [1, 2, 5, 6]) + output = is_ordered(arr, **kwargs) + assert output == expected_result + + expected_result = False + arr1 = ODA(int, [4, 3, 2, 1]) + output = is_ordered(arr1, **kwargs) + assert output == expected_result + + expected_result = True + arr2 = ODA(int, [6, 1, 2, 3, 4, 5]) + output = is_ordered(arr2, start=1, end=5, **kwargs) + assert output == expected_result + + expected_result = True + arr3 = ODA(int, [0, -1, -2, -3, -4, 4]) + output = is_ordered(arr3, start=1, end=4, + comp=lambda u, v: u > v, **kwargs) + assert output == expected_result + + expected_result = True + arr4 = DODA(int, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + arr4.delete(0) + output = is_ordered(arr4, **kwargs) + assert output == expected_result + + _test_inner_ordered() + _test_inner_ordered(backend=Backend.CPP) + def test_upper_bound(): ODA = OneDimensionalArray From b6d34efa0ceb93a4de7962a341148d8f05f8e03a Mon Sep 17 00:00:00 2001 From: Sakshi Oza <125877202+sak-codes@users.noreply.github.com> Date: Mon, 31 Jul 2023 19:04:33 +0530 Subject: [PATCH 4/4] Add benchmark test --- .../tests/benchmarks/test_algorithms.py | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py b/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py index 53320bf75..1825d69a4 100644 --- a/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py +++ b/pydatastructs/linear_data_structures/tests/benchmarks/test_algorithms.py @@ -1,7 +1,7 @@ import random, timeit, functools, os, pytest from pydatastructs import (OneDimensionalArray, Backend, DynamicOneDimensionalArray, quick_sort, bubble_sort, selection_sort, - insertion_sort) + insertion_sort, is_ordered) def _test_common_sort(sort, **kwargs): cpp = Backend.CPP @@ -50,3 +50,33 @@ def test_selection_sort(): @pytest.mark.xfail def test_insertion_sort(): _test_common_sort(insertion_sort, size=2000) + + +@pytest.mark.xfail +def test_is_ordered(): + cpp = Backend.CPP + repeat = 2 + number = 2 + + size = int(os.environ.get("PYDATASTRUCTS_BENCHMARK_SIZE", "4000")) + + def _common(array_type, dtype, *args, **kwargs): + array = array_type(dtype, *args, **kwargs) + + timer_python = timeit.Timer(functools.partial(is_ordered, array)) + python_backend = min(timer_python.repeat(repeat, number)) + + backend_dict = {"backend": cpp} + timer_cpp = timeit.Timer(functools.partial(is_ordered, array, + **backend_dict)) + cpp_backend = min(timer_cpp.repeat(repeat, number)) + + assert cpp_backend < python_backend + + # Case 1: int + data = [random.randint(0, 2 * size) for _ in range(size)] + _common(OneDimensionalArray, int, data, backend=cpp) + + # Case 3: float + data = [random.random() * 2 * size for _ in range(size)] + _common(OneDimensionalArray, float, data, backend=cpp)