From 78a3ee10b234f292421bf79844ea8d3b956bb961 Mon Sep 17 00:00:00 2001 From: Carlos Bazaga Date: Mon, 1 Sep 2025 01:37:59 +0200 Subject: [PATCH] Added sample files generated on the three different scenarios. Including C code, setup.py and run example code for: * Pi calculation with Leibniz formula. * Max sub-array calculation. (Aka: Python Hard) * Default for Gradio: Hello world. (Aka: _zz_my_module) --- .../c_extension_generator/calculate_pi.c | 83 ++++++ .../c_extension_generator/python_hard.c | 244 ++++++++++++++++++ .../setup_calculate_pi.py | 25 ++ .../setup_python_hard.py | 25 ++ .../setup_zz_my_module.py | 14 + .../usage_example_calculate_pi.py | 38 +++ .../usage_example_python_hard.py | 69 +++++ .../usage_example_zz_my_module.py | 16 ++ .../c_extension_generator/zz_my_module.c | 28 ++ 9 files changed, 542 insertions(+) create mode 100644 week4/community-contributions/c_extension_generator/calculate_pi.c create mode 100644 week4/community-contributions/c_extension_generator/python_hard.c create mode 100644 week4/community-contributions/c_extension_generator/setup_calculate_pi.py create mode 100644 week4/community-contributions/c_extension_generator/setup_python_hard.py create mode 100644 week4/community-contributions/c_extension_generator/setup_zz_my_module.py create mode 100644 week4/community-contributions/c_extension_generator/usage_example_calculate_pi.py create mode 100644 week4/community-contributions/c_extension_generator/usage_example_python_hard.py create mode 100644 week4/community-contributions/c_extension_generator/usage_example_zz_my_module.py create mode 100644 week4/community-contributions/c_extension_generator/zz_my_module.c diff --git a/week4/community-contributions/c_extension_generator/calculate_pi.c b/week4/community-contributions/c_extension_generator/calculate_pi.c new file mode 100644 index 0000000..c0b7f03 --- /dev/null +++ b/week4/community-contributions/c_extension_generator/calculate_pi.c @@ -0,0 +1,83 @@ +#define PY_SSIZE_T_CLEAN +#include +#include +#include +#include +#include + +static PyObject* leibniz_pi(PyObject* self, PyObject* args) { + PyObject* iterations_obj; + if (!PyArg_ParseTuple(args, "O", &iterations_obj)) { + return NULL; + } + + long long n_signed; + int overflow = 0; + n_signed = PyLong_AsLongLongAndOverflow(iterations_obj, &overflow); + if (n_signed == -1 && PyErr_Occurred() && overflow == 0) { + return NULL; + } + + unsigned long long n = 0ULL; + if (overflow < 0) { + n = 0ULL; + } else if (overflow > 0) { + unsigned long long tmp = PyLong_AsUnsignedLongLong(iterations_obj); + if (tmp == (unsigned long long)-1 && PyErr_Occurred()) { + return NULL; + } + n = tmp; + } else { + if (n_signed <= 0) { + n = 0ULL; + } else { + n = (unsigned long long)n_signed; + } + } + + double result = 1.0; + if (n == 0ULL) { + return PyFloat_FromDouble(result * 4.0); + } + + Py_BEGIN_ALLOW_THREADS + for (unsigned long long i = 1ULL; i <= n; ++i) { + double jd1; + if (i <= ULLONG_MAX / 4ULL) { + unsigned long long j1 = i * 4ULL - 1ULL; + jd1 = (double)j1; + } else { + jd1 = (double)i * 4.0 - 1.0; + } + result -= 1.0 / jd1; + + double jd2; + if (i <= (ULLONG_MAX - 1ULL) / 4ULL) { + unsigned long long j2 = i * 4ULL + 1ULL; + jd2 = (double)j2; + } else { + jd2 = (double)i * 4.0 + 1.0; + } + result += 1.0 / jd2; + } + Py_END_ALLOW_THREADS + + return PyFloat_FromDouble(result * 4.0); +} + +static PyMethodDef CalculatePiMethods[] = { + {"leibniz_pi", leibniz_pi, METH_VARARGS, "Compute pi using the Leibniz series with the given number of iterations."}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef calculate_pimodule = { + PyModuleDef_HEAD_INIT, + "calculate_pi", + "High-performance Leibniz pi calculation.", + -1, + CalculatePiMethods +}; + +PyMODINIT_FUNC PyInit_calculate_pi(void) { + return PyModule_Create(&calculate_pimodule); +} diff --git a/week4/community-contributions/c_extension_generator/python_hard.c b/week4/community-contributions/c_extension_generator/python_hard.c new file mode 100644 index 0000000..67968ea --- /dev/null +++ b/week4/community-contributions/c_extension_generator/python_hard.c @@ -0,0 +1,244 @@ +#include +#include +#include +#include +#include + +// LCG step with 32-bit wrap-around +static inline uint32_t lcg_next(uint32_t *state) { + *state = (uint32_t)(1664525u * (*state) + 1013904223u); + return *state; +} + +static inline int add_overflow_int64(int64_t a, int64_t b, int64_t *res) { + if ((b > 0 && a > INT64_MAX - b) || (b < 0 && a < INT64_MIN - b)) return 1; + *res = a + b; + return 0; +} + +// Kadane for int64 array with overflow detection; returns PyLong or NULL (on overflow -> signal via *overflowed) +static PyObject* kadane_int64(const int64_t *arr, Py_ssize_t n, int *overflowed) { + if (n <= 0) { + return PyFloat_FromDouble(-INFINITY); + } + int64_t meh = arr[0]; + int64_t msf = arr[0]; + for (Py_ssize_t i = 1; i < n; ++i) { + int64_t x = arr[i]; + if (meh > 0) { + int64_t tmp; + if (add_overflow_int64(meh, x, &tmp)) { *overflowed = 1; return NULL; } + meh = tmp; + } else { + meh = x; + } + if (meh > msf) msf = meh; + } + return PyLong_FromLongLong(msf); +} + +// Kadane for PyObject* integer array +static PyObject* kadane_big(PyObject **arr, Py_ssize_t n) { + if (n <= 0) { + return PyFloat_FromDouble(-INFINITY); + } + PyObject *meh = arr[0]; Py_INCREF(meh); + PyObject *msf = arr[0]; Py_INCREF(msf); + PyObject *zero = PyLong_FromLong(0); + if (!zero) { Py_DECREF(meh); Py_DECREF(msf); return NULL; } + + for (Py_ssize_t i = 1; i < n; ++i) { + int cmp = PyObject_RichCompareBool(meh, zero, Py_GT); + if (cmp < 0) { Py_DECREF(meh); Py_DECREF(msf); Py_DECREF(zero); return NULL; } + if (cmp == 1) { + PyObject *t = PyNumber_Add(meh, arr[i]); + if (!t) { Py_DECREF(meh); Py_DECREF(msf); Py_DECREF(zero); return NULL; } + Py_DECREF(meh); + meh = t; + } else { + Py_DECREF(meh); + meh = arr[i]; Py_INCREF(meh); + } + int cmp2 = PyObject_RichCompareBool(meh, msf, Py_GT); + if (cmp2 < 0) { Py_DECREF(meh); Py_DECREF(msf); Py_DECREF(zero); return NULL; } + if (cmp2 == 1) { + Py_DECREF(msf); + msf = meh; Py_INCREF(msf); + } + } + Py_DECREF(meh); + Py_DECREF(zero); + return msf; // new reference +} + +// Generate int64 array fast path; returns 0 on success +static int gen_array_int64(Py_ssize_t n, uint32_t seed, int64_t min_v, int64_t max_v, int64_t *out) { + uint32_t state = seed; + uint64_t umax = (uint64_t)max_v; + uint64_t umin = (uint64_t)min_v; + uint64_t range = (umax - umin) + 1ULL; // max>=min guaranteed by caller + for (Py_ssize_t i = 0; i < n; ++i) { + state = lcg_next(&state); + uint32_t r32 = state; + uint64_t r = (range > 0x100000000ULL) ? (uint64_t)r32 : ((uint64_t)r32 % range); + int64_t val = (int64_t)(min_v + (int64_t)r); + out[i] = val; + } + return 0; +} + +// Generate PyObject* int array general path using Python arithmetic +static PyObject** gen_array_big(Py_ssize_t n, uint32_t seed, PyObject *min_val, PyObject *max_val) { + PyObject **arr = (PyObject**)PyMem_Malloc((n > 0 ? n : 1) * sizeof(PyObject*)); + if (!arr) { + PyErr_NoMemory(); + return NULL; + } + PyObject *one = PyLong_FromLong(1); + if (!one) { PyMem_Free(arr); return NULL; } + PyObject *diff = PyNumber_Subtract(max_val, min_val); + if (!diff) { Py_DECREF(one); PyMem_Free(arr); return NULL; } + PyObject *range_obj = PyNumber_Add(diff, one); + Py_DECREF(diff); + Py_DECREF(one); + if (!range_obj) { PyMem_Free(arr); return NULL; } + + uint32_t state = seed; + for (Py_ssize_t i = 0; i < n; ++i) { + state = lcg_next(&state); + PyObject *v = PyLong_FromUnsignedLong((unsigned long)state); + if (!v) { + Py_DECREF(range_obj); + for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr[k]); + PyMem_Free(arr); + return NULL; + } + PyObject *r = PyNumber_Remainder(v, range_obj); + Py_DECREF(v); + if (!r) { + Py_DECREF(range_obj); + for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr[k]); + PyMem_Free(arr); + return NULL; + } + PyObject *val = PyNumber_Add(r, min_val); + Py_DECREF(r); + if (!val) { + Py_DECREF(range_obj); + for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr[k]); + PyMem_Free(arr); + return NULL; + } + arr[i] = val; + } + Py_DECREF(range_obj); + return arr; +} + +static PyObject* max_subarray_sum_internal(Py_ssize_t n, uint32_t seed, PyObject *min_val, PyObject *max_val) { + if (n <= 0) { + return PyFloat_FromDouble(-INFINITY); + } + + if (PyLong_Check(min_val) && PyLong_Check(max_val)) { + int overflow1 = 0, overflow2 = 0; + long long min64 = PyLong_AsLongLongAndOverflow(min_val, &overflow1); + if (overflow1) goto BIGINT_PATH; + long long max64 = PyLong_AsLongLongAndOverflow(max_val, &overflow2); + if (overflow2) goto BIGINT_PATH; + if (max64 >= min64) { + int64_t *arr = (int64_t*)PyMem_Malloc((size_t)n * sizeof(int64_t)); + if (!arr) { PyErr_NoMemory(); return NULL; } + if (gen_array_int64(n, seed, (int64_t)min64, (int64_t)max64, arr) != 0) { + PyMem_Free(arr); + return NULL; + } + int overflowed = 0; + PyObject *res = kadane_int64(arr, n, &overflowed); + if (!res && overflowed) { + // fallback to big-int Kadane + PyObject **arr_obj = (PyObject**)PyMem_Malloc((size_t)n * sizeof(PyObject*)); + if (!arr_obj) { PyMem_Free(arr); PyErr_NoMemory(); return NULL; } + for (Py_ssize_t i = 0; i < n; ++i) { + arr_obj[i] = PyLong_FromLongLong(arr[i]); + if (!arr_obj[i]) { + for (Py_ssize_t k = 0; k < i; ++k) Py_DECREF(arr_obj[k]); + PyMem_Free(arr_obj); + PyMem_Free(arr); + return NULL; + } + } + PyObject *bires = kadane_big(arr_obj, n); + for (Py_ssize_t i = 0; i < n; ++i) Py_DECREF(arr_obj[i]); + PyMem_Free(arr_obj); + PyMem_Free(arr); + return bires; + } + PyMem_Free(arr); + return res; + } + } +BIGINT_PATH: ; + PyObject **arr_obj = gen_array_big(n, seed, min_val, max_val); + if (!arr_obj) return NULL; + PyObject *res = kadane_big(arr_obj, n); + for (Py_ssize_t i = 0; i < n; ++i) Py_DECREF(arr_obj[i]); + PyMem_Free(arr_obj); + return res; +} + +static PyObject* py_max_subarray_sum(PyObject *self, PyObject *args) { + Py_ssize_t n; + PyObject *seed_obj, *min_val, *max_val; + if (!PyArg_ParseTuple(args, "nOOO", &n, &seed_obj, &min_val, &max_val)) return NULL; + if (n < 0) n = 0; + uint32_t seed = (uint32_t)(PyLong_AsUnsignedLongLongMask(seed_obj) & 0xFFFFFFFFULL); + if (PyErr_Occurred()) return NULL; + return max_subarray_sum_internal(n, seed, min_val, max_val); +} + +static PyObject* py_total_max_subarray_sum(PyObject *self, PyObject *args) { + Py_ssize_t n; + PyObject *init_seed_obj, *min_val, *max_val; + if (!PyArg_ParseTuple(args, "nOOO", &n, &init_seed_obj, &min_val, &max_val)) return NULL; + if (n < 0) n = 0; + uint32_t state = (uint32_t)(PyLong_AsUnsignedLongLongMask(init_seed_obj) & 0xFFFFFFFFULL); + if (PyErr_Occurred()) return NULL; + + PyObject *total = PyLong_FromLong(0); + if (!total) return NULL; + + for (int i = 0; i < 20; ++i) { + uint32_t seed = lcg_next(&state); + PyObject *part = max_subarray_sum_internal(n, seed, min_val, max_val); + if (!part) { Py_DECREF(total); return NULL; } + PyObject *new_total = PyNumber_Add(total, part); + Py_DECREF(part); + if (!new_total) { Py_DECREF(total); return NULL; } + Py_DECREF(total); + total = new_total; + } + return total; +} + +static PyMethodDef module_methods[] = { + {"max_subarray_sum", (PyCFunction)py_max_subarray_sum, METH_VARARGS, "Compute maximum subarray sum using LCG-generated array."}, + {"total_max_subarray_sum", (PyCFunction)py_total_max_subarray_sum, METH_VARARGS, "Compute total of maximum subarray sums over 20 LCG seeds."}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "python_hard", + NULL, + -1, + module_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC PyInit_python_hard(void) { + return PyModule_Create(&moduledef); +} diff --git a/week4/community-contributions/c_extension_generator/setup_calculate_pi.py b/week4/community-contributions/c_extension_generator/setup_calculate_pi.py new file mode 100644 index 0000000..ecb4067 --- /dev/null +++ b/week4/community-contributions/c_extension_generator/setup_calculate_pi.py @@ -0,0 +1,25 @@ +from setuptools import setup, Extension +import sys +import os + +extra_compile_args = [] +extra_link_args = [] + +if os.name == 'nt': + extra_compile_args.extend(['/O2', '/fp:precise']) +else: + extra_compile_args.extend(['-O3', '-fno-strict-aliasing']) + +module = Extension( + 'calculate_pi', + sources=['calculate_pi.c'], + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, +) + +setup( + name='calculate_pi', + version='1.0.0', + description='High-performance C extension for computing pi via the Leibniz series', + ext_modules=[module], +) diff --git a/week4/community-contributions/c_extension_generator/setup_python_hard.py b/week4/community-contributions/c_extension_generator/setup_python_hard.py new file mode 100644 index 0000000..20d3d3f --- /dev/null +++ b/week4/community-contributions/c_extension_generator/setup_python_hard.py @@ -0,0 +1,25 @@ +from setuptools import setup, Extension +import sys + +extra_compile_args = [] +extra_link_args = [] +if sys.platform == 'win32': + extra_compile_args = ['/O2', '/Ot', '/GL', '/fp:fast'] + extra_link_args = ['/LTCG'] +else: + extra_compile_args = ['-O3', '-march=native'] + +module = Extension( + name='python_hard', + sources=['python_hard.c'], + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, + language='c' +) + +setup( + name='python_hard', + version='1.0.0', + description='High-performance C extension reimplementation', + ext_modules=[module] +) diff --git a/week4/community-contributions/c_extension_generator/setup_zz_my_module.py b/week4/community-contributions/c_extension_generator/setup_zz_my_module.py new file mode 100644 index 0000000..bb0d27b --- /dev/null +++ b/week4/community-contributions/c_extension_generator/setup_zz_my_module.py @@ -0,0 +1,14 @@ + +from setuptools import setup, Extension + +module = Extension( + 'zz_my_module', + sources=['zz_my_module.c'], +) + +setup( + name='zz_my_module', + version='1.0', + description='This is a custom C extension module.', + ext_modules=[module] +) diff --git a/week4/community-contributions/c_extension_generator/usage_example_calculate_pi.py b/week4/community-contributions/c_extension_generator/usage_example_calculate_pi.py new file mode 100644 index 0000000..c3cc418 --- /dev/null +++ b/week4/community-contributions/c_extension_generator/usage_example_calculate_pi.py @@ -0,0 +1,38 @@ +# Build first: python setup.py build_ext --inplace +import time +import math +import calculate_pi + +# Original Python implementation +def py_leibniz_pi(iterations): + result = 1.0 + for i in range(1, iterations + 1): + j = i * 4 - 1 + result -= (1 / j) + j = i * 4 + 1 + result += (1 / j) + return result * 4 + +iters = 5_000_000 + +# Warm-up +calculate_pi.leibniz_pi(10) +py_leibniz_pi(10) + +start = time.perf_counter() +res_c = calculate_pi.leibniz_pi(iters) +end = time.perf_counter() +ctime = end - start + +start = time.perf_counter() +res_py = py_leibniz_pi(iters) +end = time.perf_counter() +pytime = end - start + +print(f"Iterations: {iters}") +print(f"C extension result: {res_c}") +print(f"Python result: {res_py}") +print(f"Absolute difference: {abs(res_c - res_py)}") +print(f"C extension time: {ctime:.6f} s") +print(f"Python time: {pytime:.6f} s") +print(f"Speedup: {pytime/ctime if ctime > 0 else float('inf'):.2f}x") diff --git a/week4/community-contributions/c_extension_generator/usage_example_python_hard.py b/week4/community-contributions/c_extension_generator/usage_example_python_hard.py new file mode 100644 index 0000000..552b0b5 --- /dev/null +++ b/week4/community-contributions/c_extension_generator/usage_example_python_hard.py @@ -0,0 +1,69 @@ +import time + +# Original Python code + +def lcg(seed, a=1664525, c=1013904223, m=2**32): + value = seed + while True: + value = (a * value + c) % m + yield value + +def max_subarray_sum_py(n, seed, min_val, max_val): + lcg_gen = lcg(seed) + random_numbers = [next(lcg_gen) % (max_val - min_val + 1) + min_val for _ in range(n)] + max_sum = float('-inf') + for i in range(n): + current_sum = 0 + for j in range(i, n): + current_sum += random_numbers[j] + if current_sum > max_sum: + max_sum = current_sum + return max_sum + +def total_max_subarray_sum_py(n, initial_seed, min_val, max_val): + total_sum = 0 + lcg_gen = lcg(initial_seed) + for _ in range(20): + seed = next(lcg_gen) + total_sum += max_subarray_sum_py(n, seed, min_val, max_val) + return total_sum + +# Build and import extension (after running: python setup.py build && install or develop) +import python_hard as ext + +# Example parameters +n = 600 +initial_seed = 12345678901234567890 +min_val = -1000 +max_val = 1000 + +# Time Python +t0 = time.perf_counter() +py_res1 = max_subarray_sum_py(n, (initial_seed * 1664525 + 1013904223) % (2**32), min_val, max_val) +t1 = time.perf_counter() +py_time1 = t1 - t0 + +# Time C extension +t0 = time.perf_counter() +ext_res1 = ext.max_subarray_sum(n, (initial_seed * 1664525 + 1013904223) % (2**32), min_val, max_val) +t1 = time.perf_counter() +ext_time1 = t1 - t0 + +print('max_subarray_sum equality:', py_res1 == ext_res1) +print('Python time:', py_time1) +print('C ext time:', ext_time1) + +# Total over 20 seeds +t0 = time.perf_counter() +py_res2 = total_max_subarray_sum_py(n, initial_seed, min_val, max_val) +t1 = time.perf_counter() +py_time2 = t1 - t0 + +t0 = time.perf_counter() +ext_res2 = ext.total_max_subarray_sum(n, initial_seed, min_val, max_val) +t1 = time.perf_counter() +ext_time2 = t1 - t0 + +print('total_max_subarray_sum equality:', py_res2 == ext_res2) +print('Python total time:', py_time2) +print('C ext total time:', ext_time2) diff --git a/week4/community-contributions/c_extension_generator/usage_example_zz_my_module.py b/week4/community-contributions/c_extension_generator/usage_example_zz_my_module.py new file mode 100644 index 0000000..6404cd3 --- /dev/null +++ b/week4/community-contributions/c_extension_generator/usage_example_zz_my_module.py @@ -0,0 +1,16 @@ + +import time +import zz_my_module + +def python_hello_world(): + print("Hello, World!") + +start = time.time() +python_hello_world() +end = time.time() +print(f"Python function execution time: {end - start:.6f} seconds") + +start = time.time() +zz_my_module.hello_world() +end = time.time() +print(f"C extension execution time: {end - start:.6f} seconds") diff --git a/week4/community-contributions/c_extension_generator/zz_my_module.c b/week4/community-contributions/c_extension_generator/zz_my_module.c new file mode 100644 index 0000000..2593233 --- /dev/null +++ b/week4/community-contributions/c_extension_generator/zz_my_module.c @@ -0,0 +1,28 @@ + +#include + +// Function to be called from Python +static PyObject* zz_hello_world(PyObject* self, PyObject* args) { + printf("Hello, World!\n"); + Py_RETURN_NONE; +} + +// Method definition structure +static PyMethodDef zz_my_methods[] = { + {"hello_world", zz_hello_world, METH_VARARGS, "Print 'Hello, World!'"}, + {NULL, NULL, 0, NULL} // Sentinel +}; + +// Module definition +static struct PyModuleDef zz_my_module = { + PyModuleDef_HEAD_INIT, + "zz_my_module", + "Extension module that prints Hello, World!", + -1, + zz_my_methods +}; + +// Module initialization function +PyMODINIT_FUNC PyInit_zz_my_module(void) { + return PyModule_Create(&zz_my_module); +}