differintC 0.1.0__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.
File without changes
@@ -0,0 +1,100 @@
1
+ cmake_minimum_required(VERSION 3.15)
2
+ # ——————————————————————————————————————————————————————————
3
+ # 1) Project Declaration & C++ Standard
4
+ # ——————————————————————————————————————————————————————————
5
+ project(differintC
6
+ VERSION 0.1.0
7
+ DESCRIPTION "Standalone C++ library for fractional differintegrals"
8
+ LANGUAGES CXX
9
+ )
10
+
11
+ # Force C++17
12
+ set(CMAKE_CXX_STANDARD 17)
13
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
14
+
15
+ # ——————————————————————————————————————————————————————————
16
+ # 2) Find Python & pybind11
17
+ # ——————————————————————————————————————————————————————————
18
+ find_package(Python COMPONENTS Interpreter Development REQUIRED)
19
+
20
+ include(FetchContent)
21
+ FetchContent_Declare(
22
+ pybind11
23
+ GIT_REPOSITORY https://github.com/pybind/pybind11.git
24
+ GIT_TAG v2.11.0
25
+ )
26
+ FetchContent_MakeAvailable(pybind11)
27
+
28
+ # ——————————————————————————————————————————————————————————
29
+ # 3) Core Library Target
30
+ # ——————————————————————————————————————————————————————————
31
+ add_library(differint_core
32
+ src/differint/differint.cpp
33
+ )
34
+ target_include_directories(differint_core
35
+ PUBLIC ${CMAKE_SOURCE_DIR}/include
36
+ )
37
+
38
+ # Optional standalone sanity checker
39
+ if(DIFFCHECK_GL)
40
+ add_executable(sanity
41
+ src/differint/sanity.cpp
42
+ )
43
+ target_include_directories(sanity
44
+ PRIVATE ${CMAKE_SOURCE_DIR}/include
45
+ )
46
+ target_link_libraries(sanity
47
+ PRIVATE differint_core
48
+ )
49
+ endif()
50
+
51
+ # ——————————————————————————————————————————————————————————
52
+ # 4) Python Extension Module (differintC.pyd)
53
+ # ——————————————————————————————————————————————————————————
54
+ pybind11_add_module(differintC MODULE
55
+ python/module.cpp
56
+ )
57
+
58
+ target_include_directories(differintC
59
+ PRIVATE ${CMAKE_SOURCE_DIR}/include
60
+ )
61
+
62
+ target_link_libraries(differintC
63
+ PRIVATE differint_core
64
+ )
65
+
66
+ # Fix Windows pyd naming and output
67
+ set_target_properties(differintC PROPERTIES
68
+ PREFIX "" # Avoid "lib" prefix
69
+ SUFFIX ".pyd" # Ensure Python .pyd extension
70
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Release # Optional: match build dir
71
+ )
72
+
73
+
74
+
75
+ # ——————————————————————————————————————————————————————————
76
+ # 5) Install: Python module into site-packages
77
+ # ——————————————————————————————————————————————————————————
78
+ # Detect Python site-packages directory
79
+ execute_process(
80
+ COMMAND "${Python_EXECUTABLE}" -c
81
+ "import sysconfig; print(sysconfig.get_paths()['purelib'])"
82
+ OUTPUT_VARIABLE PYTHON_SITE_PACKAGES
83
+ OUTPUT_STRIP_TRAILING_WHITESPACE
84
+ )
85
+
86
+ install(TARGETS differintC
87
+ LIBRARY DESTINATION "${PYTHON_SITE_PACKAGES}" # for .pyd module
88
+ )
89
+
90
+ # Optional: install core lib if desired
91
+ install(TARGETS differint_core
92
+ ARCHIVE DESTINATION lib
93
+ LIBRARY DESTINATION lib
94
+ )
95
+
96
+ # ——————————————————————————————————————————————————————————
97
+ # 5) (Optional) Testing — commented out until you add tests
98
+ # ——————————————————————————————————————————————————————————
99
+ # enable_testing()
100
+ # add_subdirectory(tests)
File without changes
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: differintC
3
+ Version: 0.1.0
4
+ Summary: Fast C++ implementation of fractional calculus operators
5
+ Author: Your Name
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+
File without changes
@@ -0,0 +1,5 @@
1
+ @PACKAGE_INIT@
2
+
3
+ include("${CMAKE_CURRENT_LIST_DIR}/differintCTargets.cmake")
4
+
5
+ set(differintC_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../../include")
File without changes
File without changes
@@ -0,0 +1,74 @@
1
+ #pragma once
2
+
3
+ #include <vector>
4
+ #include <cstddef>
5
+
6
+ namespace differint {
7
+
8
+ /**
9
+ * Riemann–Liouville fractional derivative at a single point using trapezoid rule.
10
+ * @tparam T Numeric type (e.g. float, double)
11
+ * @param alpha Fractional order (0 < alpha < 1)
12
+ * @param f_vals Either sample values of f at uniformly spaced points, or size-0 vector
13
+ * if no precomputed samples; in that case, user must call with
14
+ * a custom provider (see implementation)
15
+ * @param domain_start Left endpoint of interval
16
+ * @param domain_end Right endpoint of interval
17
+ * @param num_points Number of discretization points
18
+ * @return Approximated fractional derivative value
19
+ */
20
+ template <typename T>
21
+ T RLpoint(T alpha,
22
+ const std::vector<T>& f_vals,
23
+ T domain_start = T(0),
24
+ T domain_end = T(1),
25
+ std::size_t num_points = 100);
26
+
27
+
28
+ /// Riemann–Liouville fractional derivative over the entire grid
29
+ template <typename T>
30
+ std::vector<T> RL(T alpha,
31
+ const std::vector<T>& f_vals,
32
+ T domain_start = T(0),
33
+ T domain_end = T(1),
34
+ std::size_t num_points = 100);
35
+
36
+
37
+
38
+
39
+
40
+ /**
41
+ * Grünwald–Letnikov fractional derivative at a single point.
42
+ * @tparam T
43
+ * @param alpha Fractional order
44
+ * @param f_vals Sampled function values at uniform nodes
45
+ * @param domain_start Left endpoint
46
+ * @param domain_end Right endpoint
47
+ * @param num_points Number of nodes
48
+ * @return Approximated derivative
49
+ */
50
+ template <typename T>
51
+ T GLpoint(T alpha,
52
+ const std::vector<T>& f_vals,
53
+ T domain_start = T(0),
54
+ T domain_end = T(1),
55
+ std::size_t num_points = 100);
56
+
57
+ /**
58
+ * Grünwald–Letnikov fractional derivative over entire grid.
59
+ * @tparam T
60
+ * @param alpha Fractional order
61
+ * @param f_vals Sampled function values at uniform nodes
62
+ * @param domain_start Left endpoint
63
+ * @param domain_end Right endpoint
64
+ * @param num_points Number of nodes
65
+ * @return Vector of derivative values at each node
66
+ */
67
+ template <typename T>
68
+ std::vector<T> GL(T alpha,
69
+ const std::vector<T>& f_vals,
70
+ T domain_start = T(0),
71
+ T domain_end = T(1),
72
+ std::size_t num_points = 100);
73
+
74
+ } // namespace differint
@@ -0,0 +1,12 @@
1
+ [build-system]
2
+ requires = ["scikit-build-core", "pybind11"]
3
+ build-backend = "scikit_build_core.build"
4
+
5
+ [project]
6
+ name = "differintC"
7
+ version = "0.1.0"
8
+ description = "Fast C++ implementation of fractional calculus operators"
9
+ authors = [{ name = "Your Name" }]
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ requires-python = ">=3.8"
@@ -0,0 +1,21 @@
1
+ # python/CMakeLists.txt
2
+
3
+ # Create the Python extension module
4
+ pybind11_add_module(differintC module.cpp)
5
+
6
+ # Ensure it can include our public headers
7
+ target_include_directories(differintC
8
+ PRIVATE ${CMAKE_SOURCE_DIR}/include
9
+ )
10
+
11
+ # Link against the core library
12
+ target_link_libraries(differintC
13
+ PRIVATE differint_core
14
+ )
15
+
16
+ # Optionally set properties so it's built in Release by default
17
+ set_target_properties(differintC PROPERTIES
18
+ CXX_STANDARD 17
19
+ CXX_STANDARD_REQUIRED YES
20
+ BUILD_WITH_INSTALL_RPATH ON
21
+ )
@@ -0,0 +1,80 @@
1
+ #include <pybind11/pybind11.h>
2
+ #include <pybind11/numpy.h>
3
+ #include "differint/differint.hpp"
4
+
5
+ namespace py = pybind11;
6
+
7
+ template <typename Func>
8
+ std::vector<double> call_and_sample(Func&& f, double a, double b, size_t n) {
9
+ // Build the input grid
10
+ std::vector<double> vals(n);
11
+ double step = (b - a) / (n - 1);
12
+ for (size_t i = 0; i < n; ++i)
13
+ vals[i] = f(a + step * i);
14
+ return vals;
15
+ }
16
+
17
+ // Helper to convert any array-like or callable to a std::vector<double>
18
+ std::vector<double> prepare_fvals(py::object f_obj,
19
+ double a, double b, size_t n) {
20
+ // If it's an array or list, cast directly
21
+ if (py::isinstance<py::array>(f_obj) ||
22
+ py::isinstance<py::list>(f_obj)) {
23
+ auto arr = py::array_t<double>::ensure(f_obj);
24
+ if (!arr || arr.size() != n)
25
+ throw std::runtime_error("Input array length must equal num_points");
26
+ std::vector<double> v(n);
27
+ std::memcpy(v.data(), arr.data(), n * sizeof(double));
28
+ return v;
29
+ }
30
+ // Otherwise assume callable
31
+ auto cb = f_obj.cast<py::function>();
32
+ return call_and_sample(
33
+ [&](double x){ return cb(x).cast<double>(); },
34
+ a, b, n
35
+ );
36
+ }
37
+
38
+ PYBIND11_MODULE(differintC, m) {
39
+ m.doc() = "Fractional differintegral routines (RL & GL)";
40
+
41
+ m.def("RLpoint",
42
+ [](double alpha, py::object f, double a, double b, size_t n) {
43
+ auto fv = prepare_fvals(f, a, b, n);
44
+ return differint::RLpoint(alpha, fv, a, b, n);
45
+ },
46
+ py::arg("alpha"), py::arg("f_name"),
47
+ py::arg("domain_start")=0.0, py::arg("domain_end")=1.0,
48
+ py::arg("num_points")=100
49
+ );
50
+
51
+ m.def("RL",
52
+ [](double alpha, py::object f, double a, double b, size_t n) {
53
+ auto fv = prepare_fvals(f, a, b, n);
54
+ return differint::RL(alpha, fv, a, b, n);
55
+ },
56
+ py::arg("alpha"), py::arg("f_name"),
57
+ py::arg("domain_start")=0.0, py::arg("domain_end")=1.0,
58
+ py::arg("num_points")=100
59
+ );
60
+
61
+ m.def("GLpoint",
62
+ [](double alpha, py::object f, double a, double b, size_t n) {
63
+ auto fv = prepare_fvals(f, a, b, n);
64
+ return differint::GLpoint(alpha, fv, a, b, n);
65
+ },
66
+ py::arg("alpha"), py::arg("f_name"),
67
+ py::arg("domain_start")=0.0, py::arg("domain_end")=1.0,
68
+ py::arg("num_points")=100
69
+ );
70
+
71
+ m.def("GL",
72
+ [](double alpha, py::object f, double a, double b, size_t n) {
73
+ auto fv = prepare_fvals(f, a, b, n);
74
+ return differint::GL(alpha, fv, a, b, n);
75
+ },
76
+ py::arg("alpha"), py::arg("f_name"),
77
+ py::arg("domain_start")=0.0, py::arg("domain_end")=1.0,
78
+ py::arg("num_points")=100
79
+ );
80
+ }
@@ -0,0 +1,208 @@
1
+ #include <differint/differint.hpp>
2
+ #include <cmath>
3
+ #include <stdexcept>
4
+ #include <algorithm>
5
+
6
+ namespace differint {
7
+
8
+ template <typename T>
9
+ T RLpoint(T alpha,
10
+ const std::vector<T>& f_vals,
11
+ T domain_start,
12
+ T domain_end,
13
+ std::size_t num_points) {
14
+ if (num_points < 2) {
15
+ throw std::invalid_argument("num_points must be at least 2");
16
+ }
17
+ // Ensure domain ordering
18
+ if (domain_start > domain_end) {
19
+ std::swap(domain_start, domain_end);
20
+ }
21
+ // Check input vector length
22
+ if (f_vals.size() != num_points) {
23
+ throw std::invalid_argument("f_vals size does not match num_points");
24
+ }
25
+ // Compute step size
26
+ T step = (domain_end - domain_start) / static_cast<T>(num_points - 1);
27
+
28
+ std::size_t k = num_points - 1;
29
+ std::vector<T> coeffs(num_points);
30
+
31
+ // Case j == 0
32
+ if (k > 0) {
33
+ coeffs[0] = std::pow(static_cast<T>(k - 1), static_cast<T>(1) - alpha)
34
+ - (static_cast<T>(k) + alpha - static_cast<T>(1))
35
+ * std::pow(static_cast<T>(k), -alpha);
36
+ } else {
37
+ coeffs[0] = T(1);
38
+ }
39
+
40
+ // Case j == k
41
+ coeffs[k] = T(1);
42
+
43
+ // Other indices
44
+ for (std::size_t j = 1; j < k; ++j) {
45
+ T d = static_cast<T>(k - j);
46
+ coeffs[j] = std::pow(d + static_cast<T>(1), static_cast<T>(1) - alpha)
47
+ + std::pow(d - static_cast<T>(1), static_cast<T>(1) - alpha)
48
+ - static_cast<T>(2) * std::pow(d, static_cast<T>(1) - alpha);
49
+ }
50
+
51
+ // Compute normalization constant
52
+ T C = static_cast<T>(1) / std::tgamma(static_cast<T>(2) - alpha);
53
+
54
+ // Dot product
55
+ T result = T(0);
56
+ for (std::size_t i = 0; i < num_points; ++i) {
57
+ result += coeffs[i] * f_vals[i];
58
+ }
59
+
60
+ return C * std::pow(step, -alpha) * result;
61
+ }
62
+
63
+
64
+ // RL entire-grid implementation
65
+ template <typename T>
66
+ std::vector<T> RL(T alpha,
67
+ const std::vector<T>& f_vals,
68
+ T domain_start,
69
+ T domain_end,
70
+ std::size_t num_points) {
71
+ if (num_points < 2) {
72
+ throw std::invalid_argument("num_points must be at least 2");
73
+ }
74
+ if (domain_start > domain_end) {
75
+ std::swap(domain_start, domain_end);
76
+ }
77
+ if (f_vals.size() != num_points) {
78
+ throw std::invalid_argument("f_vals size does not match num_points");
79
+ }
80
+ T step = (domain_end - domain_start) / static_cast<T>(num_points - 1);
81
+
82
+ // Precompute coefficient matrix D
83
+ std::vector<std::vector<T>> D(num_points, std::vector<T>(num_points));
84
+ // Precompute powers v[k] = (k)^(1-alpha)
85
+ std::vector<T> v(num_points + 1);
86
+ for (std::size_t k = 0; k <= num_points; ++k) {
87
+ v[k] = std::pow(static_cast<T>(k), static_cast<T>(1) - alpha);
88
+ }
89
+
90
+ // Fill D
91
+ for (std::size_t i = 0; i < num_points; ++i) {
92
+ for (std::size_t j = 0; j < num_points; ++j) {
93
+ if (j == i) {
94
+ D[i][j] = T(1);
95
+ } else if (j == 0 && i > 0) {
96
+ T k = static_cast<T>(i);
97
+ D[i][0] = v[i - 1] - (k + alpha - static_cast<T>(1)) * std::pow(k, -alpha);
98
+ } else if (j < i) {
99
+ std::size_t k = i - j;
100
+ D[i][j] = v[k + 1] + v[k - 1] - static_cast<T>(2) * v[k];
101
+ } else {
102
+ D[i][j] = T(0);
103
+ }
104
+ }
105
+ }
106
+
107
+ // Normalize by Gamma
108
+ T C = static_cast<T>(1) / std::tgamma(static_cast<T>(2) - alpha);
109
+
110
+ // Multiply D @ f_vals
111
+ std::vector<T> result(num_points, T(0));
112
+ for (std::size_t i = 0; i < num_points; ++i) {
113
+ T acc = T(0);
114
+ for (std::size_t j = 0; j < num_points; ++j) {
115
+ acc += D[i][j] * f_vals[j];
116
+ }
117
+ result[i] = C * std::pow(step, -alpha) * acc;
118
+ }
119
+ return result;
120
+ }
121
+
122
+
123
+
124
+
125
+ // Compute GL coefficients up to order n
126
+ template <typename T>
127
+ std::vector<T> GLcoeffs(T alpha, std::size_t n) {
128
+ // b[0] = 1
129
+ std::vector<T> b(n + 1, T(1));
130
+ for (std::size_t j = 1; j <= n; ++j) {
131
+ b[j] = b[j - 1] * (static_cast<T>(-alpha) + static_cast<T>(j - 1))
132
+ / static_cast<T>(j);
133
+ }
134
+ return b;
135
+ }
136
+
137
+
138
+ // GL at a single point (endpoint)
139
+ template <typename T>
140
+ T GLpoint(T alpha,
141
+ const std::vector<T>& f_vals,
142
+ T domain_start,
143
+ T domain_end,
144
+ std::size_t num_points) {
145
+ if (num_points < 1) {
146
+ throw std::invalid_argument("num_points must be at least 1");
147
+ }
148
+ if (domain_start > domain_end) {
149
+ std::swap(domain_start, domain_end);
150
+ }
151
+ if (f_vals.size() != num_points) {
152
+ throw std::invalid_argument("f_vals size must equal num_points");
153
+ }
154
+ T step = (domain_end - domain_start) / static_cast<T>(num_points - 1);
155
+ // Compute coefficients for k = num_points - 1
156
+ std::size_t k = num_points - 1;
157
+ auto b = GLcoeffs(alpha, k);
158
+ T acc = T(0);
159
+ for (std::size_t j = 0; j <= k; ++j) {
160
+ acc += b[j] * f_vals[k - j];
161
+ }
162
+ return std::pow(step, -alpha) * acc;
163
+ }
164
+
165
+ // GL over entire grid
166
+ template <typename T>
167
+ std::vector<T> GL(T alpha,
168
+ const std::vector<T>& f_vals,
169
+ T domain_start,
170
+ T domain_end,
171
+ std::size_t num_points) {
172
+ if (num_points < 1) {
173
+ throw std::invalid_argument("num_points must be at least 1");
174
+ }
175
+ if (domain_start > domain_end) {
176
+ std::swap(domain_start, domain_end);
177
+ }
178
+ if (f_vals.size() != num_points) {
179
+ throw std::invalid_argument("f_vals size must equal num_points");
180
+ }
181
+ T step = (domain_end - domain_start) / static_cast<T>(num_points - 1);
182
+ auto b = GLcoeffs(alpha, num_points - 1);
183
+ std::vector<T> result(num_points, T(0));
184
+ for (std::size_t i = 0; i < num_points; ++i) {
185
+ T acc = T(0);
186
+ // convolution-like sum
187
+ for (std::size_t j = 0; j <= i; ++j) {
188
+ acc += b[j] * f_vals[i - j];
189
+ }
190
+ result[i] = std::pow(step, -alpha) * acc;
191
+ }
192
+ return result;
193
+ }
194
+
195
+
196
+ // Explicit instantiations for double
197
+ template double RLpoint<double>(double, const std::vector<double>&, double, double, std::size_t);
198
+ template std::vector<double> RL<double>(double, const std::vector<double>&, double, double, std::size_t);
199
+ template double GLpoint<double>(double, const std::vector<double>&, double, double, std::size_t);
200
+ template std::vector<double> GL<double>(double, const std::vector<double>&, double, double, std::size_t);
201
+
202
+ template std::vector<double> GLcoeffs<double>(double, std::size_t);
203
+
204
+
205
+
206
+
207
+ } // namespace differint
208
+
@@ -0,0 +1,50 @@
1
+ #include <iostream>
2
+ #include <vector>
3
+ #include <cmath>
4
+ #include "differint/differint.hpp"
5
+
6
+ int main() {
7
+ double alpha = 0.5;
8
+ std::size_t n = 1000;
9
+ double a = 0.0, b = 1.0;
10
+
11
+ // sample f(x)=x^2
12
+ std::vector<double> vals(n);
13
+ for (std::size_t i = 0; i < n; ++i) {
14
+ double x = a + (b - a) * i / (n - 1);
15
+ vals[i] = x * x;
16
+ }
17
+
18
+ double num = differint::GLpoint(alpha, vals, a, b, n);
19
+ double exact = std::tgamma(3.0) / std::tgamma(3.0 - alpha);
20
+
21
+ std::cout << "GLpoint_num = " << num << "\n"
22
+ << "GLpoint_exact= " << exact << "\n"
23
+ << "Error = " << std::abs(num - exact) << "\n";
24
+
25
+ auto gl_vec = differint::RL(alpha, vals, a, b, n);
26
+ double gl_end = gl_vec.back();
27
+ std::cout << "\nGL.back() = " << gl_end
28
+ << "\nGL_exact = " << exact
29
+ << "\nError = " << std::abs(gl_end - exact) << "\n";
30
+
31
+
32
+
33
+
34
+ // RLpoint test: f(x)=x^2 at endpoint
35
+ double rl_num = differint::RLpoint(alpha, vals, a, b, n);
36
+ double rl_exact = std::tgamma(3.0)/std::tgamma(3.0 - alpha);
37
+ std::cout << "\nRLpoint_num = " << rl_num
38
+ << "\nRLpoint_exact= " << rl_exact
39
+ << "\nError = " << std::abs(rl_num - rl_exact) << "\n";
40
+
41
+ auto rl_vec = differint::RL(alpha, vals, a, b, n);
42
+ double rl_end = rl_vec.back();
43
+ std::cout << "\nRL.back() = " << rl_end
44
+ << "\nRL_exact = " << exact
45
+ << "\nError = " << std::abs(rl_end - exact) << "\n";
46
+
47
+ std::cout << "\nPress ENTER to exit...";
48
+ std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
49
+ return 0;
50
+ }
File without changes
File without changes