From ccfc64a811a64a072b7f741f0083dcce5fe3d0db Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Mon, 29 Jul 2024 06:43:51 +0000 Subject: [PATCH 01/43] initial commit, adding Float128 dtype --- quaddtype2/README.md | 0 quaddtype2/pyproject.toml | 25 +++++++ quaddtype2/quaddtype/__init__.py | 4 ++ quaddtype2/quaddtype/scalar.py | 11 +++ quaddtype2/quaddtype/src/dtype.c | 87 +++++++++++++++++++++++ quaddtype2/quaddtype/src/dtype.h | 21 ++++++ quaddtype2/quaddtype/src/quaddtype_main.c | 57 +++++++++++++++ 7 files changed, 205 insertions(+) create mode 100644 quaddtype2/README.md create mode 100644 quaddtype2/pyproject.toml create mode 100644 quaddtype2/quaddtype/__init__.py create mode 100644 quaddtype2/quaddtype/scalar.py create mode 100644 quaddtype2/quaddtype/src/dtype.c create mode 100644 quaddtype2/quaddtype/src/dtype.h create mode 100644 quaddtype2/quaddtype/src/quaddtype_main.c diff --git a/quaddtype2/README.md b/quaddtype2/README.md new file mode 100644 index 00000000..e69de29b diff --git a/quaddtype2/pyproject.toml b/quaddtype2/pyproject.toml new file mode 100644 index 00000000..68b61e7f --- /dev/null +++ b/quaddtype2/pyproject.toml @@ -0,0 +1,25 @@ +[build-system] +requires = [ + "meson>=1.3.2", + "meson-python", + "patchelf", + "wheel", + "numpy" +] +build-backend = "mesonpy" + +[project] +name = "quaddtype" +description = "Quad (128-bit) float dtype for numpy" +version = "0.0.1" +readme = 'README.md' +author = "Swayam Singh" +requires-python = ">=3.9.0" +dependencies = [ + "numpy" +] + +[project.optional-dependencies] +test = [ + "pytest", +] diff --git a/quaddtype2/quaddtype/__init__.py b/quaddtype2/quaddtype/__init__.py new file mode 100644 index 00000000..7f979266 --- /dev/null +++ b/quaddtype2/quaddtype/__init__.py @@ -0,0 +1,4 @@ +from .scalar import QuadScalar +from ._quaddtype_main import QuadDType + +__all__ = ["QuadScalar", "QuadDType"] \ No newline at end of file diff --git a/quaddtype2/quaddtype/scalar.py b/quaddtype2/quaddtype/scalar.py new file mode 100644 index 00000000..493bf40a --- /dev/null +++ b/quaddtype2/quaddtype/scalar.py @@ -0,0 +1,11 @@ +"""Quad scalar floating point type for numpy.""" + + +class QuadScalar: + """Quad scalar floating point type.""" + + def __init__(self, value): + self.value = value + + def __repr__(self): + return f"{self.value}" \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/dtype.c b/quaddtype2/quaddtype/src/dtype.c new file mode 100644 index 00000000..d045d665 --- /dev/null +++ b/quaddtype2/quaddtype/src/dtype.c @@ -0,0 +1,87 @@ +#include + +#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API +#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NPY_TARGET_VERSION NPY_2_0_API_VERSION +#define NO_IMPORT_ARRAY +#define NO_IMPORT_UFUNC +#include "numpy/ndarraytypes.h" +#include "numpy/arrayobject.h" +#include "numpy/dtype_api.h" + +#include "dtype.h" + + +PyTypeObject *QuadScalar_Type = NULL; + +QuadDTypeObject *new_quaddtype_instance(void) +{ + QuadDTypeObject *new = + (QuadDTypeObject *)PyArrayDescr_Type.tp_new((PyTypeObject *)&QuadDType, NULL, NULL); + if (new == NULL) { + return NULL; + } + + new->base.elsize = sizeof(__float128); + new->base.alignment = _Alignof(__float128); + return new; +} + +static PyObject *quaddtype_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwargs) +{ + return (PyObject *)new_quaddtype_instance(); +} + +static void quaddtype_dealloc(QuadDTypeObject *self) +{ + PyArrayDescr_Type.tp_dealloc((PyObject *)self); +} + +static PyObject *quaddtype_repr(QuadDTypeObject *self) +{ + PyObject *res = PyUnicode_FromString("This is a quad (128-bit float) dtype."); + return res; +} + +PyArray_DTypeMeta QuadDType = { + {{ + PyVarObject_HEAD_INIT(NULL, 0).tp_name = "quaddtype.QuadDType", + .tp_basicsize = sizeof(QuadDTypeObject), + .tp_new = quaddtype_new, + .tp_dealloc = (destructor)quaddtype_dealloc, + .tp_repr = (reprfunc)quaddtype_repr, + .tp_str = (reprfunc)quaddtype_repr, + }}, +}; + +int init_quad_dtype(void) +{ + PyArrayMethod_Spec *casts[] = { + NULL, + }; + + PyArrayDTypeMeta_Spec QuadDType_DTypeSpec = { + .flags = NPY_DT_NUMERIC, + .casts = casts, + .typeobj = QuadScalar_Type, + .slots = NULL, + }; + + ((PyObject *)&QuadDType)->ob_type = &PyArrayDTypeMeta_Type; + ((PyTypeObject *)&QuadDType)->tp_base = &PyArrayDescr_Type; + if (PyType_Ready((PyTypeObject *)&QuadDType) < 0) { + return -1; + } + + if (PyArrayInitDTypeMeta_FromSpec(&QuadDType, &QuadDType_DTypeSpec) < 0) { + return -1; + } + + QuadDType.singleton = PyArray_GetDefaultDescr(&QuadDType); + return 0; +} + + + + diff --git a/quaddtype2/quaddtype/src/dtype.h b/quaddtype2/quaddtype/src/dtype.h new file mode 100644 index 00000000..bb5fda4f --- /dev/null +++ b/quaddtype2/quaddtype/src/dtype.h @@ -0,0 +1,21 @@ +#ifndef _NPY_DTYPE_H +#define _NPY_DTYPE_H + +#include +#include +#include + +typedef struct +{ + PyArray_Descr base; + +} QuadDTypeObject; + +extern PyArray_DTypeMeta QuadDType; +extern PyTypeObject *QuadScalar_Type; + +QuadDTypeObject * new_quaddtype_instance(void); + +int init_quad_dtype(void); + +#endif \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/quaddtype_main.c b/quaddtype2/quaddtype/src/quaddtype_main.c new file mode 100644 index 00000000..a5695e9c --- /dev/null +++ b/quaddtype2/quaddtype/src/quaddtype_main.c @@ -0,0 +1,57 @@ +#include + +#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API +#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#include "numpy/arrayobject.h" +#include "numpy/dtype_api.h" + +#include "dtype.h" + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "quaddtype_main", + .m_doc = "Quad (128-bit) floating point Data Type for Numpy", + .m_size = -1, +}; + +PyMODINIT_FUNC +PyInit__quaddtype_main(void) +{ + import_array(); + + PyObject *m = PyModule_Create(&moduledef); + if (m == NULL) { + PyErr_SetString(PyExc_ImportError, "Unable to create the quaddtype_main module."); + return NULL; + } + + PyObject *mod = PyImport_ImportModule("quaddtype"); + if (mod == NULL) { + PyErr_SetString(PyExc_ImportError, "Unable to import the quaddtype module."); + goto error; + } + QuadScalar_Type = (PyTypeObject *)PyObject_GetAttrString(mod, "QuadScalar"); + Py_DECREF(mod); + if (QuadScalar_Type == NULL) { + PyErr_SetString(PyExc_AttributeError, + "Unable to find QuadScalar attribute in the " + "quaddtype_main module."); + goto error; + } + if (init_quad_dtype() < 0) { + PyErr_SetString(PyExc_AttributeError, "QuadDType failed to initialize."); + goto error; + } + if (PyModule_AddObject(m, "QuadDType", (PyObject *)&QuadDType) < 0) { + PyErr_SetString(PyExc_TypeError, "Failed to add QuadDType to the quaddtype_main module."); + goto error; + } + + + return m; + +error: + Py_DECREF(m); + return NULL; +} \ No newline at end of file From d9005a92a1b9ab2b2c07dce2fcdba43b8c0b0379 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Mon, 29 Jul 2024 18:48:01 +0000 Subject: [PATCH 02/43] sleef based quadprecision --- quaddtype2/meson.build | 45 +++++++++++++++++++ quaddtype2/quaddtype/__init__.py | 2 +- .../quaddtype/{scalar.py => quadscalar.py} | 0 quaddtype2/quaddtype/src/dtype.c | 8 ++-- quaddtype2/reinstall.sh | 14 ++++++ 5 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 quaddtype2/meson.build rename quaddtype2/quaddtype/{scalar.py => quadscalar.py} (100%) create mode 100755 quaddtype2/reinstall.sh diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build new file mode 100644 index 00000000..6bd3e87d --- /dev/null +++ b/quaddtype2/meson.build @@ -0,0 +1,45 @@ +project( + 'quaddtype2', + 'c', +) + +py_mod = import('python') +py = py_mod.find_installation('/home/rootacess/numpy-user-dtypes/quaddtype2/.venv/bin/python3') + +# incdir_numpy = run_command(py, +# [ +# '-c', +# 'import numpy; print(numpy.get_include())' +# ], +# check: true +# ).stdout().strip() + +includes = include_directories( + [ + '.venv/lib/python3.12/site-packages/numpy/_core/include', + 'quaddtype/src' + ] +) + +srcs = [ + 'quaddtype/src/dtype.c', + 'quaddtype/src/quaddtype_main.c', +] + +py.install_sources( + [ + 'quaddtype/__init__.py', + 'quaddtype/quadscalar.py' + ], + subdir: 'quaddtype', + pure: false +) + +py.extension_module( + '_quaddtype_main', + srcs, + c_args: ['-g', '-O0' ,'-lsleef', '-lsleefquad'], + install: true, + subdir: 'quaddtype', + include_directories: includes +) diff --git a/quaddtype2/quaddtype/__init__.py b/quaddtype2/quaddtype/__init__.py index 7f979266..95686115 100644 --- a/quaddtype2/quaddtype/__init__.py +++ b/quaddtype2/quaddtype/__init__.py @@ -1,4 +1,4 @@ -from .scalar import QuadScalar +from .quadscalar import QuadScalar from ._quaddtype_main import QuadDType __all__ = ["QuadScalar", "QuadDType"] \ No newline at end of file diff --git a/quaddtype2/quaddtype/scalar.py b/quaddtype2/quaddtype/quadscalar.py similarity index 100% rename from quaddtype2/quaddtype/scalar.py rename to quaddtype2/quaddtype/quadscalar.py diff --git a/quaddtype2/quaddtype/src/dtype.c b/quaddtype2/quaddtype/src/dtype.c index d045d665..6d48b701 100644 --- a/quaddtype2/quaddtype/src/dtype.c +++ b/quaddtype2/quaddtype/src/dtype.c @@ -1,4 +1,6 @@ #include +#include +#include #define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API #define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API @@ -23,8 +25,8 @@ QuadDTypeObject *new_quaddtype_instance(void) return NULL; } - new->base.elsize = sizeof(__float128); - new->base.alignment = _Alignof(__float128); + new->base.elsize = sizeof(Sleef_quad); + new->base.alignment = _Alignof(Sleef_quad); return new; } @@ -40,7 +42,7 @@ static void quaddtype_dealloc(QuadDTypeObject *self) static PyObject *quaddtype_repr(QuadDTypeObject *self) { - PyObject *res = PyUnicode_FromString("This is a quad (128-bit float) dtype."); + PyObject *res = PyUnicode_FromString("This is a Sleef based quad (128-bit float) dtype."); return res; } diff --git a/quaddtype2/reinstall.sh b/quaddtype2/reinstall.sh new file mode 100755 index 00000000..52125765 --- /dev/null +++ b/quaddtype2/reinstall.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -xeuo pipefail +IFS=$'\n\t' + +if [ -d "build/" ] +then + rm -r build +fi + +#meson setup build -Db_sanitize=address,undefined +meson setup build +python -m pip uninstall -y quaddtype +python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" +#python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' From 4702764917bf9294a6854216def16aab870fa510 Mon Sep 17 00:00:00 2001 From: Swayam <74960567+SwayamInSync@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:08:34 +0530 Subject: [PATCH 03/43] fixing numpy absolute include path --- quaddtype2/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build index 6bd3e87d..f097c1b9 100644 --- a/quaddtype2/meson.build +++ b/quaddtype2/meson.build @@ -9,7 +9,7 @@ py = py_mod.find_installation('/home/rootacess/numpy-user-dtypes/quaddtype2/.ven # incdir_numpy = run_command(py, # [ # '-c', -# 'import numpy; print(numpy.get_include())' +# 'import numpy; import os; print(os.path.relpath(numpy.get_include()))' # ], # check: true # ).stdout().strip() From 6ec99a5aaae8193ed3373b2b9e2917dcf118e904 Mon Sep 17 00:00:00 2001 From: Swayam <74960567+SwayamInSync@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:09:32 +0530 Subject: [PATCH 04/43] fixing numpy absolute include path --- quaddtype2/meson.build | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build index f097c1b9..77e8a0b0 100644 --- a/quaddtype2/meson.build +++ b/quaddtype2/meson.build @@ -6,17 +6,17 @@ project( py_mod = import('python') py = py_mod.find_installation('/home/rootacess/numpy-user-dtypes/quaddtype2/.venv/bin/python3') -# incdir_numpy = run_command(py, -# [ -# '-c', -# 'import numpy; import os; print(os.path.relpath(numpy.get_include()))' -# ], -# check: true -# ).stdout().strip() +incdir_numpy = run_command(py, + [ + '-c', + 'import numpy; import os; print(os.path.relpath(numpy.get_include()))' + ], + check: true +).stdout().strip() includes = include_directories( [ - '.venv/lib/python3.12/site-packages/numpy/_core/include', + incdir_numpy, 'quaddtype/src' ] ) From a8b2599a937830dd4ddf59ae3f71dc10e761bce0 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 1 Aug 2024 18:47:10 +0530 Subject: [PATCH 05/43] adding quad precision support with sleef --- quaddtype2/meson.build | 50 +++--- quaddtype2/quaddtype/__init__.py | 5 +- quaddtype2/quaddtype/quadscalar.py | 11 -- quaddtype2/quaddtype/src/dtype.c | 203 +++++++++++++++++----- quaddtype2/quaddtype/src/dtype.h | 16 +- quaddtype2/quaddtype/src/quaddtype_main.c | 39 ++--- quaddtype2/quaddtype/src/scalar.c | 105 +++++++++++ quaddtype2/quaddtype/src/scalar.h | 27 +++ quaddtype2/reinstall.sh | 3 +- 9 files changed, 342 insertions(+), 117 deletions(-) delete mode 100644 quaddtype2/quaddtype/quadscalar.py create mode 100644 quaddtype2/quaddtype/src/scalar.c create mode 100644 quaddtype2/quaddtype/src/scalar.h diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build index 77e8a0b0..56fc5214 100644 --- a/quaddtype2/meson.build +++ b/quaddtype2/meson.build @@ -1,11 +1,12 @@ -project( - 'quaddtype2', - 'c', -) +project('quaddtype2', 'c') py_mod = import('python') py = py_mod.find_installation('/home/rootacess/numpy-user-dtypes/quaddtype2/.venv/bin/python3') +c = meson.get_compiler('c') +sleef = c.find_library('sleef') +sleefquad_math =c.find_library('sleefquad') + incdir_numpy = run_command(py, [ '-c', @@ -15,31 +16,32 @@ incdir_numpy = run_command(py, ).stdout().strip() includes = include_directories( - [ - incdir_numpy, - 'quaddtype/src' - ] + [ + incdir_numpy, + 'quaddtype/src' + ] ) srcs = [ - 'quaddtype/src/dtype.c', - 'quaddtype/src/quaddtype_main.c', + 'quaddtype/src/scalar.h', + 'quaddtype/src/scalar.c', + 'quaddtype/src/dtype.h', + 'quaddtype/src/dtype.c', + 'quaddtype/src/quaddtype_main.c' ] py.install_sources( - [ - 'quaddtype/__init__.py', - 'quaddtype/quadscalar.py' - ], - subdir: 'quaddtype', - pure: false + [ + 'quaddtype/__init__.py', + ], + subdir: 'quaddtype', + pure: false ) -py.extension_module( - '_quaddtype_main', - srcs, - c_args: ['-g', '-O0' ,'-lsleef', '-lsleefquad'], - install: true, - subdir: 'quaddtype', - include_directories: includes -) +py.extension_module('_quaddtype_main', +srcs, +c_args: ['-g', '-O0', '-lsleef', '-lsleefquad'], +install: true, +subdir: 'quaddtype', +include_directories: includes +) \ No newline at end of file diff --git a/quaddtype2/quaddtype/__init__.py b/quaddtype2/quaddtype/__init__.py index 95686115..9f246f73 100644 --- a/quaddtype2/quaddtype/__init__.py +++ b/quaddtype2/quaddtype/__init__.py @@ -1,4 +1 @@ -from .quadscalar import QuadScalar -from ._quaddtype_main import QuadDType - -__all__ = ["QuadScalar", "QuadDType"] \ No newline at end of file +from ._quaddtype_main import QuadPrecDType, QuadPrecision \ No newline at end of file diff --git a/quaddtype2/quaddtype/quadscalar.py b/quaddtype2/quaddtype/quadscalar.py deleted file mode 100644 index 493bf40a..00000000 --- a/quaddtype2/quaddtype/quadscalar.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Quad scalar floating point type for numpy.""" - - -class QuadScalar: - """Quad scalar floating point type.""" - - def __init__(self, value): - self.value = value - - def __repr__(self): - return f"{self.value}" \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/dtype.c b/quaddtype2/quaddtype/src/dtype.c index 6d48b701..d06d27da 100644 --- a/quaddtype2/quaddtype/src/dtype.c +++ b/quaddtype2/quaddtype/src/dtype.c @@ -1,89 +1,202 @@ #include -#include -#include +#include +#include -#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API #define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION #define NPY_TARGET_VERSION NPY_2_0_API_VERSION #define NO_IMPORT_ARRAY #define NO_IMPORT_UFUNC -#include "numpy/ndarraytypes.h" #include "numpy/arrayobject.h" +#include "numpy/ndarraytypes.h" #include "numpy/dtype_api.h" +#include "numpy/_public_dtype_api_table.h" // not included in dtype_api.h +#include "scalar.h" #include "dtype.h" +static inline int quad_load(Sleef_quad *x, char *data_ptr) +{ + if (data_ptr == NULL || x == NULL) + { + PyErr_SetString(PyExc_ValueError, "Invalid memory location"); + return -1; + } + *x = *(Sleef_quad *)data_ptr; + return 0; +} -PyTypeObject *QuadScalar_Type = NULL; +static inline int quad_store(char *data_ptr, Sleef_quad x) +{ + if (data_ptr == NULL) + { + PyErr_SetString(PyExc_ValueError, "Invalid memory location"); + return -1; + } + *(Sleef_quad *)data_ptr = x; + return 0; +} -QuadDTypeObject *new_quaddtype_instance(void) +QuadPrecDTypeObject * new_quaddtype_instance(void) { - QuadDTypeObject *new = - (QuadDTypeObject *)PyArrayDescr_Type.tp_new((PyTypeObject *)&QuadDType, NULL, NULL); + QuadPrecDTypeObject *new = (QuadPrecDTypeObject *)PyArrayDescr_Type.tp_new((PyTypeObject *)&QuadPrecDType, NULL, NULL); if (new == NULL) { return NULL; } - new->base.elsize = sizeof(Sleef_quad); new->base.alignment = _Alignof(Sleef_quad); + new->base.flags |= NPY_NEEDS_INIT; // Indicates memory for this data-type must be initialized (set to 0) on creation. + return new; } -static PyObject *quaddtype_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwargs) +static QuadPrecDTypeObject * ensure_canonical(QuadPrecDTypeObject *self) { - return (PyObject *)new_quaddtype_instance(); + Py_INCREF(self); + return self; } -static void quaddtype_dealloc(QuadDTypeObject *self) +static QuadPrecDTypeObject * common_instance(QuadPrecDTypeObject *dtype1, QuadPrecDTypeObject *dtype2) { - PyArrayDescr_Type.tp_dealloc((PyObject *)self); + Py_INCREF(dtype1); + return dtype1; } -static PyObject *quaddtype_repr(QuadDTypeObject *self) + +static PyArray_DTypeMeta * common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) { - PyObject *res = PyUnicode_FromString("This is a Sleef based quad (128-bit float) dtype."); - return res; -} + // Promote integer and floating-point types to QuadPrecDType + if (other->type_num >= 0 && + (PyTypeNum_ISINTEGER(other->type_num) || + PyTypeNum_ISFLOAT(other->type_num))) { + Py_INCREF(cls); + return cls; + } + // Don't promote complex types + if (PyTypeNum_ISCOMPLEX(other->type_num)) { + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; + } -PyArray_DTypeMeta QuadDType = { - {{ - PyVarObject_HEAD_INIT(NULL, 0).tp_name = "quaddtype.QuadDType", - .tp_basicsize = sizeof(QuadDTypeObject), - .tp_new = quaddtype_new, - .tp_dealloc = (destructor)quaddtype_dealloc, - .tp_repr = (reprfunc)quaddtype_repr, - .tp_str = (reprfunc)quaddtype_repr, - }}, -}; + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; +} -int init_quad_dtype(void) +static PyArray_Descr * +quadprec_discover_descriptor_from_pyobject(PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) { - PyArrayMethod_Spec *casts[] = { - NULL, - }; - - PyArrayDTypeMeta_Spec QuadDType_DTypeSpec = { - .flags = NPY_DT_NUMERIC, - .casts = casts, - .typeobj = QuadScalar_Type, - .slots = NULL, - }; + if (Py_TYPE(obj) != &QuadPrecision_Type) + { + PyErr_SetString(PyExc_TypeError, "Can only store QuadPrecision in a QuadPrecDType array."); + return NULL; + } + return (PyArray_Descr *)new_quaddtype_instance(); +} - ((PyObject *)&QuadDType)->ob_type = &PyArrayDTypeMeta_Type; - ((PyTypeObject *)&QuadDType)->tp_base = &PyArrayDescr_Type; - if (PyType_Ready((PyTypeObject *)&QuadDType) < 0) { - return -1; +static int quadprec_setitem(QuadPrecDTypeObject *descr, PyObject *obj, char *dataptr) +{ + QuadPrecisionObject *value; + if (PyObject_TypeCheck(obj, &QuadPrecision_Type)) + { + Py_INCREF(obj); + value = (QuadPrecisionObject *)obj; + } + else + { + value = QuadPrecision_from_object(obj); + if (value == NULL) { + return -1; + } } - if (PyArrayInitDTypeMeta_FromSpec(&QuadDType, &QuadDType_DTypeSpec) < 0) { + if (quad_store(dataptr, value->quad.value) < 0) + { + Py_DECREF(value); return -1; } - QuadDType.singleton = PyArray_GetDefaultDescr(&QuadDType); + Py_DECREF(value); return 0; } +static PyObject * quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) +{ + QuadPrecisionObject *new = QuadPrecision_raw_new(); + if (!new) + { + return NULL; + } + if (quad_load(&new->quad.value, dataptr) < 0) + { + Py_DECREF(new); + return NULL; + } + return (PyObject *)new; +} + +static PyType_Slot QuadPrecDType_Slots[] = +{ + {NPY_DT_ensure_canonical, &ensure_canonical}, + {NPY_DT_common_instance, &common_instance}, + {NPY_DT_common_dtype, &common_dtype}, + {NPY_DT_discover_descr_from_pyobject, &quadprec_discover_descriptor_from_pyobject}, + {NPY_DT_setitem, &quadprec_setitem}, + {NPY_DT_getitem, &quadprec_getitem}, + {0, NULL} +}; + +static PyObject * QuadPrecDType_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwds) +{ + if (PyTuple_GET_SIZE(args) != 0 || (kwds != NULL && PyDict_Size(kwds) != 0)) { + PyErr_SetString(PyExc_TypeError, + "QuadPrecDType takes no arguments"); + return NULL; + } + return (PyObject *)new_quaddtype_instance(); +} +static PyObject * QuadPrecDType_repr(QuadPrecDTypeObject *self) +{ + return PyUnicode_FromString("QuadPrecDType()"); +} + +PyArray_DTypeMeta QuadPrecDType = { + {{ + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "QuadPrecDType.QuadPrecDType", + .tp_basicsize = sizeof(QuadPrecDTypeObject), + .tp_new = QuadPrecDType_new, + .tp_repr = (reprfunc)QuadPrecDType_repr, + .tp_str = (reprfunc)QuadPrecDType_repr, + }}, +}; + +int init_quadprec_dtype(void) +{ + PyArrayMethod_Spec **casts = NULL; // Initialize casts if needed + + PyArrayDTypeMeta_Spec QuadPrecDType_DTypeSpec = { + .flags = NPY_DT_NUMERIC, + .casts = casts, + .typeobj = &QuadPrecision_Type, + .slots = QuadPrecDType_Slots, + }; + + ((PyObject *)&QuadPrecDType)->ob_type = &PyArrayDTypeMeta_Type; + + ((PyTypeObject *)&QuadPrecDType)->tp_base = &PyArrayDescr_Type; + + if (PyType_Ready((PyTypeObject *)&QuadPrecDType) < 0) + { + return -1; + } + + if (PyArrayInitDTypeMeta_FromSpec(&QuadPrecDType, &QuadPrecDType_DTypeSpec) < 0) + { + return -1; + } + return 0; +} \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/dtype.h b/quaddtype2/quaddtype/src/dtype.h index bb5fda4f..e935063f 100644 --- a/quaddtype2/quaddtype/src/dtype.h +++ b/quaddtype2/quaddtype/src/dtype.h @@ -1,21 +1,23 @@ -#ifndef _NPY_DTYPE_H -#define _NPY_DTYPE_H +#ifndef _QUADDTYPE_DTYPE_H +#define _QUADDTYPE_DTYPE_H #include +#include #include #include +#include "scalar.h" + typedef struct { PyArray_Descr base; -} QuadDTypeObject; +} QuadPrecDTypeObject; -extern PyArray_DTypeMeta QuadDType; -extern PyTypeObject *QuadScalar_Type; +extern PyArray_DTypeMeta QuadPrecDType; -QuadDTypeObject * new_quaddtype_instance(void); +QuadPrecDTypeObject * new_quaddtype_instance(void); -int init_quad_dtype(void); +int init_quadprec_dtype(void); #endif \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/quaddtype_main.c b/quaddtype2/quaddtype/src/quaddtype_main.c index a5695e9c..ed0778e1 100644 --- a/quaddtype2/quaddtype/src/quaddtype_main.c +++ b/quaddtype2/quaddtype/src/quaddtype_main.c @@ -1,8 +1,10 @@ #include -#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API #define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NPY_TARGET_VERSION NPY_2_0_API_VERSION + #include "numpy/arrayobject.h" #include "numpy/dtype_api.h" @@ -10,7 +12,7 @@ static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - .m_name = "quaddtype_main", + .m_name = "_quaddtype_main", .m_doc = "Quad (128-bit) floating point Data Type for Numpy", .m_size = -1, }; @@ -19,37 +21,26 @@ PyMODINIT_FUNC PyInit__quaddtype_main(void) { import_array(); - PyObject *m = PyModule_Create(&moduledef); - if (m == NULL) { - PyErr_SetString(PyExc_ImportError, "Unable to create the quaddtype_main module."); + if (!m) + { return NULL; } - PyObject *mod = PyImport_ImportModule("quaddtype"); - if (mod == NULL) { - PyErr_SetString(PyExc_ImportError, "Unable to import the quaddtype module."); + if (init_quadprecision_scalar() < 0) goto error; - } - QuadScalar_Type = (PyTypeObject *)PyObject_GetAttrString(mod, "QuadScalar"); - Py_DECREF(mod); - if (QuadScalar_Type == NULL) { - PyErr_SetString(PyExc_AttributeError, - "Unable to find QuadScalar attribute in the " - "quaddtype_main module."); - goto error; - } - if (init_quad_dtype() < 0) { - PyErr_SetString(PyExc_AttributeError, "QuadDType failed to initialize."); + + if(PyModule_AddObject(m, "QuadPrecision", (PyObject *)&QuadPrecision_Type) < 0) goto error; - } - if (PyModule_AddObject(m, "QuadDType", (PyObject *)&QuadDType) < 0) { - PyErr_SetString(PyExc_TypeError, "Failed to add QuadDType to the quaddtype_main module."); + + if(init_quadprec_dtype() < 0) goto error; - } + if(PyModule_AddObject(m, "QuadPrecDType", (PyObject *)&QuadPrecDType) < 0) + goto error; return m; + error: Py_DECREF(m); diff --git a/quaddtype2/quaddtype/src/scalar.c b/quaddtype2/quaddtype/src/scalar.c new file mode 100644 index 00000000..b2fe5c95 --- /dev/null +++ b/quaddtype2/quaddtype/src/scalar.c @@ -0,0 +1,105 @@ +#include +#include +#include + +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NO_IMPORT_ARRAY + +#include "numpy/arrayobject.h" +#include "numpy/ndarraytypes.h" +#include "numpy/dtype_api.h" + +#include"scalar.h" + + +// static PyTypeObject QuadPrecision_Type; + + +QuadPrecisionObject * QuadPrecision_raw_new(void) +{ + QuadPrecisionObject * new = PyObject_New(QuadPrecisionObject, &QuadPrecision_Type); + if(!new) + return NULL; + new->quad.value = Sleef_cast_from_doubleq1(0.0); // initialize to 0 + return new; +} + +QuadPrecisionObject * QuadPrecision_from_object(PyObject * value) +{ + QuadPrecisionObject *self = QuadPrecision_raw_new(); + if(!self) + return NULL; + + if(PyFloat_Check(value)) + self->quad.value = Sleef_cast_from_doubleq1(PyFloat_AsDouble(value)); + + else if(PyUnicode_CheckExact(value)) + { + const char * s = PyUnicode_AsUTF8(value); + char *endptr = NULL; + self->quad.value = Sleef_strtoq(s, &endptr); + if (*endptr != '\0' || endptr == s) + { + PyErr_SetString(PyExc_ValueError, "Unable to parse string to QuadPrecision"); + Py_DECREF(self); + return NULL; + } + } + else + { + PyErr_SetString(PyExc_TypeError, "QuadPrecision value must be a float or string"); + Py_DECREF(self); + return NULL; + } + + return self; +} + +static PyObject * +QuadPrecision_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) +{ + PyObject *value; + + if (!PyArg_ParseTuple(args, "O", &value)) { + return NULL; + } + + return (PyObject *)QuadPrecision_from_object(value); +} + +static PyObject * QuadPrecision_str(QuadPrecisionObject * self) +{ + char buffer[128]; + Sleef_snprintf(buffer, sizeof(buffer), "%.*Qe", self->quad.value); + return PyUnicode_FromString(buffer); +} + +static PyObject * QuadPrecision_repr(QuadPrecisionObject* self) +{ + PyObject *str = QuadPrecision_str(self); + if (str == NULL) { + return NULL; + } + PyObject *res = PyUnicode_FromFormat("QuadPrecision('%S')", str); + Py_DECREF(str); + return res; +} + +PyTypeObject QuadPrecision_Type = +{ + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "QuadPrecType.QuadPrecision", + .tp_basicsize = sizeof(QuadPrecisionObject), + .tp_itemsize = 0, + .tp_new = QuadPrecision_new, + .tp_repr = (reprfunc)QuadPrecision_repr, + .tp_str = (reprfunc)QuadPrecision_str, + +}; + +int +init_quadprecision_scalar(void) +{ + return PyType_Ready(&QuadPrecision_Type); +} \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/scalar.h b/quaddtype2/quaddtype/src/scalar.h new file mode 100644 index 00000000..7ac39f31 --- /dev/null +++ b/quaddtype2/quaddtype/src/scalar.h @@ -0,0 +1,27 @@ +#ifndef _QUADDTYPE_SCALAR_H +#define _QUADDTYPE_SCALAR_H + +#include +#include + +typedef struct +{ + Sleef_quad value; +} quad_field; + +typedef struct +{ + PyObject_HEAD + quad_field quad; +} QuadPrecisionObject; + +extern PyTypeObject QuadPrecision_Type; + +QuadPrecisionObject * QuadPrecision_raw_new(void); + +QuadPrecisionObject * QuadPrecision_from_object(PyObject * value); + +int init_quadprecision_scalar(void); + + +#endif \ No newline at end of file diff --git a/quaddtype2/reinstall.sh b/quaddtype2/reinstall.sh index 52125765..f2fc885f 100755 --- a/quaddtype2/reinstall.sh +++ b/quaddtype2/reinstall.sh @@ -10,5 +10,4 @@ fi #meson setup build -Db_sanitize=address,undefined meson setup build python -m pip uninstall -y quaddtype -python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" -#python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' +python -m pip install . -v --no-build-isolation --global-option="build_ext" --global-option="-v" --global-option="--build-dir=build" --global-option="--debug" \ No newline at end of file From d6bdb99d554ceb789545ddc372e39eb76706193b Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 2 Aug 2024 13:59:41 +0530 Subject: [PATCH 06/43] fixing sleef linking issues in meson --- quaddtype2/meson.build | 11 +++++++---- quaddtype2/reinstall.sh | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build index 56fc5214..ecae41d0 100644 --- a/quaddtype2/meson.build +++ b/quaddtype2/meson.build @@ -1,11 +1,12 @@ project('quaddtype2', 'c') py_mod = import('python') -py = py_mod.find_installation('/home/rootacess/numpy-user-dtypes/quaddtype2/.venv/bin/python3') +py = py_mod.find_installation() c = meson.get_compiler('c') -sleef = c.find_library('sleef') -sleefquad_math =c.find_library('sleefquad') + +sleef_dep = c.find_library('sleef', dirs: ['/usr/local/lib']) # sleef installation directories +sleefquad_dep = c.find_library('sleefquad', dirs: ['/usr/local/lib']) incdir_numpy = run_command(py, [ @@ -18,7 +19,8 @@ incdir_numpy = run_command(py, includes = include_directories( [ incdir_numpy, - 'quaddtype/src' + '/usr/local/include', + 'quaddtype/src', ] ) @@ -41,6 +43,7 @@ py.install_sources( py.extension_module('_quaddtype_main', srcs, c_args: ['-g', '-O0', '-lsleef', '-lsleefquad'], +dependencies: [sleef_dep, sleefquad_dep], install: true, subdir: 'quaddtype', include_directories: includes diff --git a/quaddtype2/reinstall.sh b/quaddtype2/reinstall.sh index f2fc885f..3ed245d9 100755 --- a/quaddtype2/reinstall.sh +++ b/quaddtype2/reinstall.sh @@ -10,4 +10,5 @@ fi #meson setup build -Db_sanitize=address,undefined meson setup build python -m pip uninstall -y quaddtype -python -m pip install . -v --no-build-isolation --global-option="build_ext" --global-option="-v" --global-option="--build-dir=build" --global-option="--debug" \ No newline at end of file +python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" +#python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' \ No newline at end of file From 8ff6e0cc8a65218e08f11ed319bbe5c7f9f3bc1c Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 2 Aug 2024 16:01:49 +0530 Subject: [PATCH 07/43] adding NPY_SAME_KIND_CASTING --- quaddtype2/meson.build | 4 +- quaddtype2/quaddtype/src/casts.cpp | 114 +++++++++++++++++++++++++++++ quaddtype2/quaddtype/src/casts.h | 22 ++++++ quaddtype2/quaddtype/src/dtype.c | 12 ++- 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 quaddtype2/quaddtype/src/casts.cpp create mode 100644 quaddtype2/quaddtype/src/casts.h diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build index ecae41d0..531570eb 100644 --- a/quaddtype2/meson.build +++ b/quaddtype2/meson.build @@ -1,4 +1,4 @@ -project('quaddtype2', 'c') +project('quaddtype2', 'c', 'cpp') py_mod = import('python') py = py_mod.find_installation() @@ -25,6 +25,8 @@ includes = include_directories( ) srcs = [ + 'quaddtype/src/casts.h', + 'quaddtype/src/casts.cpp', 'quaddtype/src/scalar.h', 'quaddtype/src/scalar.c', 'quaddtype/src/dtype.h', diff --git a/quaddtype2/quaddtype/src/casts.cpp b/quaddtype2/quaddtype/src/casts.cpp new file mode 100644 index 00000000..24ebf4b7 --- /dev/null +++ b/quaddtype2/quaddtype/src/casts.cpp @@ -0,0 +1,114 @@ +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API +#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NPY_TARGET_VERSION NPY_2_0_API_VERSION +#define NO_IMPORT_ARRAY +#define NO_IMPORT_UFUNC + +#include +#include +#include +#include + +#include "numpy/arrayobject.h" +#include "numpy/ndarraytypes.h" +#include "numpy/dtype_api.h" + +#include "scalar.h" +#include "casts.h" +#include "dtype.h" + + +static NPY_CASTING quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + QuadPrecDTypeObject *given_descrs[2], + QuadPrecDTypeObject *loop_descrs[2], + npy_intp *view_offset) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + if (given_descrs[1] == NULL) { + Py_INCREF(given_descrs[0]); + loop_descrs[1] = given_descrs[0]; + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + *view_offset = 0; + return NPY_EQUIV_CASTING; +} + +static int quad_to_quad_strided_loop( + PyArrayMethod_Context *context, + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], void *NPY_UNUSED(auxdata)) +{ + npy_intp N = dimensions[0]; + char *in_ptr = data[0]; + char *out_ptr = data[1]; + + while (N--) { + Sleef_quad *in = (Sleef_quad *)in_ptr; + Sleef_quad *out = (Sleef_quad *)out_ptr; + + *out = *in; + + in_ptr += strides[0]; + out_ptr += strides[1]; + } + return 0; +} + +static std::vectorspecs; + + +PyArrayMethod_Spec ** init_casts_internal(void) +{ + PyArray_DTypeMeta **quad2quad_dtypes = new PyArray_DTypeMeta *[2]{nullptr, nullptr}; + + specs.push_back(new PyArrayMethod_Spec { + .name = "cast_QuadPrec_to_QuadPrec", + .nin = 1, + .nout = 1, + .casting = NPY_SAME_KIND_CASTING, + .flags = NPY_METH_SUPPORTS_UNALIGNED, + .dtypes = quad2quad_dtypes, + .slots = new PyType_Slot[3]{ + {NPY_METH_resolve_descriptors, (void *)&quad_to_quad_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_to_quad_strided_loop}, + {0, NULL} + }}); + + + return specs.data(); +} + +PyArrayMethod_Spec ** init_casts(void) +{ + try + { + return init_casts_internal(); + } + catch(const std::exception& e) + { + PyErr_NoMemory(); + return nullptr; + } + +} + +void free_casts(void) +{ + for (auto cast : specs) { + if (cast == nullptr) { + continue; + } + delete cast->dtypes; + delete cast->slots; + delete cast; + } + specs.clear(); +} diff --git a/quaddtype2/quaddtype/src/casts.h b/quaddtype2/quaddtype/src/casts.h new file mode 100644 index 00000000..504b9cd4 --- /dev/null +++ b/quaddtype2/quaddtype/src/casts.h @@ -0,0 +1,22 @@ +#ifndef _QUADDTYPE_CASTS_H +#define _QUADDTYPE_CASTS_H + +#include +#include "numpy/dtype_api.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyArrayMethod_Spec QuadtoQuadCastSpec; + +PyArrayMethod_Spec ** init_casts(void); + +void free_casts(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/dtype.c b/quaddtype2/quaddtype/src/dtype.c index d06d27da..f062f191 100644 --- a/quaddtype2/quaddtype/src/dtype.c +++ b/quaddtype2/quaddtype/src/dtype.c @@ -14,6 +14,7 @@ #include "numpy/_public_dtype_api_table.h" // not included in dtype_api.h #include "scalar.h" +#include "casts.h" #include "dtype.h" static inline int quad_load(Sleef_quad *x, char *data_ptr) @@ -135,6 +136,11 @@ static PyObject * quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) return (PyObject *)new; } +static PyArray_Descr *quadprec_default_descr(PyArray_DTypeMeta *NPY_UNUSED(cls)) +{ + return (PyArray_Descr *)new_quaddtype_instance(); +} + static PyType_Slot QuadPrecDType_Slots[] = { {NPY_DT_ensure_canonical, &ensure_canonical}, @@ -143,6 +149,7 @@ static PyType_Slot QuadPrecDType_Slots[] = {NPY_DT_discover_descr_from_pyobject, &quadprec_discover_descriptor_from_pyobject}, {NPY_DT_setitem, &quadprec_setitem}, {NPY_DT_getitem, &quadprec_getitem}, + {NPY_DT_default_descr, &quadprec_default_descr}, {0, NULL} }; @@ -176,7 +183,7 @@ PyArray_DTypeMeta QuadPrecDType = { int init_quadprec_dtype(void) { - PyArrayMethod_Spec **casts = NULL; // Initialize casts if needed + PyArrayMethod_Spec **casts = init_casts(); PyArrayDTypeMeta_Spec QuadPrecDType_DTypeSpec = { .flags = NPY_DT_NUMERIC, @@ -198,5 +205,8 @@ int init_quadprec_dtype(void) { return -1; } + + free_casts(); + return 0; } \ No newline at end of file From 35592326e975eaf2138f5592a24469e50ce4102f Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 2 Aug 2024 17:35:34 +0530 Subject: [PATCH 08/43] fixing NPY_SAME_CAST --- quaddtype2/quaddtype/src/casts.cpp | 2 +- quaddtype2/quaddtype/src/dtype.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/quaddtype2/quaddtype/src/casts.cpp b/quaddtype2/quaddtype/src/casts.cpp index 24ebf4b7..2dac91e8 100644 --- a/quaddtype2/quaddtype/src/casts.cpp +++ b/quaddtype2/quaddtype/src/casts.cpp @@ -38,7 +38,7 @@ static NPY_CASTING quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), } *view_offset = 0; - return NPY_EQUIV_CASTING; + return NPY_SAME_KIND_CASTING; } static int quad_to_quad_strided_loop( diff --git a/quaddtype2/quaddtype/src/dtype.c b/quaddtype2/quaddtype/src/dtype.c index f062f191..5992b42d 100644 --- a/quaddtype2/quaddtype/src/dtype.c +++ b/quaddtype2/quaddtype/src/dtype.c @@ -184,6 +184,8 @@ PyArray_DTypeMeta QuadPrecDType = { int init_quadprec_dtype(void) { PyArrayMethod_Spec **casts = init_casts(); + if (!casts) + return -1; PyArrayDTypeMeta_Spec QuadPrecDType_DTypeSpec = { .flags = NPY_DT_NUMERIC, From a481648dc1d164989311198e5c6299d2bf2299ba Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Sat, 3 Aug 2024 14:15:27 +0530 Subject: [PATCH 09/43] fixing quad precsion printing issue --- quaddtype2/quaddtype/src/casts.cpp | 73 +++++++++++------------ quaddtype2/quaddtype/src/scalar.c | 2 +- quaddtype2/quaddtype/tests/test_scalar.py | 9 +++ 3 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 quaddtype2/quaddtype/tests/test_scalar.py diff --git a/quaddtype2/quaddtype/src/casts.cpp b/quaddtype2/quaddtype/src/casts.cpp index 2dac91e8..6b5b042a 100644 --- a/quaddtype2/quaddtype/src/casts.cpp +++ b/quaddtype2/quaddtype/src/casts.cpp @@ -5,10 +5,10 @@ #define NO_IMPORT_ARRAY #define NO_IMPORT_UFUNC -#include -#include -#include -#include +#include +#include +#include +#include #include "numpy/arrayobject.h" #include "numpy/ndarraytypes.h" @@ -18,12 +18,11 @@ #include "casts.h" #include "dtype.h" - -static NPY_CASTING quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), - PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), - QuadPrecDTypeObject *given_descrs[2], - QuadPrecDTypeObject *loop_descrs[2], - npy_intp *view_offset) +static NPY_CASTING +quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), + PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), + QuadPrecDTypeObject *given_descrs[2], + QuadPrecDTypeObject *loop_descrs[2], npy_intp *view_offset) { Py_INCREF(given_descrs[0]); loop_descrs[0] = given_descrs[0]; @@ -41,10 +40,10 @@ static NPY_CASTING quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), return NPY_SAME_KIND_CASTING; } -static int quad_to_quad_strided_loop( - PyArrayMethod_Context *context, - char *const data[], npy_intp const dimensions[], - npy_intp const strides[], void *NPY_UNUSED(auxdata)) +static int +quad_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[], + npy_intp const dimensions[], npy_intp const strides[], + void *NPY_UNUSED(auxdata)) { npy_intp N = dimensions[0]; char *in_ptr = data[0]; @@ -62,45 +61,43 @@ static int quad_to_quad_strided_loop( return 0; } -static std::vectorspecs; - +static std::vector specs; -PyArrayMethod_Spec ** init_casts_internal(void) +PyArrayMethod_Spec ** +init_casts_internal(void) { PyArray_DTypeMeta **quad2quad_dtypes = new PyArray_DTypeMeta *[2]{nullptr, nullptr}; - specs.push_back(new PyArrayMethod_Spec { - .name = "cast_QuadPrec_to_QuadPrec", - .nin = 1, - .nout = 1, - .casting = NPY_SAME_KIND_CASTING, - .flags = NPY_METH_SUPPORTS_UNALIGNED, - .dtypes = quad2quad_dtypes, - .slots = new PyType_Slot[3]{ - {NPY_METH_resolve_descriptors, (void *)&quad_to_quad_resolve_descriptors}, - {NPY_METH_strided_loop, (void *)&quad_to_quad_strided_loop}, - {0, NULL} - }}); - - + specs.push_back(new PyArrayMethod_Spec{ + .name = "cast_QuadPrec_to_QuadPrec", + .nin = 1, + .nout = 1, + .casting = NPY_SAME_KIND_CASTING, + .flags = NPY_METH_SUPPORTS_UNALIGNED, + .dtypes = quad2quad_dtypes, + .slots = new PyType_Slot[4]{ + {NPY_METH_resolve_descriptors, (void *)&quad_to_quad_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_to_quad_strided_loop}, + {NPY_METH_unaligned_strided_loop, (void *)&quad_to_quad_strided_loop}, + {0, NULL}}}); + return specs.data(); } -PyArrayMethod_Spec ** init_casts(void) +PyArrayMethod_Spec ** +init_casts(void) { - try - { + try { return init_casts_internal(); } - catch(const std::exception& e) - { + catch (const std::exception &e) { PyErr_NoMemory(); return nullptr; } - } -void free_casts(void) +void +free_casts(void) { for (auto cast : specs) { if (cast == nullptr) { diff --git a/quaddtype2/quaddtype/src/scalar.c b/quaddtype2/quaddtype/src/scalar.c index b2fe5c95..b4fcc81a 100644 --- a/quaddtype2/quaddtype/src/scalar.c +++ b/quaddtype2/quaddtype/src/scalar.c @@ -71,7 +71,7 @@ QuadPrecision_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs) static PyObject * QuadPrecision_str(QuadPrecisionObject * self) { char buffer[128]; - Sleef_snprintf(buffer, sizeof(buffer), "%.*Qe", self->quad.value); + Sleef_snprintf(buffer, sizeof(buffer), "%.*Qe", SLEEF_QUAD_DIG, self->quad.value); return PyUnicode_FromString(buffer); } diff --git a/quaddtype2/quaddtype/tests/test_scalar.py b/quaddtype2/quaddtype/tests/test_scalar.py new file mode 100644 index 00000000..329f2507 --- /dev/null +++ b/quaddtype2/quaddtype/tests/test_scalar.py @@ -0,0 +1,9 @@ +import pytest + +import numpy as np +from quaddtype import QuadPrecDType, QuadPrecision + + +def test(): + a = QuadPrecision("1.63") + assert f"{np.array([a], dtype=QuadPrecDType).dtype}" == "QuadPrecDType()" From a33ea7f18130f46b24b23cbd68b131610c79150b Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Sat, 3 Aug 2024 14:16:43 +0530 Subject: [PATCH 10/43] fixing quad precsion printing issue --- quaddtype2/quaddtype/tests/{test_scalar.py => test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename quaddtype2/quaddtype/tests/{test_scalar.py => test.py} (100%) diff --git a/quaddtype2/quaddtype/tests/test_scalar.py b/quaddtype2/quaddtype/tests/test.py similarity index 100% rename from quaddtype2/quaddtype/tests/test_scalar.py rename to quaddtype2/quaddtype/tests/test.py From 220e0ae500b3a4c39dc80ded73bf506b1381ebf6 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Mon, 5 Aug 2024 15:25:59 +0530 Subject: [PATCH 11/43] removing hardcoded paths from meson build --- quaddtype2/meson.build | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/quaddtype2/meson.build b/quaddtype2/meson.build index 531570eb..d50c93c9 100644 --- a/quaddtype2/meson.build +++ b/quaddtype2/meson.build @@ -1,12 +1,12 @@ -project('quaddtype2', 'c', 'cpp') +project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17']) py_mod = import('python') py = py_mod.find_installation() c = meson.get_compiler('c') -sleef_dep = c.find_library('sleef', dirs: ['/usr/local/lib']) # sleef installation directories -sleefquad_dep = c.find_library('sleefquad', dirs: ['/usr/local/lib']) +sleef_dep = c.find_library('sleef') +sleefquad_dep = c.find_library('sleefquad') incdir_numpy = run_command(py, [ @@ -19,7 +19,6 @@ incdir_numpy = run_command(py, includes = include_directories( [ incdir_numpy, - '/usr/local/include', 'quaddtype/src', ] ) From 4dfe4904caa0df0ef9db4e20d157d717fa0a67c2 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 00:45:15 +0530 Subject: [PATCH 12/43] adding CI changes --- .github/workflows/ci.yml | 14 +- .gitignore | 1 + quaddtype/.flake8 | 6 - quaddtype/README.md | 16 - quaddtype/meson.build | 58 ++-- quaddtype/pyproject.toml | 4 +- quaddtype/quaddtype/__init__.py | 9 +- quaddtype/quaddtype/quadscalar.py | 11 - quaddtype/quaddtype/src/casts.c | 157 ---------- .../quaddtype/src/casts.cpp | 0 quaddtype/quaddtype/src/casts.h | 25 +- quaddtype/quaddtype/src/dtype.c | 279 ++++++++++-------- quaddtype/quaddtype/src/dtype.h | 28 +- quaddtype/quaddtype/src/quaddtype_main.c | 49 +-- .../quaddtype/src/scalar.c | 0 .../quaddtype/src/scalar.h | 0 quaddtype/quaddtype/src/umath.c | 124 -------- quaddtype/quaddtype/src/umath.h | 7 - .../quaddtype/tests/test.py | 0 quaddtype/reinstall.sh | 3 +- quaddtype/tests/conftest.py | 3 - quaddtype/tests/test_quaddtype.py | 33 --- quaddtype2/README.md | 0 quaddtype2/meson.build | 51 ---- quaddtype2/pyproject.toml | 25 -- quaddtype2/quaddtype/__init__.py | 1 - quaddtype2/quaddtype/src/casts.h | 22 -- quaddtype2/quaddtype/src/dtype.c | 214 -------------- quaddtype2/quaddtype/src/dtype.h | 23 -- quaddtype2/quaddtype/src/quaddtype_main.c | 48 --- quaddtype2/reinstall.sh | 14 - 31 files changed, 249 insertions(+), 976 deletions(-) delete mode 100644 quaddtype/.flake8 delete mode 100644 quaddtype/quaddtype/quadscalar.py delete mode 100644 quaddtype/quaddtype/src/casts.c rename {quaddtype2 => quaddtype}/quaddtype/src/casts.cpp (100%) rename {quaddtype2 => quaddtype}/quaddtype/src/scalar.c (100%) rename {quaddtype2 => quaddtype}/quaddtype/src/scalar.h (100%) delete mode 100644 quaddtype/quaddtype/src/umath.c delete mode 100644 quaddtype/quaddtype/src/umath.h rename {quaddtype2 => quaddtype}/quaddtype/tests/test.py (100%) delete mode 100644 quaddtype/tests/conftest.py delete mode 100644 quaddtype/tests/test_quaddtype.py delete mode 100644 quaddtype2/README.md delete mode 100644 quaddtype2/meson.build delete mode 100644 quaddtype2/pyproject.toml delete mode 100644 quaddtype2/quaddtype/__init__.py delete mode 100644 quaddtype2/quaddtype/src/casts.h delete mode 100644 quaddtype2/quaddtype/src/dtype.c delete mode 100644 quaddtype2/quaddtype/src/dtype.h delete mode 100644 quaddtype2/quaddtype/src/quaddtype_main.c delete mode 100755 quaddtype2/reinstall.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 354fd221..b41a0f01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,11 +57,21 @@ jobs: working-directory: unytdtype run: | pytest -vvv --color=yes + - name: Install quaddtype dependencies + run: | + sudo apt-get update + sudo apt-get install -y libmpfr-dev libssl-dev libfftw3-dev + - name: Install SLEEF + run: | + git clone https://github.com/shibatch/sleef.git + cd sleef + cmake -S . -B build/ + cmake --build build/ --clean-first -j + sudo cmake --install build/ --prefix /usr - name: Install quaddtype working-directory: quaddtype run: | - python -m build --no-isolation --wheel -Cbuilddir=build - find ./dist/*.whl | xargs python -m pip install + python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" - name: Run quaddtype tests working-directory: quaddtype run: | diff --git a/.gitignore b/.gitignore index 0bc2e657..65005454 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,4 @@ compile_commands.json .ruff-cache/ .asv +.vscode/ \ No newline at end of file diff --git a/quaddtype/.flake8 b/quaddtype/.flake8 deleted file mode 100644 index e82426e8..00000000 --- a/quaddtype/.flake8 +++ /dev/null @@ -1,6 +0,0 @@ -[flake8] -max-line-length = 100 -ignore = D107,D104,W503 -per-file-ignores = __init__.py:F401,tests/*:D100 -docstring-convention = numpy -docstring_style = numpy diff --git a/quaddtype/README.md b/quaddtype/README.md index f1b9322e..e69de29b 100644 --- a/quaddtype/README.md +++ b/quaddtype/README.md @@ -1,16 +0,0 @@ -# quaddtype - -Quad (128-bit) float dtype for numpy - -## Installation - -To install, make sure you have `numpy` nightly installed. Then build without -isolation so that the `quaddtype` can link against the experimental dtype API -headers, which aren't in the latest releases of `numpy`: - -```bash -pip install -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy -pip install . --no-build-isolation -``` - -Developed with Python 3.11, but 3.9 and 3.10 will probably also work. diff --git a/quaddtype/meson.build b/quaddtype/meson.build index 3c1b0712..d50c93c9 100644 --- a/quaddtype/meson.build +++ b/quaddtype/meson.build @@ -1,47 +1,51 @@ -project( - 'quaddtype', - 'c', -) +project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17']) py_mod = import('python') py = py_mod.find_installation() +c = meson.get_compiler('c') + +sleef_dep = c.find_library('sleef') +sleefquad_dep = c.find_library('sleefquad') + incdir_numpy = run_command(py, [ '-c', - 'import numpy; print(numpy.get_include())' + 'import numpy; import os; print(os.path.relpath(numpy.get_include()))' ], check: true ).stdout().strip() includes = include_directories( - [ - incdir_numpy, - 'quaddtype/src' - ] + [ + incdir_numpy, + 'quaddtype/src', + ] ) srcs = [ - 'quaddtype/src/umath.c', - 'quaddtype/src/casts.c', - 'quaddtype/src/dtype.c', - 'quaddtype/src/quaddtype_main.c', + 'quaddtype/src/casts.h', + 'quaddtype/src/casts.cpp', + 'quaddtype/src/scalar.h', + 'quaddtype/src/scalar.c', + 'quaddtype/src/dtype.h', + 'quaddtype/src/dtype.c', + 'quaddtype/src/quaddtype_main.c' ] py.install_sources( - [ - 'quaddtype/__init__.py', - 'quaddtype/quadscalar.py' - ], - subdir: 'quaddtype', - pure: false + [ + 'quaddtype/__init__.py', + ], + subdir: 'quaddtype', + pure: false ) -py.extension_module( - '_quaddtype_main', - srcs, - c_args: ['-g', '-O0'], - install: true, - subdir: 'quaddtype', - include_directories: includes -) +py.extension_module('_quaddtype_main', +srcs, +c_args: ['-g', '-O0', '-lsleef', '-lsleefquad'], +dependencies: [sleef_dep, sleefquad_dep], +install: true, +subdir: 'quaddtype', +include_directories: includes +) \ No newline at end of file diff --git a/quaddtype/pyproject.toml b/quaddtype/pyproject.toml index a2a6fca1..68b61e7f 100644 --- a/quaddtype/pyproject.toml +++ b/quaddtype/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "meson>=0.63.0", + "meson>=1.3.2", "meson-python", "patchelf", "wheel", @@ -13,7 +13,7 @@ name = "quaddtype" description = "Quad (128-bit) float dtype for numpy" version = "0.0.1" readme = 'README.md' -author = "Peyton Murray" +author = "Swayam Singh" requires-python = ">=3.9.0" dependencies = [ "numpy" diff --git a/quaddtype/quaddtype/__init__.py b/quaddtype/quaddtype/__init__.py index c6fe3db1..9f246f73 100644 --- a/quaddtype/quaddtype/__init__.py +++ b/quaddtype/quaddtype/__init__.py @@ -1,8 +1 @@ -# Scalar quantity must be defined _before_ the dtype, so don't isort it. -# During initialization of _quaddtype_main, QuadScalar is imported from this -# (partially initialized) -# module, and therefore has to be defined first. -from .quadscalar import QuadScalar # isort: skip -from ._quaddtype_main import QuadDType - -__all__ = ["QuadScalar", "QuadDType"] +from ._quaddtype_main import QuadPrecDType, QuadPrecision \ No newline at end of file diff --git a/quaddtype/quaddtype/quadscalar.py b/quaddtype/quaddtype/quadscalar.py deleted file mode 100644 index 117c2b34..00000000 --- a/quaddtype/quaddtype/quadscalar.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Quad scalar floating point type for numpy.""" - - -class QuadScalar: - """Quad scalar floating point type.""" - - def __init__(self, value): - self.value = value - - def __repr__(self): - return f"{self.value}" diff --git a/quaddtype/quaddtype/src/casts.c b/quaddtype/quaddtype/src/casts.c deleted file mode 100644 index 999344f5..00000000 --- a/quaddtype/quaddtype/src/casts.c +++ /dev/null @@ -1,157 +0,0 @@ -#include - -#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API -#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION -#define NPY_TARGET_VERSION NPY_2_0_API_VERSION -#define NO_IMPORT_ARRAY -#define NO_IMPORT_UFUNC -#include "numpy/ndarraytypes.h" -#include "numpy/arrayobject.h" -#include "numpy/dtype_api.h" - -#include "dtype.h" -#include "casts.h" - -// And now the actual cast code! Starting with the "resolver" which tells -// us about cast safety. -// Note also the `view_offset`! It is a way for you to tell NumPy, that this -// cast does not require anything at all, but the cast can simply be done as -// a view. -// For `arr.astype()` it might mean returning a view (eventually, not yet). -// For ufuncs, it already means that they don't have to do a cast at all! -// -// From https://numpy.org/neps/nep-0043-extensible-ufuncs.html#arraymethod: -// resolve_descriptors returns the safety of the operation (casting safety) -static NPY_CASTING -quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), - PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), - PyArray_Descr *given_descrs[2], PyArray_Descr *loop_descrs[2], - npy_intp *view_offset) -{ - Py_INCREF(given_descrs[0]); - loop_descrs[0] = given_descrs[0]; - - if (given_descrs[1] == NULL) { - Py_INCREF(given_descrs[0]); - loop_descrs[1] = given_descrs[0]; - } - else { - Py_INCREF(given_descrs[1]); - loop_descrs[1] = given_descrs[1]; - } - - return NPY_SAME_KIND_CASTING; -} - -// Each element is a __float128 element; no casting needed -static int -quad_to_quad_contiguous(PyArrayMethod_Context *NPY_UNUSED(context), char *const data[], - npy_intp const dimensions[], npy_intp const strides[], - NpyAuxData *NPY_UNUSED(auxdata)) -{ - npy_intp N = dimensions[0]; - __float128 *in = (__float128 *)data[0]; - __float128 *out = (__float128 *)data[1]; - - while (N--) { - *out = *in; - out++; - in++; - } - - return 0; -} - -// Elements are strided, e.g. -// -// x = np.linspace(40) -// x[::3] -// -// Therefore the stride needs to be used to increment the pointers inside the loop. -static int -quad_to_quad_strided(PyArrayMethod_Context *NPY_UNUSED(context), char *const data[], - npy_intp const dimensions[], npy_intp const strides[], - NpyAuxData *NPY_UNUSED(auxdata)) -{ - npy_intp N = dimensions[0]; - char *in = data[0]; - char *out = data[1]; - npy_intp in_stride = strides[0]; - npy_intp out_stride = strides[1]; - - while (N--) { - *(__float128 *)out = *(__float128 *)in; - in += in_stride; - out += out_stride; - } - - return 0; -} - -// Arrays are unaligned. -static int -quad_to_quad_unaligned(PyArrayMethod_Context *NPY_UNUSED(context), char *const data[], - npy_intp const dimensions[], npy_intp const strides[], - NpyAuxData *NPY_UNUSED(auxdata)) -{ - npy_intp N = dimensions[0]; - char *in = data[0]; - char *out = data[1]; - npy_intp in_stride = strides[0]; - npy_intp out_stride = strides[1]; - - while (N--) { - memcpy(out, in, sizeof(__float128)); // NOLINT - in += in_stride; - out += out_stride; - } - - return 0; -} - -// Returns the low-level C (strided inner-loop) function which -// performs the actual operation. This method may initially be private, users will be -// able to provide a set of optimized inner-loop functions instead: -// * `strided_inner_loop` -// * `contiguous_inner_loop` -// * `unaligned_strided_loop` -// * ... -static int -quad_to_quad_get_loop(PyArrayMethod_Context *context, int aligned, int NPY_UNUSED(move_references), - const npy_intp *strides, PyArrayMethod_StridedLoop **out_loop, - NpyAuxData **out_transferdata, NPY_ARRAYMETHOD_FLAGS *flags) -{ - int contig = (strides[0] == sizeof(__float128) && strides[1] == sizeof(__float128)); - - if (aligned && contig) - *out_loop = (PyArrayMethod_StridedLoop *)&quad_to_quad_contiguous; - else if (aligned) - *out_loop = (PyArrayMethod_StridedLoop *)&quad_to_quad_strided; - else - *out_loop = (PyArrayMethod_StridedLoop *)&quad_to_quad_unaligned; - - *flags = 0; - return 0; -} - -/* - * NumPy currently allows NULL for the own DType/"cls". For other DTypes - * we would have to fill it in here: - */ -static PyArray_DTypeMeta *QuadToQuadDtypes[2] = {NULL, NULL}; - -static PyType_Slot QuadToQuadSlots[] = { - {NPY_METH_resolve_descriptors, &quad_to_quad_resolve_descriptors}, - {NPY_METH_get_loop, &quad_to_quad_get_loop}, - {0, NULL}}; - -PyArrayMethod_Spec QuadToQuadCastSpec = { - .name = "cast_QuadDType_to_QuadDType", - .nin = 1, - .nout = 1, - .flags = NPY_METH_SUPPORTS_UNALIGNED, - .casting = NPY_SAME_KIND_CASTING, - .dtypes = QuadToQuadDtypes, - .slots = QuadToQuadSlots, -}; diff --git a/quaddtype2/quaddtype/src/casts.cpp b/quaddtype/quaddtype/src/casts.cpp similarity index 100% rename from quaddtype2/quaddtype/src/casts.cpp rename to quaddtype/quaddtype/src/casts.cpp diff --git a/quaddtype/quaddtype/src/casts.h b/quaddtype/quaddtype/src/casts.h index de775617..504b9cd4 100644 --- a/quaddtype/quaddtype/src/casts.h +++ b/quaddtype/quaddtype/src/casts.h @@ -1,7 +1,22 @@ -#ifndef _NPY_CASTS_H -#define _NPY_CASTS_H +#ifndef _QUADDTYPE_CASTS_H +#define _QUADDTYPE_CASTS_H -extern PyArrayMethod_Spec QuadToQuadCastSpec; -extern PyArrayMethod_Spec QuadToFloat128CastSpec; +#include +#include "numpy/dtype_api.h" -#endif /* _NPY_CASTS_H */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern PyArrayMethod_Spec QuadtoQuadCastSpec; + +PyArrayMethod_Spec ** init_casts(void); + +void free_casts(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/quaddtype/quaddtype/src/dtype.c b/quaddtype/quaddtype/src/dtype.c index 63a6840e..5992b42d 100644 --- a/quaddtype/quaddtype/src/dtype.c +++ b/quaddtype/quaddtype/src/dtype.c @@ -1,197 +1,214 @@ #include +#include +#include -#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API #define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION #define NPY_TARGET_VERSION NPY_2_0_API_VERSION #define NO_IMPORT_ARRAY #define NO_IMPORT_UFUNC -#include "numpy/ndarraytypes.h" #include "numpy/arrayobject.h" +#include "numpy/ndarraytypes.h" #include "numpy/dtype_api.h" +#include "numpy/_public_dtype_api_table.h" // not included in dtype_api.h -#include "dtype.h" -#include "abstract.h" +#include "scalar.h" #include "casts.h" +#include "dtype.h" -PyTypeObject *QuadScalar_Type = NULL; - -QuadDTypeObject * -new_quaddtype_instance(void) +static inline int quad_load(Sleef_quad *x, char *data_ptr) { - QuadDTypeObject *new = - (QuadDTypeObject *)PyArrayDescr_Type.tp_new((PyTypeObject *)&QuadDType, NULL, NULL); - if (new == NULL) { - return NULL; + if (data_ptr == NULL || x == NULL) + { + PyErr_SetString(PyExc_ValueError, "Invalid memory location"); + return -1; } - - new->base.elsize = sizeof(__float128); - new->base.alignment = _Alignof(__float128); - return new; + *x = *(Sleef_quad *)data_ptr; + return 0; } -// Take an python double and put a copy into the array -static int -quad_setitem(QuadDTypeObject *descr, PyObject *obj, char *dataptr) +static inline int quad_store(char *data_ptr, Sleef_quad x) { - __float128 val = (__float128)PyFloat_AsDouble(obj); - memcpy(dataptr, &val, sizeof(__float128)); + if (data_ptr == NULL) + { + PyErr_SetString(PyExc_ValueError, "Invalid memory location"); + return -1; + } + *(Sleef_quad *)data_ptr = x; return 0; } -static PyObject * -quad_getitem(QuadDTypeObject *descr, char *dataptr) +QuadPrecDTypeObject * new_quaddtype_instance(void) { - __float128 val; - memcpy(&val, dataptr, sizeof(__float128)); - - PyObject *val_obj = PyFloat_FromDouble((double)val); - if (val_obj == NULL) { + QuadPrecDTypeObject *new = (QuadPrecDTypeObject *)PyArrayDescr_Type.tp_new((PyTypeObject *)&QuadPrecDType, NULL, NULL); + if (new == NULL) { return NULL; } + new->base.elsize = sizeof(Sleef_quad); + new->base.alignment = _Alignof(Sleef_quad); + new->base.flags |= NPY_NEEDS_INIT; // Indicates memory for this data-type must be initialized (set to 0) on creation. - // Need to create a new QuadScalar instance here and return that... - PyObject *res = PyObject_CallFunctionObjArgs((PyObject *)QuadScalar_Type, val_obj, NULL); - if (res == NULL) { - return NULL; - } - Py_DECREF(val_obj); - return res; + return new; } -// For two instances of the same dtype, both have the same precision. Return -// self. -static QuadDTypeObject * -common_instance(QuadDTypeObject *self, QuadDTypeObject *other) +static QuadPrecDTypeObject * ensure_canonical(QuadPrecDTypeObject *self) { + Py_INCREF(self); return self; } -// When dtypes are mixed, find a "common" dtype for the two which can hold -// content of both without loss of information. I guess this should return a -// 256-bit float dtype? Since this isn't natively supported by any platform, -// just return another 128-bit float dtype. -static PyArray_DTypeMeta * -common_dtype(PyArray_DTypeMeta *self, PyArray_DTypeMeta *other) +static QuadPrecDTypeObject * common_instance(QuadPrecDTypeObject *dtype1, QuadPrecDTypeObject *dtype2) +{ + Py_INCREF(dtype1); + return dtype1; +} + + +static PyArray_DTypeMeta * common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) { - /* - * Typenum is useful for NumPy, but there it can still be convenient. - * (New-style user dtypes will probably get -1 as type number...) - */ - if (other->type_num >= 0 && PyTypeNum_ISNUMBER(other->type_num) && - !PyTypeNum_ISCOMPLEX(other->type_num)) { - // float128 is the biggest natively supported float. Return it in all - // cases where other is a number (and not complex). - Py_INCREF(self); - return self; + // Promote integer and floating-point types to QuadPrecDType + if (other->type_num >= 0 && + (PyTypeNum_ISINTEGER(other->type_num) || + PyTypeNum_ISFLOAT(other->type_num))) { + Py_INCREF(cls); + return cls; + } + // Don't promote complex types + if (PyTypeNum_ISCOMPLEX(other->type_num)) { + Py_INCREF(Py_NotImplemented); + return (PyArray_DTypeMeta *)Py_NotImplemented; } - // Revert to object dtype in all other cases. Py_INCREF(Py_NotImplemented); return (PyArray_DTypeMeta *)Py_NotImplemented; } -// Expected to have this, and that it does an incref; see NEP42 -// Without this you'll get weird memory corruption bugs in the casting code -static QuadDTypeObject * -quaddtype_ensure_canonical(QuadDTypeObject *self) +static PyArray_Descr * +quadprec_discover_descriptor_from_pyobject(PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) { - Py_INCREF(self); - return self; + if (Py_TYPE(obj) != &QuadPrecision_Type) + { + PyErr_SetString(PyExc_TypeError, "Can only store QuadPrecision in a QuadPrecDType array."); + return NULL; + } + return (PyArray_Descr *)new_quaddtype_instance(); } -static PyArray_Descr * -quad_discover_descriptor_from_pyobject(PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) +static int quadprec_setitem(QuadPrecDTypeObject *descr, PyObject *obj, char *dataptr) { - if (Py_TYPE(obj) != QuadScalar_Type) { - PyErr_SetString(PyExc_TypeError, "Can only store QuadScalars in a QuadDType array."); - return NULL; + QuadPrecisionObject *value; + if (PyObject_TypeCheck(obj, &QuadPrecision_Type)) + { + Py_INCREF(obj); + value = (QuadPrecisionObject *)obj; + } + else + { + value = QuadPrecision_from_object(obj); + if (value == NULL) { + return -1; + } } - // Get the dtype attribute from the object. - return (PyArray_Descr *)PyObject_GetAttrString(obj, "dtype"); + if (quad_store(dataptr, value->quad.value) < 0) + { + Py_DECREF(value); + return -1; + } + + Py_DECREF(value); + return 0; } -static PyType_Slot QuadDType_Slots[] = { - {NPY_DT_common_instance, &common_instance}, - {NPY_DT_common_dtype, &common_dtype}, - {NPY_DT_discover_descr_from_pyobject, &quad_discover_descriptor_from_pyobject}, - /* The header is wrong on main :(, so we add 1 */ - {NPY_DT_setitem, &quad_setitem}, - {NPY_DT_getitem, &quad_getitem}, - {NPY_DT_ensure_canonical, &quaddtype_ensure_canonical}, - {0, NULL}}; - -/* - * The following defines everything type object related (i.e. not NumPy - * specific). - * - * Note that this function is by default called without any arguments to fetch - * a default version of the descriptor (in principle at least). During init - * we fill in `cls->singleton` though for the dimensionless unit. - */ -static PyObject * -quaddtype_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwargs) +static PyObject * quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) { - return (PyObject *)new_quaddtype_instance(); + QuadPrecisionObject *new = QuadPrecision_raw_new(); + if (!new) + { + return NULL; + } + if (quad_load(&new->quad.value, dataptr) < 0) + { + Py_DECREF(new); + return NULL; + } + return (PyObject *)new; +} + +static PyArray_Descr *quadprec_default_descr(PyArray_DTypeMeta *NPY_UNUSED(cls)) +{ + return (PyArray_Descr *)new_quaddtype_instance(); } -static void -quaddtype_dealloc(QuadDTypeObject *self) +static PyType_Slot QuadPrecDType_Slots[] = { - PyArrayDescr_Type.tp_dealloc((PyObject *)self); + {NPY_DT_ensure_canonical, &ensure_canonical}, + {NPY_DT_common_instance, &common_instance}, + {NPY_DT_common_dtype, &common_dtype}, + {NPY_DT_discover_descr_from_pyobject, &quadprec_discover_descriptor_from_pyobject}, + {NPY_DT_setitem, &quadprec_setitem}, + {NPY_DT_getitem, &quadprec_getitem}, + {NPY_DT_default_descr, &quadprec_default_descr}, + {0, NULL} +}; + + +static PyObject * QuadPrecDType_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwds) +{ + if (PyTuple_GET_SIZE(args) != 0 || (kwds != NULL && PyDict_Size(kwds) != 0)) { + PyErr_SetString(PyExc_TypeError, + "QuadPrecDType takes no arguments"); + return NULL; + } + + return (PyObject *)new_quaddtype_instance(); } -static PyObject * -quaddtype_repr(QuadDTypeObject *self) +static PyObject * QuadPrecDType_repr(QuadPrecDTypeObject *self) { - PyObject *res = PyUnicode_FromString("This is a quad (128-bit float) dtype."); - return res; + return PyUnicode_FromString("QuadPrecDType()"); } -// These are the basic things that you need to create a Python Type/Class in C. -// However, there is a slight difference here because we create a -// PyArray_DTypeMeta, which is a larger struct than a typical type. -// (This should get a bit nicer eventually with Python >3.11.) -PyArray_DTypeMeta QuadDType = { - {{ - PyVarObject_HEAD_INIT(NULL, 0).tp_name = "quaddtype.QuadDType", - .tp_basicsize = sizeof(QuadDTypeObject), - .tp_new = quaddtype_new, - .tp_dealloc = (destructor)quaddtype_dealloc, - .tp_repr = (reprfunc)quaddtype_repr, - .tp_str = (reprfunc)quaddtype_repr, - }}, - /* rest, filled in during DTypeMeta initialization */ +PyArray_DTypeMeta QuadPrecDType = { + {{ + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "QuadPrecDType.QuadPrecDType", + .tp_basicsize = sizeof(QuadPrecDTypeObject), + .tp_new = QuadPrecDType_new, + .tp_repr = (reprfunc)QuadPrecDType_repr, + .tp_str = (reprfunc)QuadPrecDType_repr, + }}, }; -int -init_quad_dtype(void) +int init_quadprec_dtype(void) { - // To create our DType, we have to use a "Spec" that tells NumPy how to - // do it. You first have to create a static type, but see the note there! - PyArrayMethod_Spec *casts[] = { - &QuadToQuadCastSpec, - NULL, - }; + PyArrayMethod_Spec **casts = init_casts(); + if (!casts) + return -1; - PyArrayDTypeMeta_Spec QuadDType_DTypeSpec = { - .flags = NPY_DT_NUMERIC, - .casts = casts, - .typeobj = QuadScalar_Type, - .slots = QuadDType_Slots, + PyArrayDTypeMeta_Spec QuadPrecDType_DTypeSpec = { + .flags = NPY_DT_NUMERIC, + .casts = casts, + .typeobj = &QuadPrecision_Type, + .slots = QuadPrecDType_Slots, }; - ((PyObject *)&QuadDType)->ob_type = &PyArrayDTypeMeta_Type; - ((PyTypeObject *)&QuadDType)->tp_base = &PyArrayDescr_Type; - if (PyType_Ready((PyTypeObject *)&QuadDType) < 0) { + ((PyObject *)&QuadPrecDType)->ob_type = &PyArrayDTypeMeta_Type; + + ((PyTypeObject *)&QuadPrecDType)->tp_base = &PyArrayDescr_Type; + + if (PyType_Ready((PyTypeObject *)&QuadPrecDType) < 0) + { return -1; } - if (PyArrayInitDTypeMeta_FromSpec(&QuadDType, &QuadDType_DTypeSpec) < 0) { + if (PyArrayInitDTypeMeta_FromSpec(&QuadPrecDType, &QuadPrecDType_DTypeSpec) < 0) + { return -1; } - QuadDType.singleton = PyArray_GetDefaultDescr(&QuadDType); + free_casts(); + return 0; -} +} \ No newline at end of file diff --git a/quaddtype/quaddtype/src/dtype.h b/quaddtype/quaddtype/src/dtype.h index 104ed3e3..e935063f 100644 --- a/quaddtype/quaddtype/src/dtype.h +++ b/quaddtype/quaddtype/src/dtype.h @@ -1,17 +1,23 @@ -#ifndef _NPY_DTYPE_H -#define _NPY_DTYPE_H +#ifndef _QUADDTYPE_DTYPE_H +#define _QUADDTYPE_DTYPE_H -typedef struct { +#include +#include +#include +#include + +#include "scalar.h" + +typedef struct +{ PyArray_Descr base; -} QuadDTypeObject; + +} QuadPrecDTypeObject; -extern PyArray_DTypeMeta QuadDType; -extern PyTypeObject *QuadScalar_Type; +extern PyArray_DTypeMeta QuadPrecDType; -QuadDTypeObject * -new_quaddtype_instance(void); +QuadPrecDTypeObject * new_quaddtype_instance(void); -int -init_quad_dtype(void); +int init_quadprec_dtype(void); -#endif /*_NPY_DTYPE_H*/ +#endif \ No newline at end of file diff --git a/quaddtype/quaddtype/src/quaddtype_main.c b/quaddtype/quaddtype/src/quaddtype_main.c index d9adeff5..ed0778e1 100644 --- a/quaddtype/quaddtype/src/quaddtype_main.c +++ b/quaddtype/quaddtype/src/quaddtype_main.c @@ -1,65 +1,48 @@ #include -#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API #define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NPY_TARGET_VERSION NPY_2_0_API_VERSION + #include "numpy/arrayobject.h" -#include "numpy/ufuncobject.h" #include "numpy/dtype_api.h" #include "dtype.h" -#include "umath.h" static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, - .m_name = "quaddtype_main", - .m_doc = "Quad (128-bit) floating point experimental numpy dtype", + .m_name = "_quaddtype_main", + .m_doc = "Quad (128-bit) floating point Data Type for Numpy", .m_size = -1, }; -// Initialize the python module PyMODINIT_FUNC PyInit__quaddtype_main(void) { import_array(); - import_umath(); - PyObject *m = PyModule_Create(&moduledef); - if (m == NULL) { - PyErr_SetString(PyExc_ImportError, "Unable to create the quaddtype_main module."); + if (!m) + { return NULL; } - PyObject *mod = PyImport_ImportModule("quaddtype"); - if (mod == NULL) { - PyErr_SetString(PyExc_ImportError, "Unable to import the quaddtype module."); + if (init_quadprecision_scalar() < 0) goto error; - } - QuadScalar_Type = (PyTypeObject *)PyObject_GetAttrString(mod, "QuadScalar"); - Py_DECREF(mod); - if (QuadScalar_Type == NULL) { - PyErr_SetString(PyExc_AttributeError, - "Unable to find QuadScalar attribute in the " - "quaddtype_main module."); - goto error; - } - if (init_quad_dtype() < 0) { - PyErr_SetString(PyExc_AttributeError, "QuadDType failed to initialize."); + + if(PyModule_AddObject(m, "QuadPrecision", (PyObject *)&QuadPrecision_Type) < 0) goto error; - } - if (PyModule_AddObject(m, "QuadDType", (PyObject *)&QuadDType) < 0) { - PyErr_SetString(PyExc_TypeError, "Failed to add QuadDType to the quaddtype_main module."); + + if(init_quadprec_dtype() < 0) goto error; - } - if (init_multiply_ufunc() == -1) { - PyErr_SetString(PyExc_TypeError, "Failed to initialize the quadscalar multiply ufunc."); + if(PyModule_AddObject(m, "QuadPrecDType", (PyObject *)&QuadPrecDType) < 0) goto error; - } return m; + error: Py_DECREF(m); return NULL; -} +} \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/scalar.c b/quaddtype/quaddtype/src/scalar.c similarity index 100% rename from quaddtype2/quaddtype/src/scalar.c rename to quaddtype/quaddtype/src/scalar.c diff --git a/quaddtype2/quaddtype/src/scalar.h b/quaddtype/quaddtype/src/scalar.h similarity index 100% rename from quaddtype2/quaddtype/src/scalar.h rename to quaddtype/quaddtype/src/scalar.h diff --git a/quaddtype/quaddtype/src/umath.c b/quaddtype/quaddtype/src/umath.c deleted file mode 100644 index 6c9fece6..00000000 --- a/quaddtype/quaddtype/src/umath.c +++ /dev/null @@ -1,124 +0,0 @@ -#include - -#define PY_ARRAY_UNIQUE_SYMBOL quaddtype_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL quaddtype_UFUNC_API -#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION -#define NPY_TARGET_VERSION NPY_2_0_API_VERSION -#define NO_IMPORT_ARRAY -#define NO_IMPORT_UFUNC -#include "numpy/ndarraytypes.h" -#include "numpy/arrayobject.h" -#include "numpy/ufuncobject.h" -#include "numpy/dtype_api.h" - -#include "dtype.h" -#include "umath.h" - -// The multiplication loop, this is very minimal! Look at the cast to see -// some of the more advanced things you can do for optimization! -// Look at the seberg/unitdtype exaple repository for how this can be done -// more generically without implementing a multiply loop! -static int -quad_multiply_strided_loop(PyArrayMethod_Context *context, char *const data[], - npy_intp const dimensions[], npy_intp const strides[], - NpyAuxData *auxdata) -{ - npy_intp N = dimensions[0]; - char *in1 = data[0], *in2 = data[1]; - char *out = data[2]; - npy_intp in1_stride = strides[0]; - npy_intp in2_stride = strides[1]; - npy_intp out_stride = strides[2]; - - while (N--) { - *(__float128 *)out = *(__float128 *)in1 * *(__float128 *)in2; - in1 += in1_stride; - in2 += in2_stride; - out += out_stride; - } - return 0; -} - -// This is the "dtype/descriptor resolver". Its main job is to fill in the -// result dtype in this case, that is: - -// given_descrs[0] = arr1.dtype -// given_descrs[1] = arr2.dtype -// given_descrs[2] = NULL or out.dtype - -// The code now sets `loop_descrs` to what we actually get as a result. -// In practice that is just fill in the last `out.dtype`. But in principle, -// we might need casting (e.g. swap `>f8` to `=1.3.2", - "meson-python", - "patchelf", - "wheel", - "numpy" -] -build-backend = "mesonpy" - -[project] -name = "quaddtype" -description = "Quad (128-bit) float dtype for numpy" -version = "0.0.1" -readme = 'README.md' -author = "Swayam Singh" -requires-python = ">=3.9.0" -dependencies = [ - "numpy" -] - -[project.optional-dependencies] -test = [ - "pytest", -] diff --git a/quaddtype2/quaddtype/__init__.py b/quaddtype2/quaddtype/__init__.py deleted file mode 100644 index 9f246f73..00000000 --- a/quaddtype2/quaddtype/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from ._quaddtype_main import QuadPrecDType, QuadPrecision \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/casts.h b/quaddtype2/quaddtype/src/casts.h deleted file mode 100644 index 504b9cd4..00000000 --- a/quaddtype2/quaddtype/src/casts.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _QUADDTYPE_CASTS_H -#define _QUADDTYPE_CASTS_H - -#include -#include "numpy/dtype_api.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -extern PyArrayMethod_Spec QuadtoQuadCastSpec; - -PyArrayMethod_Spec ** init_casts(void); - -void free_casts(void); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/dtype.c b/quaddtype2/quaddtype/src/dtype.c deleted file mode 100644 index 5992b42d..00000000 --- a/quaddtype2/quaddtype/src/dtype.c +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include -#include - -#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API -#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION -#define NPY_TARGET_VERSION NPY_2_0_API_VERSION -#define NO_IMPORT_ARRAY -#define NO_IMPORT_UFUNC -#include "numpy/arrayobject.h" -#include "numpy/ndarraytypes.h" -#include "numpy/dtype_api.h" -#include "numpy/_public_dtype_api_table.h" // not included in dtype_api.h - -#include "scalar.h" -#include "casts.h" -#include "dtype.h" - -static inline int quad_load(Sleef_quad *x, char *data_ptr) -{ - if (data_ptr == NULL || x == NULL) - { - PyErr_SetString(PyExc_ValueError, "Invalid memory location"); - return -1; - } - *x = *(Sleef_quad *)data_ptr; - return 0; -} - -static inline int quad_store(char *data_ptr, Sleef_quad x) -{ - if (data_ptr == NULL) - { - PyErr_SetString(PyExc_ValueError, "Invalid memory location"); - return -1; - } - *(Sleef_quad *)data_ptr = x; - return 0; -} - -QuadPrecDTypeObject * new_quaddtype_instance(void) -{ - QuadPrecDTypeObject *new = (QuadPrecDTypeObject *)PyArrayDescr_Type.tp_new((PyTypeObject *)&QuadPrecDType, NULL, NULL); - if (new == NULL) { - return NULL; - } - new->base.elsize = sizeof(Sleef_quad); - new->base.alignment = _Alignof(Sleef_quad); - new->base.flags |= NPY_NEEDS_INIT; // Indicates memory for this data-type must be initialized (set to 0) on creation. - - return new; -} - -static QuadPrecDTypeObject * ensure_canonical(QuadPrecDTypeObject *self) -{ - Py_INCREF(self); - return self; -} - -static QuadPrecDTypeObject * common_instance(QuadPrecDTypeObject *dtype1, QuadPrecDTypeObject *dtype2) -{ - Py_INCREF(dtype1); - return dtype1; -} - - -static PyArray_DTypeMeta * common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) -{ - // Promote integer and floating-point types to QuadPrecDType - if (other->type_num >= 0 && - (PyTypeNum_ISINTEGER(other->type_num) || - PyTypeNum_ISFLOAT(other->type_num))) { - Py_INCREF(cls); - return cls; - } - // Don't promote complex types - if (PyTypeNum_ISCOMPLEX(other->type_num)) { - Py_INCREF(Py_NotImplemented); - return (PyArray_DTypeMeta *)Py_NotImplemented; - } - - Py_INCREF(Py_NotImplemented); - return (PyArray_DTypeMeta *)Py_NotImplemented; -} - -static PyArray_Descr * -quadprec_discover_descriptor_from_pyobject(PyArray_DTypeMeta *NPY_UNUSED(cls), PyObject *obj) -{ - if (Py_TYPE(obj) != &QuadPrecision_Type) - { - PyErr_SetString(PyExc_TypeError, "Can only store QuadPrecision in a QuadPrecDType array."); - return NULL; - } - return (PyArray_Descr *)new_quaddtype_instance(); -} - -static int quadprec_setitem(QuadPrecDTypeObject *descr, PyObject *obj, char *dataptr) -{ - QuadPrecisionObject *value; - if (PyObject_TypeCheck(obj, &QuadPrecision_Type)) - { - Py_INCREF(obj); - value = (QuadPrecisionObject *)obj; - } - else - { - value = QuadPrecision_from_object(obj); - if (value == NULL) { - return -1; - } - } - - if (quad_store(dataptr, value->quad.value) < 0) - { - Py_DECREF(value); - return -1; - } - - Py_DECREF(value); - return 0; -} - -static PyObject * quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) -{ - QuadPrecisionObject *new = QuadPrecision_raw_new(); - if (!new) - { - return NULL; - } - if (quad_load(&new->quad.value, dataptr) < 0) - { - Py_DECREF(new); - return NULL; - } - return (PyObject *)new; -} - -static PyArray_Descr *quadprec_default_descr(PyArray_DTypeMeta *NPY_UNUSED(cls)) -{ - return (PyArray_Descr *)new_quaddtype_instance(); -} - -static PyType_Slot QuadPrecDType_Slots[] = -{ - {NPY_DT_ensure_canonical, &ensure_canonical}, - {NPY_DT_common_instance, &common_instance}, - {NPY_DT_common_dtype, &common_dtype}, - {NPY_DT_discover_descr_from_pyobject, &quadprec_discover_descriptor_from_pyobject}, - {NPY_DT_setitem, &quadprec_setitem}, - {NPY_DT_getitem, &quadprec_getitem}, - {NPY_DT_default_descr, &quadprec_default_descr}, - {0, NULL} -}; - - -static PyObject * QuadPrecDType_new(PyTypeObject *NPY_UNUSED(cls), PyObject *args, PyObject *kwds) -{ - if (PyTuple_GET_SIZE(args) != 0 || (kwds != NULL && PyDict_Size(kwds) != 0)) { - PyErr_SetString(PyExc_TypeError, - "QuadPrecDType takes no arguments"); - return NULL; - } - - return (PyObject *)new_quaddtype_instance(); -} - -static PyObject * QuadPrecDType_repr(QuadPrecDTypeObject *self) -{ - return PyUnicode_FromString("QuadPrecDType()"); -} - -PyArray_DTypeMeta QuadPrecDType = { - {{ - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "QuadPrecDType.QuadPrecDType", - .tp_basicsize = sizeof(QuadPrecDTypeObject), - .tp_new = QuadPrecDType_new, - .tp_repr = (reprfunc)QuadPrecDType_repr, - .tp_str = (reprfunc)QuadPrecDType_repr, - }}, -}; - -int init_quadprec_dtype(void) -{ - PyArrayMethod_Spec **casts = init_casts(); - if (!casts) - return -1; - - PyArrayDTypeMeta_Spec QuadPrecDType_DTypeSpec = { - .flags = NPY_DT_NUMERIC, - .casts = casts, - .typeobj = &QuadPrecision_Type, - .slots = QuadPrecDType_Slots, - }; - - ((PyObject *)&QuadPrecDType)->ob_type = &PyArrayDTypeMeta_Type; - - ((PyTypeObject *)&QuadPrecDType)->tp_base = &PyArrayDescr_Type; - - if (PyType_Ready((PyTypeObject *)&QuadPrecDType) < 0) - { - return -1; - } - - if (PyArrayInitDTypeMeta_FromSpec(&QuadPrecDType, &QuadPrecDType_DTypeSpec) < 0) - { - return -1; - } - - free_casts(); - - return 0; -} \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/dtype.h b/quaddtype2/quaddtype/src/dtype.h deleted file mode 100644 index e935063f..00000000 --- a/quaddtype2/quaddtype/src/dtype.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _QUADDTYPE_DTYPE_H -#define _QUADDTYPE_DTYPE_H - -#include -#include -#include -#include - -#include "scalar.h" - -typedef struct -{ - PyArray_Descr base; - -} QuadPrecDTypeObject; - -extern PyArray_DTypeMeta QuadPrecDType; - -QuadPrecDTypeObject * new_quaddtype_instance(void); - -int init_quadprec_dtype(void); - -#endif \ No newline at end of file diff --git a/quaddtype2/quaddtype/src/quaddtype_main.c b/quaddtype2/quaddtype/src/quaddtype_main.c deleted file mode 100644 index ed0778e1..00000000 --- a/quaddtype2/quaddtype/src/quaddtype_main.c +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API -#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API -#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION -#define NPY_TARGET_VERSION NPY_2_0_API_VERSION - -#include "numpy/arrayobject.h" -#include "numpy/dtype_api.h" - -#include "dtype.h" - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - .m_name = "_quaddtype_main", - .m_doc = "Quad (128-bit) floating point Data Type for Numpy", - .m_size = -1, -}; - -PyMODINIT_FUNC -PyInit__quaddtype_main(void) -{ - import_array(); - PyObject *m = PyModule_Create(&moduledef); - if (!m) - { - return NULL; - } - - if (init_quadprecision_scalar() < 0) - goto error; - - if(PyModule_AddObject(m, "QuadPrecision", (PyObject *)&QuadPrecision_Type) < 0) - goto error; - - if(init_quadprec_dtype() < 0) - goto error; - - if(PyModule_AddObject(m, "QuadPrecDType", (PyObject *)&QuadPrecDType) < 0) - goto error; - - return m; - - -error: - Py_DECREF(m); - return NULL; -} \ No newline at end of file diff --git a/quaddtype2/reinstall.sh b/quaddtype2/reinstall.sh deleted file mode 100755 index 3ed245d9..00000000 --- a/quaddtype2/reinstall.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -xeuo pipefail -IFS=$'\n\t' - -if [ -d "build/" ] -then - rm -r build -fi - -#meson setup build -Db_sanitize=address,undefined -meson setup build -python -m pip uninstall -y quaddtype -python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" -#python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' \ No newline at end of file From 64d1e9ec05e8969df24da6e989518510006c6236 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 00:48:23 +0530 Subject: [PATCH 13/43] adding quaddtype branch in CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b41a0f01..c17de6cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - quaddtype pull_request: jobs: From ae9d8b8e002ec0363e1cdbd1aee159a1c900d043 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 00:53:40 +0530 Subject: [PATCH 14/43] fixing sleefquad dep in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c17de6cf..264f094e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: run: | git clone https://github.com/shibatch/sleef.git cd sleef - cmake -S . -B build/ + cmake -S . -B -DSLEEF_BUILD_QUAD:BOOL=ON build/ cmake --build build/ --clean-first -j sudo cmake --install build/ --prefix /usr - name: Install quaddtype From 28c205b62f74d1147097de74c0c4d7c61ea784f3 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 00:57:41 +0530 Subject: [PATCH 15/43] fixing cmake sleefquad dep in CI --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 264f094e..e7667c56 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,9 +66,9 @@ jobs: run: | git clone https://github.com/shibatch/sleef.git cd sleef - cmake -S . -B -DSLEEF_BUILD_QUAD:BOOL=ON build/ + cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON cmake --build build/ --clean-first -j - sudo cmake --install build/ --prefix /usr + sudo cmake --install build --prefix /usr - name: Install quaddtype working-directory: quaddtype run: | From 7dd86117778ffac78a7db60f59c3273d711ca53b Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 01:04:17 +0530 Subject: [PATCH 16/43] fixing sleef compiling issue with FPIC --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e7667c56..0e8e8b54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,7 @@ jobs: - name: Install quaddtype working-directory: quaddtype run: | - python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" + CFLAGS="-fPIC" CXXFLAGS="-fPIC" python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" - name: Run quaddtype tests working-directory: quaddtype run: | From d0da3e51caa5247f5cb2bf726af181c92d9377d5 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 01:21:16 +0530 Subject: [PATCH 17/43] adding Build position-independent --- quaddtype/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype/meson.build b/quaddtype/meson.build index d50c93c9..644bcbbf 100644 --- a/quaddtype/meson.build +++ b/quaddtype/meson.build @@ -1,4 +1,4 @@ -project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17']) +project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17'], b_pie=true) py_mod = import('python') py = py_mod.find_installation() From 9077bec76340a91dde077b7d035fe912ed75d193 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 01:26:39 +0530 Subject: [PATCH 18/43] adding Build position-independent --- quaddtype/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype/meson.build b/quaddtype/meson.build index 644bcbbf..483ff58f 100644 --- a/quaddtype/meson.build +++ b/quaddtype/meson.build @@ -1,4 +1,4 @@ -project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17'], b_pie=true) +project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17'], b_pie : true) py_mod = import('python') py = py_mod.find_installation() From 982fba2d29d1dd2c04915717eebaa27f57462b26 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 01:38:06 +0530 Subject: [PATCH 19/43] adding Build position-independent --- quaddtype/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype/meson.build b/quaddtype/meson.build index 483ff58f..6e07fb26 100644 --- a/quaddtype/meson.build +++ b/quaddtype/meson.build @@ -1,4 +1,4 @@ -project('quaddtype2', 'c', 'cpp', default_options : ['cpp_std=c++17'], b_pie : true) +project('quaddtype', 'c', 'cpp', default_options : ['cpp_std=c++17', 'b_pie=true']) py_mod = import('python') py = py_mod.find_installation() From 5a3ae9481aa803c95cfeeb14d04671b50e40dec5 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 01:48:21 +0530 Subject: [PATCH 20/43] adding Build position-independent --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e8e8b54..bf7341b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: run: | git clone https://github.com/shibatch/sleef.git cd sleef - cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON + cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build/ --clean-first -j sudo cmake --install build --prefix /usr - name: Install quaddtype From 18b537a239a81540002869b333df2f36befb944f Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 01:56:02 +0530 Subject: [PATCH 21/43] fixing tests --- quaddtype/{quaddtype => }/tests/test.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename quaddtype/{quaddtype => }/tests/test.py (100%) diff --git a/quaddtype/quaddtype/tests/test.py b/quaddtype/tests/test.py similarity index 100% rename from quaddtype/quaddtype/tests/test.py rename to quaddtype/tests/test.py From f53c1308367f0f011dea7af32f4bc7603879d0da Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 02:02:07 +0530 Subject: [PATCH 22/43] fixing test namings --- quaddtype/tests/{test.py => test_arr.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename quaddtype/tests/{test.py => test_arr.py} (91%) diff --git a/quaddtype/tests/test.py b/quaddtype/tests/test_arr.py similarity index 91% rename from quaddtype/tests/test.py rename to quaddtype/tests/test_arr.py index 329f2507..4d57dd67 100644 --- a/quaddtype/tests/test.py +++ b/quaddtype/tests/test_arr.py @@ -4,6 +4,6 @@ from quaddtype import QuadPrecDType, QuadPrecision -def test(): +def test_dtype(): a = QuadPrecision("1.63") assert f"{np.array([a], dtype=QuadPrecDType).dtype}" == "QuadPrecDType()" From 77e9967fcd6977eac6e18bdf16780bfd0d794707 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 02:53:59 +0530 Subject: [PATCH 23/43] debugging sleef installation issues --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf7341b7..efe3398c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,10 @@ jobs: working-directory: quaddtype run: | CFLAGS="-fPIC" CXXFLAGS="-fPIC" python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" + - name: Check SLEEF installation + run: | + ls -l /usr/local/lib | grep sleef + ls -l /usr/local/include | grep sleef - name: Run quaddtype tests working-directory: quaddtype run: | From 375a621e8922f8745d6800fad67df5930ab24263 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 03:07:52 +0530 Subject: [PATCH 24/43] debugging sleef installation issues --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efe3398c..5db8fcd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: run: | git clone https://github.com/shibatch/sleef.git cd sleef - cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON + cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON -DSLEEF_BUILD_SHARED_LIBS:BOOL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build/ --clean-first -j sudo cmake --install build --prefix /usr - name: Install quaddtype From 820588d43c49cb616c5eaf532dd39048ba0c2f32 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 03:08:24 +0530 Subject: [PATCH 25/43] fixing sleef linking issues --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5db8fcd0..fe84bf44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,10 +73,6 @@ jobs: working-directory: quaddtype run: | CFLAGS="-fPIC" CXXFLAGS="-fPIC" python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" - - name: Check SLEEF installation - run: | - ls -l /usr/local/lib | grep sleef - ls -l /usr/local/include | grep sleef - name: Run quaddtype tests working-directory: quaddtype run: | From 53396dbaa58a45e961de7c2574764f02c0fc6ed9 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 04:35:58 +0530 Subject: [PATCH 26/43] fixing sleef linking issues --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe84bf44..86ee6d17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,8 @@ jobs: - name: Install quaddtype working-directory: quaddtype run: | - CFLAGS="-fPIC" CXXFLAGS="-fPIC" python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" + python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" + find ./dist/*.whl | xargs python -m pip install - name: Run quaddtype tests working-directory: quaddtype run: | From e91104e77eaf174e051f7ad43b9e262a1f90f404 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 04:44:26 +0530 Subject: [PATCH 27/43] fixing sleef linking issues --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86ee6d17..e6583649 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,11 +69,14 @@ jobs: cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON -DSLEEF_BUILD_SHARED_LIBS:BOOL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build/ --clean-first -j sudo cmake --install build --prefix /usr + - name: Check Sleef installation + working-directory: quaddtype + run: | + find / -name libsleefquad.3.dylib 2>/dev/null - name: Install quaddtype working-directory: quaddtype run: | python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" - find ./dist/*.whl | xargs python -m pip install - name: Run quaddtype tests working-directory: quaddtype run: | From 9639628e21503755b4e9c64b6e62be60da7361d8 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 04:51:05 +0530 Subject: [PATCH 28/43] fixing sleef linking issues --- .github/workflows/ci.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6583649..0e332fad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,14 +69,10 @@ jobs: cmake -S . -B build -DSLEEF_BUILD_QUAD:BOOL=ON -DSLEEF_BUILD_SHARED_LIBS:BOOL=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build/ --clean-first -j sudo cmake --install build --prefix /usr - - name: Check Sleef installation - working-directory: quaddtype - run: | - find / -name libsleefquad.3.dylib 2>/dev/null - name: Install quaddtype working-directory: quaddtype run: | - python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" + LDFLAGS="-Wl,-rpath,/usr/lib" python -m pip install . -v --no-build-isolation -Cbuilddir=build -C'compile-args=-v' -Csetup-args="-Dbuildtype=debug" - name: Run quaddtype tests working-directory: quaddtype run: | From bef1cc2614bb40274fe8585b150d4d35ffe7d6ae Mon Sep 17 00:00:00 2001 From: Swayam <74960567+SwayamInSync@users.noreply.github.com> Date: Thu, 8 Aug 2024 05:17:39 +0530 Subject: [PATCH 29/43] Update CI branch --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e332fad..c6dd4952 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,6 @@ on: push: branches: - main - - quaddtype pull_request: jobs: From 4f242fd8808ef8d463bc8ad039740217112a790d Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 8 Aug 2024 20:08:14 +0530 Subject: [PATCH 30/43] fixing pandas meson==0.13.1 dependency issue as --no-build-isolation --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6dd4952..f5af0668 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: python -m pip install -U pip build pytest unyt wheel meson ninja meson-python patchelf pip uninstall -y numpy pip install -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy - pip install --no-deps -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple pandas + pip install --no-deps --no-build-isolation -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple pandas - name: Install asciidtype working-directory: asciidtype run: | From 96ee075cdc6058d0363612b678c22433a4544cf7 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 9 Aug 2024 19:20:09 +0530 Subject: [PATCH 31/43] adding ufuncs --- quaddtype/meson.build | 2 +- quaddtype/quaddtype/src/ops.hpp | 33 +++ quaddtype/quaddtype/src/umath.cpp | 224 ++++++++++++++++++ quaddtype/quaddtype/src/umath.h | 15 ++ .../tests/{test_arr.py => test_quaddtype.py} | 0 temp.py | 111 +++++++++ 6 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 quaddtype/quaddtype/src/ops.hpp create mode 100644 quaddtype/quaddtype/src/umath.cpp create mode 100644 quaddtype/quaddtype/src/umath.h rename quaddtype/tests/{test_arr.py => test_quaddtype.py} (100%) create mode 100644 temp.py diff --git a/quaddtype/meson.build b/quaddtype/meson.build index 6e07fb26..01ae59e3 100644 --- a/quaddtype/meson.build +++ b/quaddtype/meson.build @@ -5,7 +5,7 @@ py = py_mod.find_installation() c = meson.get_compiler('c') -sleef_dep = c.find_library('sleef') +sleef_dep = c.find_library('sleef', dirs:['/usr/local/lib']) sleefquad_dep = c.find_library('sleefquad') incdir_numpy = run_command(py, diff --git a/quaddtype/quaddtype/src/ops.hpp b/quaddtype/quaddtype/src/ops.hpp new file mode 100644 index 00000000..bf5877d3 --- /dev/null +++ b/quaddtype/quaddtype/src/ops.hpp @@ -0,0 +1,33 @@ +#include +#include + +typedef int (*unary_op_def)(Sleef_quad *, Sleef_quad *); + +static inline int +quad_negative(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_negq1(*op); + return 0; +} + +static inline int +quad_absolute(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_fabsq1(*op); + return 0; +} + +// binary ops +typedef int (*binop_def)(Sleef_quad *, Sleef_quad *, Sleef_quad *); + +static inline int quad_add(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) +{ + *out = Sleef_addq1_u05(*in1, *in2); + return 0; +} + +static inline int quad_sub(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) +{ + *out = Sleef_subq1_u05(*in1, *in2); + return 0; +} \ No newline at end of file diff --git a/quaddtype/quaddtype/src/umath.cpp b/quaddtype/quaddtype/src/umath.cpp new file mode 100644 index 00000000..329b2cfd --- /dev/null +++ b/quaddtype/quaddtype/src/umath.cpp @@ -0,0 +1,224 @@ +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API +#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NPY_TARGET_VERSION NPY_2_0_API_VERSION +#define NO_IMPORT_ARRAY +#define NO_IMPORT_UFUNC + +extern "C" { + #include + + #include "numpy/arrayobject.h" + #include "numpy/ndarraytypes.h" + #include "numpy/ufuncobject.h" + + #include "numpy/dtype_api.h" +} + +#include "scalar.h" +#include "dtype.h" +#include "umath.h" +#include "ops.hpp" + +template +int quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *auxdata) +{ + npy_intp N = dimensions[0]; + char *in_ptr = data[0]; + char *out_ptr = data[1]; + npy_intp in_stride = strides[0]; + npy_intp out_stride = strides[1]; + + while (N--) + { + unary_op((Sleef_quad *)in_ptr, (Sleef_quad *)out_ptr); + in_ptr += in_stride; + out_ptr += out_stride; + } + return 0; +} + +static NPY_CASTING +quad_unary_op_resolve_descriptors(PyObject *self, + PyArray_DTypeMeta *dtypes[], QuadPrecDTypeObject *given_descrs[], + QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + if (given_descrs[1] == NULL) { + Py_INCREF(given_descrs[0]); + loop_descrs[1] = given_descrs[0]; + return NPY_NO_CASTING; + } + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + + return NPY_NO_CASTING; // Quad precision is always the same precision +} + +template +int create_quad_unary_ufunc(PyObject *numpy, const char *ufunc_name) +{ + PyObject *ufunc = PyObject_GetAttrString(numpy, ufunc_name); + if (ufunc == NULL) { + return -1; + } + + PyArray_DTypeMeta *dtypes[2] = { + &QuadPrecDType, &QuadPrecDType}; + + PyType_Slot slots[] = { + {NPY_METH_resolve_descriptors, (void *)&quad_unary_op_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_generic_unary_op_strided_loop}, + {0, NULL} + }; + + PyArrayMethod_Spec Spec = { + .name = "quad_unary_op", + .nin = 1, + .nout = 1, + .casting = NPY_NO_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, + }; + + if (PyUFunc_AddLoopFromSpec(ufunc, &Spec) < 0) { + return -1; + } + + return 0; +} + +int init_quad_unary_ops(PyObject *numpy) +{ + if (create_quad_unary_ufunc(numpy, "negative") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "absolute") < 0) { + return -1; + } + return 0; +} + +// Binary ufuncs + +template +int quad_generic_binop_strided_loop(PyArrayMethod_Context *context, + char *const data[], npy_intp const dimensions[], + npy_intp const strides[], NpyAuxData *auxdata) +{ + npy_intp N = dimensions[0]; + char *in1_ptr = data[0], *in2_ptr = data[1]; + char *out_ptr = data[2]; + npy_intp in1_stride = strides[0]; + npy_intp in2_stride = strides[1]; + npy_intp out_stride = strides[2]; + + while (N--) { + binop((Sleef_quad *)out_ptr, (Sleef_quad *)in1_ptr, (Sleef_quad *)in2_ptr); + + in1_ptr += in1_stride; + in2_ptr += in2_stride; + out_ptr += out_stride; + } + return 0; +} + +static NPY_CASTING +quad_binary_op_resolve_descriptors(PyObject *self, + PyArray_DTypeMeta *dtypes[], QuadPrecDTypeObject *given_descrs[], + QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + + if (given_descrs[2] == NULL) { + Py_INCREF(given_descrs[0]); + loop_descrs[2] = given_descrs[0]; + } + else { + Py_INCREF(given_descrs[2]); + loop_descrs[2] = given_descrs[2]; + } + + return NPY_NO_CASTING; // Quad precision is always the same precision +} + +// todo: skipping the promoter for now, since same type operation will be requried + +template +int create_quad_binary_ufunc(PyObject *numpy, const char *ufunc_name) +{ + PyObject *ufunc = PyObject_GetAttrString(numpy, ufunc_name); + if (ufunc == NULL) { + return -1; + } + + PyArray_DTypeMeta *dtypes[3] = { + &QuadPrecDType, &QuadPrecDType, &QuadPrecDType}; + + PyType_Slot slots[] = { + {NPY_METH_resolve_descriptors, + (void *)&quad_binary_op_resolve_descriptors}, + {NPY_METH_strided_loop, + (void *)&quad_generic_binop_strided_loop}, + {0, NULL} + }; + + PyArrayMethod_Spec Spec = { + .name = "quad_binop", + .nin = 2, + .nout = 1, + .casting = NPY_NO_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, + }; + + if (PyUFunc_AddLoopFromSpec(ufunc, &Spec) < 0) { + return -1; + } + + return 0; +} + +int init_quad_binary_ops(PyObject *numpy) +{ + if (create_quad_binary_ufunc(numpy, "add") < 0) { + return -1; + } + if (create_quad_binary_ufunc(numpy, "subtract") < 0) { + return -1; + } + + return 0; +} + +int init_quad_umath(void) +{ + PyObject * numpy = PyImport_ImportModule("numpy"); + if (!numpy) + return -1; + + if (init_quad_unary_ops(numpy) < 0) { + goto err; + } + + if (init_quad_binary_ops(numpy) < 0) { + goto err; + } + + Py_DECREF(numpy); + return 0; + + err: + Py_DECREF(numpy); + return -1; + +} \ No newline at end of file diff --git a/quaddtype/quaddtype/src/umath.h b/quaddtype/quaddtype/src/umath.h new file mode 100644 index 00000000..d64f26be --- /dev/null +++ b/quaddtype/quaddtype/src/umath.h @@ -0,0 +1,15 @@ +#ifndef _QUADDTYPE_UMATH_H +#define _QUADDTYPE_UMATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +int +init_quad_umath(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/quaddtype/tests/test_arr.py b/quaddtype/tests/test_quaddtype.py similarity index 100% rename from quaddtype/tests/test_arr.py rename to quaddtype/tests/test_quaddtype.py diff --git a/temp.py b/temp.py new file mode 100644 index 00000000..80716e41 --- /dev/null +++ b/temp.py @@ -0,0 +1,111 @@ +import numpy as np +from quaddtype import QuadPrecDType, QuadPrecision +import matplotlib.pyplot as plt + +def get_color(t, interior_t): + epsilon = QuadPrecision("1e-10") + + if abs(t - QuadPrecision(1.0)) < epsilon: + value = int(255 * float(interior_t)) + return np.array([value, value, value], dtype=np.uint8) + + t = np.power(t, 0.5) + t = np.mod(t * 20, 1.0) + + if t < 0.16: + return np.array([0, int(255 * (t / 0.16)), int(128 + 127 * (t / 0.16))], dtype=np.uint8) + elif t < 0.33: + return np.array([0, 255, int(255 * (1 - (t - 0.16) / 0.17))], dtype=np.uint8) + elif t < 0.5: + return np.array([int(255 * ((t - 0.33) / 0.17)), 255, 0], dtype=np.uint8) + elif t < 0.66: + return np.array([255, int(255 * (1 - (t - 0.5) / 0.16)), 0], dtype=np.uint8) + elif t < 0.83: + return np.array([255, 0, int(255 * ((t - 0.66) / 0.17))], dtype=np.uint8) + else: + return np.array([int(255 * (1 - (t - 0.83) / 0.17)), 0, int(128 * ((t - 0.83) / 0.17))], dtype=np.uint8) + +def iterate_and_compute_derivatives(c, max_iter): + z = 0 + dz = 1 + dc = 0 + dzdz = 0 + + for _ in range(max_iter): + dzdz = 2 * (z * dzdz + dz * dz) + dz = 2 * z * dz + dc + z = z * z + c + dc = 1 + + return z, dz, dc, dzdz + +def estimate_interior_distance(c, max_iter): + z, dz, dc, dzdz = iterate_and_compute_derivatives(c, max_iter) + + dz_abs_sq = np.abs(dz) ** 2 + numerator = 1 - dz_abs_sq + + denominator = np.abs(dc * dz + dzdz * z * dc) + + return numerator / denominator + +def mandelbrot(c, max_iter, radius2): + z = 0 + for i in range(max_iter): + z = z * z + c + if np.abs(z) ** 2 > radius2: + log_zn = np.log(np.abs(z)) + nu = np.log(log_zn / np.log(2)) / np.log(2) + return i + 1 - nu, z + return max_iter, z + +def mandelbrot_set(width, height, max_iter, center_r, center_i, zoom): + radius = 2.0 + radius2 = radius * radius + zoom_q = 1 / zoom + + x = np.linspace(center_r - radius / zoom, center_r + radius / zoom, width) + y = np.linspace(center_i - radius / zoom, center_i + radius / zoom, height) + c = x[np.newaxis, :] + 1j * y[:, np.newaxis] + + smooth_iter, final_z = np.frompyfunc(lambda c: mandelbrot(c, max_iter, radius2), 1, 2)(c) + smooth_iter = smooth_iter.astype(np.float64) + final_z = final_z.astype(np.complex128) + + img = np.zeros((height, width, 3), dtype=np.uint8) + + interior_mask = smooth_iter == max_iter + interior_c = c[interior_mask] + interior_distance = np.frompyfunc(lambda c: estimate_interior_distance(c, max_iter), 1, 1)(interior_c) + interior_distance = interior_distance.astype(np.float64) + interior_t = interior_distance - np.floor(interior_distance) + + exterior_mask = ~interior_mask + t = smooth_iter[exterior_mask] / max_iter + + interior_colors = np.array(list(map(lambda t: get_color(1.0, t), interior_t))) + exterior_colors = np.array(list(map(lambda t: get_color(t, 0.0), t))) + + img[interior_mask] = interior_colors + img[exterior_mask] = exterior_colors + + return img + +def plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom): + img_array = mandelbrot_set(width, height, max_iter, center_r, center_i, zoom) + + plt.figure(figsize=(10, 10)) + plt.imshow(img_array) + plt.axis('off') + plt.title(f'Mandelbrot Set (zoom: {zoom}, center: {center_r} + {center_i}i, iterations: {max_iter}, dtype: numpy.float64)') + plt.show() + +if __name__ == "__main__": + width = 800 + height = 800 + max_iter = 1000 + center_r = -0.75 + center_i = 0.0 + zoom = 1.0 + + plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom) \ No newline at end of file From 7f7ebc39fa8e3afbb5bcd39ee133d6f9e71932a5 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Sat, 10 Aug 2024 23:11:53 +0530 Subject: [PATCH 32/43] added ufuncs, castings and scalar ops --- mandelbrot.py | 148 ++++++++++ quaddtype/quaddtype/src/casts.cpp | 344 ++++++++++++++++++++++- quaddtype/quaddtype/src/dtype.h | 7 + quaddtype/quaddtype/src/ops.hpp | 150 +++++++++- quaddtype/quaddtype/src/quaddtype_main.c | 7 + quaddtype/quaddtype/src/scalar.c | 9 +- quaddtype/quaddtype/src/scalar.h | 22 +- quaddtype/quaddtype/src/scalar_ops.cpp | 171 +++++++++++ quaddtype/quaddtype/src/scalar_ops.h | 20 ++ quaddtype/quaddtype/src/umath.cpp | 245 ++++++++++++---- 10 files changed, 1052 insertions(+), 71 deletions(-) create mode 100644 mandelbrot.py create mode 100644 quaddtype/quaddtype/src/scalar_ops.cpp create mode 100644 quaddtype/quaddtype/src/scalar_ops.h diff --git a/mandelbrot.py b/mandelbrot.py new file mode 100644 index 00000000..f0134f30 --- /dev/null +++ b/mandelbrot.py @@ -0,0 +1,148 @@ +import numpy as np +from quaddtype import QuadPrecDType, QuadPrecision +import matplotlib.pyplot as plt + + +def get_color(t, interior_t): + epsilon = QuadPrecision("1e-10") + + if abs(t - QuadPrecision(1.0)) < epsilon: + value = int(255 * interior_t) + return np.array([value, value, value], dtype=np.uint8) + + t = np.power(t, 0.5) + t = np.mod(t * 20, 1.0) + + if t < 0.16: + return np.array([0, int(255 * (t / 0.16)), int(128 + 127 * (t / 0.16))], dtype=np.uint8) + elif t < 0.33: + return np.array([0, 255, int(255 * (1 - (t - 0.16) / 0.17))], dtype=np.uint8) + elif t < 0.5: + return np.array([int(255 * ((t - 0.33) / 0.17)), 255, 0], dtype=np.uint8) + elif t < 0.66: + return np.array([255, int(255 * (1 - (t - 0.5) / 0.16)), 0], dtype=np.uint8) + elif t < 0.83: + return np.array([255, 0, int(255 * ((t - 0.66) / 0.17))], dtype=np.uint8) + else: + return np.array([int(255 * (1 - (t - 0.83) / 0.17)), 0, int(128 * ((t - 0.83) / 0.17))], dtype=np.uint8) + + +def iterate_and_compute_derivatives(cr, ci, max_iter): + zr, zi = 0.0, 0.0 + dzr, dzi = 1.0, 0.0 + dcr, dci = 0.0, 0.0 + dzdzr, dzdzi = 0.0, 0.0 + + for _ in range(max_iter): + dzdzr_new = 2 * (zr * dzdzr - zi * dzdzi + dzr * dzr - dzi * dzi) + dzdzi_new = 2 * (zr * dzdzi + zi * dzdzr + 2 * dzr * dzi) + dzr_new = 2 * (zr * dzr - zi * dzi) + dcr + dzi_new = 2 * (zr * dzi + zi * dzr) + dci + zr_new = zr * zr - zi * zi + cr + zi_new = 2 * zr * zi + ci + + dzdzr, dzdzi = dzdzr_new, dzdzi_new + dzr, dzi = dzr_new, dzi_new + zr, zi = zr_new, zi_new + dcr, dci = 1.0, 0.0 + + return zr, zi, dzr, dzi, dcr, dci, dzdzr, dzdzi + + +def estimate_interior_distance(cr, ci, max_iter): + zr, zi, dzr, dzi, dcr, dci, dzdzr, dzdzi = iterate_and_compute_derivatives( + cr, ci, max_iter) + + dz_abs_sq = dzr * dzr + dzi * dzi + numerator = 1 - dz_abs_sq + + denominator = np.abs((dcr * dzr + dci * dzi) + + (dzdzr * zr + dzdzi * zi) * dcr) + + return numerator / denominator + + +def mandelbrot(cr, ci, max_iter, radius2): + zr, zi = QuadPrecision(0.0), QuadPrecision(0.0) + for i in range(max_iter): + zr_new = zr * zr - zi * zi + cr + zi_new = QuadPrecision(2) * zr * zi + ci + zr, zi = zr_new, zi_new + if zr * zr + zi * zi > radius2: + log_zn = np.log(zr * zr + zi * zi) / QuadPrecision(2) + nu = np.log(log_zn / np.log(QuadPrecision(2))) / \ + np.log(QuadPrecision(2)) + return i + QuadPrecision(1) - nu, zr, zi + return max_iter, zr, zi + + +def mandelbrot_set(width, height, max_iter, center_r, center_i, zoom): + radius = QuadPrecision(2.0) + radius2 = radius * radius + zoom = 1 / zoom + + x = np.linspace(np.float64(center_r - radius / zoom), + np.float64(center_r + radius / zoom), + width) + y = np.linspace(np.float64(center_i - radius / zoom), + np.float64(center_i + radius / zoom), + height) + cr, ci = np.meshgrid(x, y) + + smooth_iter = np.zeros((height, width), dtype=QuadPrecDType) + final_zr = np.zeros((height, width), dtype=QuadPrecDType) + final_zi = np.zeros((height, width), dtype=QuadPrecDType) + + for i in range(height): + for j in range(width): + smooth_iter[i, j], final_zr[i, j], final_zi[i, j] = mandelbrot( + cr[i, j], ci[i, j], max_iter, radius2) + + img = np.zeros((height, width, 3), dtype=np.uint8) + + interior_mask = smooth_iter == QuadPrecision(max_iter) + interior_cr = cr[interior_mask] + interior_ci = ci[interior_mask] + interior_distance = np.array([estimate_interior_distance( + cr, ci, max_iter) for cr, ci in zip(interior_cr, interior_ci)]) + interior_t = (interior_distance - + np.floor(interior_distance)).astype(np.float64) + + exterior_mask = ~interior_mask + t = smooth_iter[exterior_mask] / QuadPrecision(max_iter) + + interior_colors = np.array( + [get_color(QuadPrecision(1.0), t) for t in interior_t]) + exterior_colors = np.array([get_color(t, QuadPrecision(0.0)) for t in t]) + + img[interior_mask] = interior_colors + img[exterior_mask] = exterior_colors + + return img + + +def plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom): + center_rq = QuadPrecision(center_r) + center_iq = QuadPrecision(center_i) + zoom_q = QuadPrecision(zoom) + + img_array = mandelbrot_set( + width, height, max_iter, center_rq, center_iq, zoom_q) + + plt.figure(figsize=(10, 10)) + plt.imshow(img_array) + plt.axis('off') + plt.title( + f'Mandelbrot Set (zoom: {zoom}, center: {center_r} + {center_i}i, iterations: {max_iter}, dtype: numpy.float64)') + plt.show() + + +if __name__ == "__main__": + width = 800 + height = 800 + max_iter = 1000 + center_r = -0.75 + center_i = 0.0 + zoom = 1.0 + + plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom) diff --git a/quaddtype/quaddtype/src/casts.cpp b/quaddtype/quaddtype/src/casts.cpp index 6b5b042a..3fbd8502 100644 --- a/quaddtype/quaddtype/src/casts.cpp +++ b/quaddtype/quaddtype/src/casts.cpp @@ -5,14 +5,16 @@ #define NO_IMPORT_ARRAY #define NO_IMPORT_UFUNC +extern "C" { #include -#include -#include -#include #include "numpy/arrayobject.h" #include "numpy/ndarraytypes.h" #include "numpy/dtype_api.h" +} +#include "sleef.h" +#include "sleefquad.h" +#include #include "scalar.h" #include "casts.h" @@ -61,8 +63,313 @@ quad_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[], return 0; } +// Casting from other types to QuadDType + +template +static inline Sleef_quad +to_quad(T x); + +template <> +inline Sleef_quad +to_quad(npy_bool x) +{ + return x ? Sleef_cast_from_doubleq1(1.0) : Sleef_cast_from_doubleq1(0.0); +} +template <> +inline Sleef_quad +to_quad(npy_byte x) +{ + return Sleef_cast_from_int64q1(x); +} +// template <> +// inline Sleef_quad +// to_quad(npy_ubyte x) +// { +// return Sleef_cast_from_uint64q1(x); +// } +template <> +inline Sleef_quad +to_quad(npy_short x) +{ + return Sleef_cast_from_int64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_ushort x) +{ + return Sleef_cast_from_uint64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_int x) +{ + return Sleef_cast_from_int64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_uint x) +{ + return Sleef_cast_from_uint64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_long x) +{ + return Sleef_cast_from_int64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_ulong x) +{ + return Sleef_cast_from_uint64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_longlong x) +{ + return Sleef_cast_from_int64q1(x); +} +template <> +inline Sleef_quad +to_quad(npy_ulonglong x) +{ + return Sleef_cast_from_uint64q1(x); +} +template <> +inline Sleef_quad +to_quad(float x) +{ + return Sleef_cast_from_doubleq1(x); +} +template <> +inline Sleef_quad +to_quad(double x) +{ + return Sleef_cast_from_doubleq1(x); +} +template <> +inline Sleef_quad +to_quad(long double x) +{ + return Sleef_cast_from_doubleq1(x); +} + +template +static NPY_CASTING +numpy_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], PyArray_Descr *loop_descrs[2], + npy_intp *view_offset) +{ + if (given_descrs[1] == NULL) { + loop_descrs[1] = (PyArray_Descr *)new_quaddtype_instance(); + if (loop_descrs[1] == nullptr) { + return (NPY_CASTING)-1; + } + } + else { + Py_INCREF(given_descrs[1]); + loop_descrs[1] = given_descrs[1]; + } + + loop_descrs[0] = PyArray_GetDefaultDescr(dtypes[0]); + *view_offset = 0; + return NPY_SAFE_CASTING; +} + +template +static int +numpy_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[], + npy_intp const dimensions[], npy_intp const strides[], + void *NPY_UNUSED(auxdata)) +{ + npy_intp N = dimensions[0]; + char *in_ptr = data[0]; + char *out_ptr = data[1]; + + while (N--) { + T in_val = *(T *)in_ptr; + Sleef_quad *out_val = (Sleef_quad *)out_ptr; + *out_val = to_quad(in_val); + + in_ptr += strides[0]; + out_ptr += strides[1]; + } + return 0; +} + +// Casting from QuadDType to other types + +template +static inline T +from_quad(Sleef_quad x); + +template <> +inline npy_bool +from_quad(Sleef_quad x) +{ + return Sleef_cast_to_int64q1(x) != 0; +} +template <> +inline npy_byte +from_quad(Sleef_quad x) +{ + return (npy_byte)Sleef_cast_to_int64q1(x); +} +// template <> +// inline npy_ubyte +// from_quad(Sleef_quad x) +// { +// return (npy_ubyte)Sleef_cast_to_uint64q1(x); +// } +template <> +inline npy_short +from_quad(Sleef_quad x) +{ + return (npy_short)Sleef_cast_to_int64q1(x); +} +template <> +inline npy_ushort +from_quad(Sleef_quad x) +{ + return (npy_ushort)Sleef_cast_to_uint64q1(x); +} +template <> +inline npy_int +from_quad(Sleef_quad x) +{ + return (npy_int)Sleef_cast_to_int64q1(x); +} +template <> +inline npy_uint +from_quad(Sleef_quad x) +{ + return (npy_uint)Sleef_cast_to_uint64q1(x); +} +template <> +inline npy_long +from_quad(Sleef_quad x) +{ + return (npy_long)Sleef_cast_to_int64q1(x); +} +template <> +inline npy_ulong +from_quad(Sleef_quad x) +{ + return (npy_ulong)Sleef_cast_to_uint64q1(x); +} +template <> +inline npy_longlong +from_quad(Sleef_quad x) +{ + return Sleef_cast_to_int64q1(x); +} +template <> +inline npy_ulonglong +from_quad(Sleef_quad x) +{ + return Sleef_cast_to_uint64q1(x); +} +template <> +inline float +from_quad(Sleef_quad x) +{ + return Sleef_cast_to_doubleq1(x); +} +template <> +inline double +from_quad(Sleef_quad x) +{ + return Sleef_cast_to_doubleq1(x); +} +template <> +inline long double +from_quad(Sleef_quad x) +{ + return Sleef_cast_to_doubleq1(x); +} + +template +static NPY_CASTING +quad_to_numpy_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta *dtypes[2], + PyArray_Descr *given_descrs[2], PyArray_Descr *loop_descrs[2], + npy_intp *view_offset) +{ + Py_INCREF(given_descrs[0]); + loop_descrs[0] = given_descrs[0]; + + loop_descrs[1] = PyArray_GetDefaultDescr(dtypes[1]); + *view_offset = 0; + return NPY_SAME_KIND_CASTING; +} + +template +static int +quad_to_numpy_strided_loop(PyArrayMethod_Context *context, char *const data[], + npy_intp const dimensions[], npy_intp const strides[], + void *NPY_UNUSED(auxdata)) +{ + npy_intp N = dimensions[0]; + char *in_ptr = data[0]; + char *out_ptr = data[1]; + + while (N--) { + Sleef_quad in_val = *(Sleef_quad *)in_ptr; + T *out_val = (T *)out_ptr; + *out_val = from_quad(in_val); + + in_ptr += strides[0]; + out_ptr += strides[1]; + } + return 0; +} + static std::vector specs; +// functions to add casts +template +void +add_cast_from(PyArray_DTypeMeta *to) +{ + PyArray_DTypeMeta **dtypes = new PyArray_DTypeMeta *[2]{nullptr, to}; + + PyType_Slot *slots = new PyType_Slot[3]{ + {NPY_METH_resolve_descriptors, (void *)&quad_to_numpy_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_to_numpy_strided_loop}, + {0, nullptr}}; + + specs.push_back(new PyArrayMethod_Spec{ + .name = "cast_QuadPrec_to_NumPy", + .nin = 1, + .nout = 1, + .casting = NPY_SAME_KIND_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, + }); +} + +template +void +add_cast_to(PyArray_DTypeMeta *from) +{ + PyArray_DTypeMeta **dtypes = new PyArray_DTypeMeta *[2]{from, nullptr}; + + PyType_Slot *slots = new PyType_Slot[3]{ + {NPY_METH_resolve_descriptors, (void *)&numpy_to_quad_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&numpy_to_quad_strided_loop}, + {0, nullptr}}; + + specs.push_back(new PyArrayMethod_Spec{ + .name = "cast_NumPy_to_QuadPrec", + .nin = 1, + .nout = 1, + .casting = NPY_SAFE_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, + }); +} + PyArrayMethod_Spec ** init_casts_internal(void) { @@ -81,6 +388,37 @@ init_casts_internal(void) {NPY_METH_unaligned_strided_loop, (void *)&quad_to_quad_strided_loop}, {0, NULL}}}); + add_cast_to(&PyArray_BoolDType); + add_cast_to(&PyArray_ByteDType); + add_cast_to(&PyArray_UByteDType); + add_cast_to(&PyArray_ShortDType); + add_cast_to(&PyArray_UShortDType); + add_cast_to(&PyArray_IntDType); + add_cast_to(&PyArray_UIntDType); + add_cast_to(&PyArray_LongDType); + add_cast_to(&PyArray_ULongDType); + add_cast_to(&PyArray_LongLongDType); + add_cast_to(&PyArray_ULongLongDType); + add_cast_to(&PyArray_FloatDType); + add_cast_to(&PyArray_DoubleDType); + add_cast_to(&PyArray_LongDoubleDType); + + add_cast_from(&PyArray_BoolDType); + add_cast_from(&PyArray_ByteDType); + add_cast_from(&PyArray_UByteDType); + add_cast_from(&PyArray_ShortDType); + add_cast_from(&PyArray_UShortDType); + add_cast_from(&PyArray_IntDType); + add_cast_from(&PyArray_UIntDType); + add_cast_from(&PyArray_LongDType); + add_cast_from(&PyArray_ULongDType); + add_cast_from(&PyArray_LongLongDType); + add_cast_from(&PyArray_ULongLongDType); + add_cast_from(&PyArray_FloatDType); + add_cast_from(&PyArray_DoubleDType); + add_cast_from(&PyArray_LongDoubleDType); + + specs.push_back(nullptr); return specs.data(); } diff --git a/quaddtype/quaddtype/src/dtype.h b/quaddtype/quaddtype/src/dtype.h index e935063f..162d5714 100644 --- a/quaddtype/quaddtype/src/dtype.h +++ b/quaddtype/quaddtype/src/dtype.h @@ -1,5 +1,8 @@ #ifndef _QUADDTYPE_DTYPE_H #define _QUADDTYPE_DTYPE_H +#ifdef __cplusplus +extern "C" { +#endif #include #include @@ -20,4 +23,8 @@ QuadPrecDTypeObject * new_quaddtype_instance(void); int init_quadprec_dtype(void); +#ifdef __cplusplus +} +#endif + #endif \ No newline at end of file diff --git a/quaddtype/quaddtype/src/ops.hpp b/quaddtype/quaddtype/src/ops.hpp index bf5877d3..51f97564 100644 --- a/quaddtype/quaddtype/src/ops.hpp +++ b/quaddtype/quaddtype/src/ops.hpp @@ -17,17 +17,163 @@ quad_absolute(Sleef_quad *op, Sleef_quad *out) return 0; } +static inline int +quad_rint(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_rintq1(*op); + return 0; +} + +static inline int +quad_trunc(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_truncq1(*op); + return 0; +} + +static inline int +quad_floor(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_floorq1(*op); + return 0; +} + +static inline int +quad_ceil(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_ceilq1(*op); + return 0; +} + +static inline int +quad_sqrt(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_sqrtq1_u05(*op); + return 0; +} + +static inline int +quad_square(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_mulq1_u05(*op, *op); + return 0; +} + +static inline int +quad_log(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_logq1_u10(*op); + return 0; +} + +static inline int +quad_log2(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_log2q1_u10(*op); + return 0; +} + +static inline int +quad_log10(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_log10q1_u10(*op); + return 0; +} + +static inline int +quad_log1p(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_log1pq1_u10(*op); + return 0; +} + +static inline int +quad_exp(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_expq1_u10(*op); + return 0; +} + +static inline int +quad_exp2(Sleef_quad *op, Sleef_quad *out) +{ + *out = Sleef_exp2q1_u10(*op); + return 0; +} + // binary ops typedef int (*binop_def)(Sleef_quad *, Sleef_quad *, Sleef_quad *); -static inline int quad_add(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) +static inline int +quad_add(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) { *out = Sleef_addq1_u05(*in1, *in2); return 0; } -static inline int quad_sub(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) +static inline int +quad_sub(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) { *out = Sleef_subq1_u05(*in1, *in2); return 0; +} + +static inline int +quad_mul(Sleef_quad *res, Sleef_quad *a, Sleef_quad *b) +{ + *res = Sleef_mulq1_u05(*a, *b); + return 0; +} + +static inline int +quad_div(Sleef_quad *res, Sleef_quad *a, Sleef_quad *b) +{ + *res = Sleef_divq1_u05(*a, *b); + return 0; +} + +static inline int +quad_pow(Sleef_quad *res, Sleef_quad *a, Sleef_quad *b) +{ + *res = Sleef_powq1_u10(*a, *b); + return 0; +} + +// comparison functions +typedef npy_bool (*cmp_def)(const Sleef_quad *, const Sleef_quad *); + +static inline npy_bool +quad_equal(const Sleef_quad *a, const Sleef_quad *b) +{ + return Sleef_icmpeqq1(*a, *b); +} + +static inline npy_bool +quad_notequal(const Sleef_quad *a, const Sleef_quad *b) +{ + return Sleef_icmpneq1(*a, *b); +} + +static inline npy_bool +quad_less(const Sleef_quad *a, const Sleef_quad *b) +{ + return Sleef_icmpltq1(*a, *b); +} + +static inline npy_bool +quad_lessequal(const Sleef_quad *a, const Sleef_quad *b) +{ + return Sleef_icmpleq1(*a, *b); +} + +static inline npy_bool +quad_greater(const Sleef_quad *a, const Sleef_quad *b) +{ + return Sleef_icmpgtq1(*a, *b); +} + +static inline npy_bool +quad_greaterequal(const Sleef_quad *a, const Sleef_quad *b) +{ + return Sleef_icmpgeq1(*a, *b); } \ No newline at end of file diff --git a/quaddtype/quaddtype/src/quaddtype_main.c b/quaddtype/quaddtype/src/quaddtype_main.c index ed0778e1..098c0749 100644 --- a/quaddtype/quaddtype/src/quaddtype_main.c +++ b/quaddtype/quaddtype/src/quaddtype_main.c @@ -7,8 +7,10 @@ #include "numpy/arrayobject.h" #include "numpy/dtype_api.h" +#include "numpy/ufuncobject.h" #include "dtype.h" +#include "umath.h" static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, @@ -21,6 +23,7 @@ PyMODINIT_FUNC PyInit__quaddtype_main(void) { import_array(); + import_umath(); PyObject *m = PyModule_Create(&moduledef); if (!m) { @@ -39,6 +42,10 @@ PyInit__quaddtype_main(void) if(PyModule_AddObject(m, "QuadPrecDType", (PyObject *)&QuadPrecDType) < 0) goto error; + if (init_quad_umath() < 0) { + goto error; + } + return m; diff --git a/quaddtype/quaddtype/src/scalar.c b/quaddtype/quaddtype/src/scalar.c index b4fcc81a..668e90af 100644 --- a/quaddtype/quaddtype/src/scalar.c +++ b/quaddtype/quaddtype/src/scalar.c @@ -10,7 +10,8 @@ #include "numpy/ndarraytypes.h" #include "numpy/dtype_api.h" -#include"scalar.h" +#include "scalar.h" +#include "scalar_ops.h" // static PyTypeObject QuadPrecision_Type; @@ -46,6 +47,10 @@ QuadPrecisionObject * QuadPrecision_from_object(PyObject * value) return NULL; } } + else if(PyLong_Check(value)) + { + self->quad.value = Sleef_cast_from_int64q1(PyLong_AsLong(value)); + } else { PyErr_SetString(PyExc_TypeError, "QuadPrecision value must be a float or string"); @@ -95,6 +100,8 @@ PyTypeObject QuadPrecision_Type = .tp_new = QuadPrecision_new, .tp_repr = (reprfunc)QuadPrecision_repr, .tp_str = (reprfunc)QuadPrecision_str, + .tp_as_number = &quad_as_scalar, + .tp_richcompare = (richcmpfunc)quad_richcompare }; diff --git a/quaddtype/quaddtype/src/scalar.h b/quaddtype/quaddtype/src/scalar.h index 7ac39f31..962a1fec 100644 --- a/quaddtype/quaddtype/src/scalar.h +++ b/quaddtype/quaddtype/src/scalar.h @@ -1,27 +1,35 @@ #ifndef _QUADDTYPE_SCALAR_H #define _QUADDTYPE_SCALAR_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include -typedef struct -{ +typedef struct { Sleef_quad value; } quad_field; -typedef struct -{ +typedef struct { PyObject_HEAD quad_field quad; } QuadPrecisionObject; extern PyTypeObject QuadPrecision_Type; -QuadPrecisionObject * QuadPrecision_raw_new(void); +QuadPrecisionObject * +QuadPrecision_raw_new(void); -QuadPrecisionObject * QuadPrecision_from_object(PyObject * value); +QuadPrecisionObject * +QuadPrecision_from_object(PyObject *value); -int init_quadprecision_scalar(void); +int +init_quadprecision_scalar(void); +#ifdef __cplusplus +} +#endif #endif \ No newline at end of file diff --git a/quaddtype/quaddtype/src/scalar_ops.cpp b/quaddtype/quaddtype/src/scalar_ops.cpp new file mode 100644 index 00000000..fa83713b --- /dev/null +++ b/quaddtype/quaddtype/src/scalar_ops.cpp @@ -0,0 +1,171 @@ +#define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION +#define NPY_TARGET_VERSION NPY_2_0_API_VERSION +#define NO_IMPORT_ARRAY + +extern "C" { +#include + +#include "numpy/arrayobject.h" +#include "numpy/ndarraytypes.h" +#include "numpy/ufuncobject.h" + +#include "numpy/dtype_api.h" +} + +#include "scalar.h" +#include "ops.hpp" +#include "scalar_ops.h" + +template +static PyObject * +quad_unary_func(QuadPrecisionObject *self) +{ + QuadPrecisionObject *res = QuadPrecision_raw_new(); + if (!res) { + return NULL; + } + + unary_op(&self->quad.value, &res->quad.value); + return (PyObject *)res; +} + +PyObject * +quad_nonzero(QuadPrecisionObject *self) +{ + return PyBool_FromLong(Sleef_icmpneq1(self->quad.value, Sleef_cast_from_int64q1(0))); +} + +template +static PyObject * +quad_binary_func(PyObject *op1, PyObject *op2) +{ + QuadPrecisionObject *self; + PyObject *other; + QuadPrecisionObject *other_quad = NULL; + int is_forward; + + if (PyObject_TypeCheck(op1, &QuadPrecision_Type)) { + is_forward = 1; + self = (QuadPrecisionObject *)op1; + other = Py_NewRef(op2); + } + else { + is_forward = 0; + self = (QuadPrecisionObject *)op2; + other = Py_NewRef(op1); + } + + if (PyObject_TypeCheck(other, &QuadPrecision_Type)) { + Py_INCREF(other); + other_quad = (QuadPrecisionObject *)other; + } + else if (PyLong_Check(other) || PyFloat_Check(other)) { + other_quad = QuadPrecision_raw_new(); + if (!other_quad) { + Py_DECREF(other); + return NULL; + } + + if (PyLong_Check(other)) { + long long value = PyLong_AsLongLong(other); + if (value == -1 && PyErr_Occurred()) { + Py_DECREF(other); + Py_DECREF(other_quad); + return NULL; + } + other_quad->quad.value = Sleef_cast_from_int64q1(value); + } + else { + double value = PyFloat_AsDouble(other); + if (value == -1.0 && PyErr_Occurred()) { + Py_DECREF(other); + Py_DECREF(other_quad); + return NULL; + } + other_quad->quad.value = Sleef_cast_from_doubleq1(value); + } + } + else { + Py_DECREF(other); + Py_RETURN_NOTIMPLEMENTED; + } + + QuadPrecisionObject *res = QuadPrecision_raw_new(); + if (!res) { + Py_DECREF(other_quad); + Py_DECREF(other); + return NULL; + } + + if (is_forward) { + binary_op(&res->quad.value, &self->quad.value, &other_quad->quad.value); + } + else { + binary_op(&res->quad.value, &other_quad->quad.value, &self->quad.value); + } + + Py_DECREF(other_quad); + Py_DECREF(other); + return (PyObject *)res; +} + +// todo: add support with float and int +PyObject * +quad_richcompare(QuadPrecisionObject *self, PyObject *other, int cmp_op) +{ + QuadPrecisionObject *other_quad = NULL; + + if (PyObject_TypeCheck(other, &QuadPrecision_Type)) { + Py_INCREF(other); + other_quad = (QuadPrecisionObject *)other; + } + else if (PyLong_CheckExact(other) || PyFloat_CheckExact(other)) { + other_quad = QuadPrecision_from_object(other); + if (other_quad == NULL) { + return NULL; + } + } + else { + Py_RETURN_NOTIMPLEMENTED; + } + int cmp; + switch (cmp_op) { + case Py_LT: + cmp = Sleef_icmpltq1(self->quad.value, other_quad->quad.value); + break; + case Py_LE: + cmp = Sleef_icmpleq1(self->quad.value, other_quad->quad.value); + break; + case Py_EQ: + cmp = Sleef_icmpeqq1(self->quad.value, other_quad->quad.value); + break; + case Py_NE: + cmp = Sleef_icmpneq1(self->quad.value, other_quad->quad.value); + break; + case Py_GT: + cmp = Sleef_icmpgtq1(self->quad.value, other_quad->quad.value); + break; + case Py_GE: + cmp = Sleef_icmpgeq1(self->quad.value, other_quad->quad.value); + break; + default: + Py_DECREF(other_quad); + Py_RETURN_NOTIMPLEMENTED; + } + Py_DECREF(other_quad); + + return PyBool_FromLong(cmp); +} + +PyNumberMethods quad_as_scalar = { + .nb_add = (binaryfunc)quad_binary_func, + .nb_subtract = (binaryfunc)quad_binary_func, + .nb_multiply = (binaryfunc)quad_binary_func, + .nb_true_divide = (binaryfunc)quad_binary_func, + .nb_power = (ternaryfunc)quad_binary_func, + .nb_negative = (unaryfunc)quad_unary_func, + .nb_positive = (unaryfunc)quad_unary_func, + .nb_absolute = (unaryfunc)quad_unary_func, + .nb_bool = (inquiry)quad_nonzero, +}; \ No newline at end of file diff --git a/quaddtype/quaddtype/src/scalar_ops.h b/quaddtype/quaddtype/src/scalar_ops.h new file mode 100644 index 00000000..138738fd --- /dev/null +++ b/quaddtype/quaddtype/src/scalar_ops.h @@ -0,0 +1,20 @@ +#ifndef _QUADDTYPE_SCALAR_OPS_H +#define _QUADDTYPE_SCALAR_OPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "dtype.h" + +PyObject * +quad_richcompare(QuadPrecisionObject *self, PyObject *other, int cmp_op); + +extern PyNumberMethods quad_as_scalar; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/quaddtype/quaddtype/src/umath.cpp b/quaddtype/quaddtype/src/umath.cpp index 329b2cfd..7417886b 100644 --- a/quaddtype/quaddtype/src/umath.cpp +++ b/quaddtype/quaddtype/src/umath.cpp @@ -1,3 +1,5 @@ +#include "scalar.h" + #define PY_ARRAY_UNIQUE_SYMBOL QuadPrecType_ARRAY_API #define PY_UFUNC_UNIQUE_SYMBOL QuadPrecType_UFUNC_API #define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION @@ -6,24 +8,24 @@ #define NO_IMPORT_UFUNC extern "C" { - #include +#include - #include "numpy/arrayobject.h" - #include "numpy/ndarraytypes.h" - #include "numpy/ufuncobject.h" +#include "numpy/arrayobject.h" +#include "numpy/ndarraytypes.h" +#include "numpy/ufuncobject.h" - #include "numpy/dtype_api.h" +#include "numpy/dtype_api.h" } -#include "scalar.h" #include "dtype.h" #include "umath.h" #include "ops.hpp" template -int quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, - char *const data[], npy_intp const dimensions[], - npy_intp const strides[], NpyAuxData *auxdata) +int +quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, char *const data[], + npy_intp const dimensions[], npy_intp const strides[], + NpyAuxData *auxdata) { npy_intp N = dimensions[0]; char *in_ptr = data[0]; @@ -31,8 +33,7 @@ int quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, npy_intp in_stride = strides[0]; npy_intp out_stride = strides[1]; - while (N--) - { + while (N--) { unary_op((Sleef_quad *)in_ptr, (Sleef_quad *)out_ptr); in_ptr += in_stride; out_ptr += out_stride; @@ -41,9 +42,9 @@ int quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, } static NPY_CASTING -quad_unary_op_resolve_descriptors(PyObject *self, - PyArray_DTypeMeta *dtypes[], QuadPrecDTypeObject *given_descrs[], - QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) +quad_unary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *dtypes[], + QuadPrecDTypeObject *given_descrs[], + QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) { Py_INCREF(given_descrs[0]); loop_descrs[0] = given_descrs[0]; @@ -60,30 +61,29 @@ quad_unary_op_resolve_descriptors(PyObject *self, } template -int create_quad_unary_ufunc(PyObject *numpy, const char *ufunc_name) +int +create_quad_unary_ufunc(PyObject *numpy, const char *ufunc_name) { PyObject *ufunc = PyObject_GetAttrString(numpy, ufunc_name); if (ufunc == NULL) { return -1; } - PyArray_DTypeMeta *dtypes[2] = { - &QuadPrecDType, &QuadPrecDType}; + PyArray_DTypeMeta *dtypes[2] = {&QuadPrecDType, &QuadPrecDType}; PyType_Slot slots[] = { - {NPY_METH_resolve_descriptors, (void *)&quad_unary_op_resolve_descriptors}, - {NPY_METH_strided_loop, (void *)&quad_generic_unary_op_strided_loop}, - {0, NULL} - }; + {NPY_METH_resolve_descriptors, (void *)&quad_unary_op_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_generic_unary_op_strided_loop}, + {0, NULL}}; PyArrayMethod_Spec Spec = { - .name = "quad_unary_op", - .nin = 1, - .nout = 1, - .casting = NPY_NO_CASTING, - .flags = (NPY_ARRAYMETHOD_FLAGS)0, - .dtypes = dtypes, - .slots = slots, + .name = "quad_unary_op", + .nin = 1, + .nout = 1, + .casting = NPY_NO_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, }; if (PyUFunc_AddLoopFromSpec(ufunc, &Spec) < 0) { @@ -93,7 +93,8 @@ int create_quad_unary_ufunc(PyObject *numpy, const char *ufunc_name) return 0; } -int init_quad_unary_ops(PyObject *numpy) +int +init_quad_unary_ops(PyObject *numpy) { if (create_quad_unary_ufunc(numpy, "negative") < 0) { return -1; @@ -101,15 +102,52 @@ int init_quad_unary_ops(PyObject *numpy) if (create_quad_unary_ufunc(numpy, "absolute") < 0) { return -1; } + if (create_quad_unary_ufunc(numpy, "rint") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "trunc") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "floor") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "ceil") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "sqrt") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "square") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "log") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "log2") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "log10") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "log1p") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "exp") < 0) { + return -1; + } + if (create_quad_unary_ufunc(numpy, "exp2") < 0) { + return -1; + } return 0; } // Binary ufuncs template -int quad_generic_binop_strided_loop(PyArrayMethod_Context *context, - char *const data[], npy_intp const dimensions[], - npy_intp const strides[], NpyAuxData *auxdata) +int +quad_generic_binop_strided_loop(PyArrayMethod_Context *context, char *const data[], + npy_intp const dimensions[], npy_intp const strides[], + NpyAuxData *auxdata) { npy_intp N = dimensions[0]; char *in1_ptr = data[0], *in2_ptr = data[1]; @@ -129,9 +167,9 @@ int quad_generic_binop_strided_loop(PyArrayMethod_Context *context, } static NPY_CASTING -quad_binary_op_resolve_descriptors(PyObject *self, - PyArray_DTypeMeta *dtypes[], QuadPrecDTypeObject *given_descrs[], - QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) +quad_binary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *dtypes[], + QuadPrecDTypeObject *given_descrs[], + QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) { Py_INCREF(given_descrs[0]); loop_descrs[0] = given_descrs[0]; @@ -153,32 +191,29 @@ quad_binary_op_resolve_descriptors(PyObject *self, // todo: skipping the promoter for now, since same type operation will be requried template -int create_quad_binary_ufunc(PyObject *numpy, const char *ufunc_name) +int +create_quad_binary_ufunc(PyObject *numpy, const char *ufunc_name) { PyObject *ufunc = PyObject_GetAttrString(numpy, ufunc_name); if (ufunc == NULL) { return -1; } - PyArray_DTypeMeta *dtypes[3] = { - &QuadPrecDType, &QuadPrecDType, &QuadPrecDType}; + PyArray_DTypeMeta *dtypes[3] = {&QuadPrecDType, &QuadPrecDType, &QuadPrecDType}; PyType_Slot slots[] = { - {NPY_METH_resolve_descriptors, - (void *)&quad_binary_op_resolve_descriptors}, - {NPY_METH_strided_loop, - (void *)&quad_generic_binop_strided_loop}, - {0, NULL} - }; + {NPY_METH_resolve_descriptors, (void *)&quad_binary_op_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_generic_binop_strided_loop}, + {0, NULL}}; PyArrayMethod_Spec Spec = { - .name = "quad_binop", - .nin = 2, - .nout = 1, - .casting = NPY_NO_CASTING, - .flags = (NPY_ARRAYMETHOD_FLAGS)0, - .dtypes = dtypes, - .slots = slots, + .name = "quad_binop", + .nin = 2, + .nout = 1, + .casting = NPY_NO_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, }; if (PyUFunc_AddLoopFromSpec(ufunc, &Spec) < 0) { @@ -188,7 +223,8 @@ int create_quad_binary_ufunc(PyObject *numpy, const char *ufunc_name) return 0; } -int init_quad_binary_ops(PyObject *numpy) +int +init_quad_binary_ops(PyObject *numpy) { if (create_quad_binary_ufunc(numpy, "add") < 0) { return -1; @@ -196,13 +232,103 @@ int init_quad_binary_ops(PyObject *numpy) if (create_quad_binary_ufunc(numpy, "subtract") < 0) { return -1; } + if (create_quad_binary_ufunc(numpy, "multiply") < 0) { + return -1; + } + if (create_quad_binary_ufunc(numpy, "divide") < 0) { + return -1; + } + if (create_quad_binary_ufunc(numpy, "power") < 0) { + return -1; + } + return 0; +} + +// comparison functions + +template +int +quad_generic_comp_strided_loop(PyArrayMethod_Context *context, char *const data[], + npy_intp const dimensions[], npy_intp const strides[], + NpyAuxData *auxdata) +{ + npy_intp N = dimensions[0]; + char *in1_ptr = data[0], *in2_ptr = data[1]; + char *out_ptr = data[2]; + npy_intp in1_stride = strides[0]; + npy_intp in2_stride = strides[1]; + npy_intp out_stride = strides[2]; + + while (N--) { + *((npy_bool *)out_ptr) = comp((Sleef_quad *)in1_ptr, (Sleef_quad *)in2_ptr); + + in1_ptr += in1_stride; + in2_ptr += in2_stride; + out_ptr += out_stride; + } + return 0; +} + +template +int +create_quad_comparison_ufunc(PyObject *numpy, const char *ufunc_name) +{ + PyObject *ufunc = PyObject_GetAttrString(numpy, ufunc_name); + if (ufunc == NULL) { + return -1; + } + + PyArray_DTypeMeta *dtypes[3] = {&QuadPrecDType, &QuadPrecDType, &PyArray_BoolDType}; + + PyType_Slot slots[] = {{NPY_METH_strided_loop, (void *)&quad_generic_comp_strided_loop}, + {0, NULL}}; + + PyArrayMethod_Spec Spec = { + .name = "quad_comp", + .nin = 2, + .nout = 1, + .casting = NPY_NO_CASTING, + .flags = (NPY_ARRAYMETHOD_FLAGS)0, + .dtypes = dtypes, + .slots = slots, + }; + + if (PyUFunc_AddLoopFromSpec(ufunc, &Spec) < 0) { + return -1; + } return 0; } -int init_quad_umath(void) +int +init_quad_comps(PyObject *numpy) { - PyObject * numpy = PyImport_ImportModule("numpy"); + if (create_quad_comparison_ufunc(numpy, "equal") < 0) { + return -1; + } + if (create_quad_comparison_ufunc(numpy, "not_equal") < 0) { + return -1; + } + if (create_quad_comparison_ufunc(numpy, "less") < 0) { + return -1; + } + if (create_quad_comparison_ufunc(numpy, "less_equal") < 0) { + return -1; + } + if (create_quad_comparison_ufunc(numpy, "greater") < 0) { + return -1; + } + if (create_quad_comparison_ufunc(numpy, "greater_equal") < 0) { + return -1; + } + + return 0; +} + +int +init_quad_umath(void) +{ + PyObject *numpy = PyImport_ImportModule("numpy"); if (!numpy) return -1; @@ -214,11 +340,14 @@ int init_quad_umath(void) goto err; } + if (init_quad_comps(numpy) < 0) { + goto err; + } + Py_DECREF(numpy); return 0; - err: - Py_DECREF(numpy); - return -1; - +err: + Py_DECREF(numpy); + return -1; } \ No newline at end of file From ffaa617e2c24be951699b8f95252045ad2b48ac0 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Sun, 11 Aug 2024 03:07:16 +0530 Subject: [PATCH 33/43] more castings and ufuncs --- mandelbrot.py | 148 ------------------------------ quaddtype/quaddtype/src/ops.hpp | 7 ++ quaddtype/quaddtype/src/umath.cpp | 3 + temp.py | 111 ---------------------- 4 files changed, 10 insertions(+), 259 deletions(-) delete mode 100644 mandelbrot.py delete mode 100644 temp.py diff --git a/mandelbrot.py b/mandelbrot.py deleted file mode 100644 index f0134f30..00000000 --- a/mandelbrot.py +++ /dev/null @@ -1,148 +0,0 @@ -import numpy as np -from quaddtype import QuadPrecDType, QuadPrecision -import matplotlib.pyplot as plt - - -def get_color(t, interior_t): - epsilon = QuadPrecision("1e-10") - - if abs(t - QuadPrecision(1.0)) < epsilon: - value = int(255 * interior_t) - return np.array([value, value, value], dtype=np.uint8) - - t = np.power(t, 0.5) - t = np.mod(t * 20, 1.0) - - if t < 0.16: - return np.array([0, int(255 * (t / 0.16)), int(128 + 127 * (t / 0.16))], dtype=np.uint8) - elif t < 0.33: - return np.array([0, 255, int(255 * (1 - (t - 0.16) / 0.17))], dtype=np.uint8) - elif t < 0.5: - return np.array([int(255 * ((t - 0.33) / 0.17)), 255, 0], dtype=np.uint8) - elif t < 0.66: - return np.array([255, int(255 * (1 - (t - 0.5) / 0.16)), 0], dtype=np.uint8) - elif t < 0.83: - return np.array([255, 0, int(255 * ((t - 0.66) / 0.17))], dtype=np.uint8) - else: - return np.array([int(255 * (1 - (t - 0.83) / 0.17)), 0, int(128 * ((t - 0.83) / 0.17))], dtype=np.uint8) - - -def iterate_and_compute_derivatives(cr, ci, max_iter): - zr, zi = 0.0, 0.0 - dzr, dzi = 1.0, 0.0 - dcr, dci = 0.0, 0.0 - dzdzr, dzdzi = 0.0, 0.0 - - for _ in range(max_iter): - dzdzr_new = 2 * (zr * dzdzr - zi * dzdzi + dzr * dzr - dzi * dzi) - dzdzi_new = 2 * (zr * dzdzi + zi * dzdzr + 2 * dzr * dzi) - dzr_new = 2 * (zr * dzr - zi * dzi) + dcr - dzi_new = 2 * (zr * dzi + zi * dzr) + dci - zr_new = zr * zr - zi * zi + cr - zi_new = 2 * zr * zi + ci - - dzdzr, dzdzi = dzdzr_new, dzdzi_new - dzr, dzi = dzr_new, dzi_new - zr, zi = zr_new, zi_new - dcr, dci = 1.0, 0.0 - - return zr, zi, dzr, dzi, dcr, dci, dzdzr, dzdzi - - -def estimate_interior_distance(cr, ci, max_iter): - zr, zi, dzr, dzi, dcr, dci, dzdzr, dzdzi = iterate_and_compute_derivatives( - cr, ci, max_iter) - - dz_abs_sq = dzr * dzr + dzi * dzi - numerator = 1 - dz_abs_sq - - denominator = np.abs((dcr * dzr + dci * dzi) + - (dzdzr * zr + dzdzi * zi) * dcr) - - return numerator / denominator - - -def mandelbrot(cr, ci, max_iter, radius2): - zr, zi = QuadPrecision(0.0), QuadPrecision(0.0) - for i in range(max_iter): - zr_new = zr * zr - zi * zi + cr - zi_new = QuadPrecision(2) * zr * zi + ci - zr, zi = zr_new, zi_new - if zr * zr + zi * zi > radius2: - log_zn = np.log(zr * zr + zi * zi) / QuadPrecision(2) - nu = np.log(log_zn / np.log(QuadPrecision(2))) / \ - np.log(QuadPrecision(2)) - return i + QuadPrecision(1) - nu, zr, zi - return max_iter, zr, zi - - -def mandelbrot_set(width, height, max_iter, center_r, center_i, zoom): - radius = QuadPrecision(2.0) - radius2 = radius * radius - zoom = 1 / zoom - - x = np.linspace(np.float64(center_r - radius / zoom), - np.float64(center_r + radius / zoom), - width) - y = np.linspace(np.float64(center_i - radius / zoom), - np.float64(center_i + radius / zoom), - height) - cr, ci = np.meshgrid(x, y) - - smooth_iter = np.zeros((height, width), dtype=QuadPrecDType) - final_zr = np.zeros((height, width), dtype=QuadPrecDType) - final_zi = np.zeros((height, width), dtype=QuadPrecDType) - - for i in range(height): - for j in range(width): - smooth_iter[i, j], final_zr[i, j], final_zi[i, j] = mandelbrot( - cr[i, j], ci[i, j], max_iter, radius2) - - img = np.zeros((height, width, 3), dtype=np.uint8) - - interior_mask = smooth_iter == QuadPrecision(max_iter) - interior_cr = cr[interior_mask] - interior_ci = ci[interior_mask] - interior_distance = np.array([estimate_interior_distance( - cr, ci, max_iter) for cr, ci in zip(interior_cr, interior_ci)]) - interior_t = (interior_distance - - np.floor(interior_distance)).astype(np.float64) - - exterior_mask = ~interior_mask - t = smooth_iter[exterior_mask] / QuadPrecision(max_iter) - - interior_colors = np.array( - [get_color(QuadPrecision(1.0), t) for t in interior_t]) - exterior_colors = np.array([get_color(t, QuadPrecision(0.0)) for t in t]) - - img[interior_mask] = interior_colors - img[exterior_mask] = exterior_colors - - return img - - -def plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom): - center_rq = QuadPrecision(center_r) - center_iq = QuadPrecision(center_i) - zoom_q = QuadPrecision(zoom) - - img_array = mandelbrot_set( - width, height, max_iter, center_rq, center_iq, zoom_q) - - plt.figure(figsize=(10, 10)) - plt.imshow(img_array) - plt.axis('off') - plt.title( - f'Mandelbrot Set (zoom: {zoom}, center: {center_r} + {center_i}i, iterations: {max_iter}, dtype: numpy.float64)') - plt.show() - - -if __name__ == "__main__": - width = 800 - height = 800 - max_iter = 1000 - center_r = -0.75 - center_i = 0.0 - zoom = 1.0 - - plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom) diff --git a/quaddtype/quaddtype/src/ops.hpp b/quaddtype/quaddtype/src/ops.hpp index 51f97564..f5f0a237 100644 --- a/quaddtype/quaddtype/src/ops.hpp +++ b/quaddtype/quaddtype/src/ops.hpp @@ -139,6 +139,13 @@ quad_pow(Sleef_quad *res, Sleef_quad *a, Sleef_quad *b) return 0; } +static inline int +quad_mod(Sleef_quad *res, Sleef_quad *a, Sleef_quad *b) +{ + *res = Sleef_fmodq1(*a, *b); + return 0; +} + // comparison functions typedef npy_bool (*cmp_def)(const Sleef_quad *, const Sleef_quad *); diff --git a/quaddtype/quaddtype/src/umath.cpp b/quaddtype/quaddtype/src/umath.cpp index 7417886b..c45e83d2 100644 --- a/quaddtype/quaddtype/src/umath.cpp +++ b/quaddtype/quaddtype/src/umath.cpp @@ -241,6 +241,9 @@ init_quad_binary_ops(PyObject *numpy) if (create_quad_binary_ufunc(numpy, "power") < 0) { return -1; } + if (create_quad_binary_ufunc(numpy, "mod") < 0) { + return -1; + } return 0; } diff --git a/temp.py b/temp.py deleted file mode 100644 index 80716e41..00000000 --- a/temp.py +++ /dev/null @@ -1,111 +0,0 @@ -import numpy as np -from quaddtype import QuadPrecDType, QuadPrecision -import matplotlib.pyplot as plt - -def get_color(t, interior_t): - epsilon = QuadPrecision("1e-10") - - if abs(t - QuadPrecision(1.0)) < epsilon: - value = int(255 * float(interior_t)) - return np.array([value, value, value], dtype=np.uint8) - - t = np.power(t, 0.5) - t = np.mod(t * 20, 1.0) - - if t < 0.16: - return np.array([0, int(255 * (t / 0.16)), int(128 + 127 * (t / 0.16))], dtype=np.uint8) - elif t < 0.33: - return np.array([0, 255, int(255 * (1 - (t - 0.16) / 0.17))], dtype=np.uint8) - elif t < 0.5: - return np.array([int(255 * ((t - 0.33) / 0.17)), 255, 0], dtype=np.uint8) - elif t < 0.66: - return np.array([255, int(255 * (1 - (t - 0.5) / 0.16)), 0], dtype=np.uint8) - elif t < 0.83: - return np.array([255, 0, int(255 * ((t - 0.66) / 0.17))], dtype=np.uint8) - else: - return np.array([int(255 * (1 - (t - 0.83) / 0.17)), 0, int(128 * ((t - 0.83) / 0.17))], dtype=np.uint8) - -def iterate_and_compute_derivatives(c, max_iter): - z = 0 - dz = 1 - dc = 0 - dzdz = 0 - - for _ in range(max_iter): - dzdz = 2 * (z * dzdz + dz * dz) - dz = 2 * z * dz + dc - z = z * z + c - dc = 1 - - return z, dz, dc, dzdz - -def estimate_interior_distance(c, max_iter): - z, dz, dc, dzdz = iterate_and_compute_derivatives(c, max_iter) - - dz_abs_sq = np.abs(dz) ** 2 - numerator = 1 - dz_abs_sq - - denominator = np.abs(dc * dz + dzdz * z * dc) - - return numerator / denominator - -def mandelbrot(c, max_iter, radius2): - z = 0 - for i in range(max_iter): - z = z * z + c - if np.abs(z) ** 2 > radius2: - log_zn = np.log(np.abs(z)) - nu = np.log(log_zn / np.log(2)) / np.log(2) - return i + 1 - nu, z - return max_iter, z - -def mandelbrot_set(width, height, max_iter, center_r, center_i, zoom): - radius = 2.0 - radius2 = radius * radius - zoom_q = 1 / zoom - - x = np.linspace(center_r - radius / zoom, center_r + radius / zoom, width) - y = np.linspace(center_i - radius / zoom, center_i + radius / zoom, height) - c = x[np.newaxis, :] + 1j * y[:, np.newaxis] - - smooth_iter, final_z = np.frompyfunc(lambda c: mandelbrot(c, max_iter, radius2), 1, 2)(c) - smooth_iter = smooth_iter.astype(np.float64) - final_z = final_z.astype(np.complex128) - - img = np.zeros((height, width, 3), dtype=np.uint8) - - interior_mask = smooth_iter == max_iter - interior_c = c[interior_mask] - interior_distance = np.frompyfunc(lambda c: estimate_interior_distance(c, max_iter), 1, 1)(interior_c) - interior_distance = interior_distance.astype(np.float64) - interior_t = interior_distance - np.floor(interior_distance) - - exterior_mask = ~interior_mask - t = smooth_iter[exterior_mask] / max_iter - - interior_colors = np.array(list(map(lambda t: get_color(1.0, t), interior_t))) - exterior_colors = np.array(list(map(lambda t: get_color(t, 0.0), t))) - - img[interior_mask] = interior_colors - img[exterior_mask] = exterior_colors - - return img - -def plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom): - img_array = mandelbrot_set(width, height, max_iter, center_r, center_i, zoom) - - plt.figure(figsize=(10, 10)) - plt.imshow(img_array) - plt.axis('off') - plt.title(f'Mandelbrot Set (zoom: {zoom}, center: {center_r} + {center_i}i, iterations: {max_iter}, dtype: numpy.float64)') - plt.show() - -if __name__ == "__main__": - width = 800 - height = 800 - max_iter = 1000 - center_r = -0.75 - center_i = 0.0 - zoom = 1.0 - - plot_mandelbrot(width, height, max_iter, center_r, center_i, zoom) \ No newline at end of file From 299568b0a9c5366600136c9df0a4d00ab8abdddc Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Sun, 11 Aug 2024 12:04:36 +0530 Subject: [PATCH 34/43] updated meson.build --- quaddtype/meson.build | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/quaddtype/meson.build b/quaddtype/meson.build index 01ae59e3..f1f9eab1 100644 --- a/quaddtype/meson.build +++ b/quaddtype/meson.build @@ -5,7 +5,7 @@ py = py_mod.find_installation() c = meson.get_compiler('c') -sleef_dep = c.find_library('sleef', dirs:['/usr/local/lib']) +sleef_dep = c.find_library('sleef') sleefquad_dep = c.find_library('sleefquad') incdir_numpy = run_command(py, @@ -30,7 +30,12 @@ srcs = [ 'quaddtype/src/scalar.c', 'quaddtype/src/dtype.h', 'quaddtype/src/dtype.c', - 'quaddtype/src/quaddtype_main.c' + 'quaddtype/src/quaddtype_main.c', + 'quaddtype/src/scalar_ops.h', + 'quaddtype/src/scalar_ops.cpp', + 'quaddtype/src/ops.hpp', + 'quaddtype/src/umath.h', + 'quaddtype/src/umath.cpp' ] py.install_sources( From d13f86366150695f34916bddf96e3e2bdf3de3e6 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Mon, 12 Aug 2024 00:06:36 +0530 Subject: [PATCH 35/43] adding more scalar tests --- quaddtype/quaddtype/src/ops.hpp | 7 ++ quaddtype/quaddtype/src/scalar_ops.cpp | 2 +- quaddtype/tests/test_quaddtype.py | 107 ++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 5 deletions(-) diff --git a/quaddtype/quaddtype/src/ops.hpp b/quaddtype/quaddtype/src/ops.hpp index f5f0a237..8cc3b401 100644 --- a/quaddtype/quaddtype/src/ops.hpp +++ b/quaddtype/quaddtype/src/ops.hpp @@ -10,6 +10,13 @@ quad_negative(Sleef_quad *op, Sleef_quad *out) return 0; } +static int +quad_positive(Sleef_quad *op, Sleef_quad *out) +{ + *out = *op; + return 0; +} + static inline int quad_absolute(Sleef_quad *op, Sleef_quad *out) { diff --git a/quaddtype/quaddtype/src/scalar_ops.cpp b/quaddtype/quaddtype/src/scalar_ops.cpp index fa83713b..f4f3ed93 100644 --- a/quaddtype/quaddtype/src/scalar_ops.cpp +++ b/quaddtype/quaddtype/src/scalar_ops.cpp @@ -165,7 +165,7 @@ PyNumberMethods quad_as_scalar = { .nb_true_divide = (binaryfunc)quad_binary_func, .nb_power = (ternaryfunc)quad_binary_func, .nb_negative = (unaryfunc)quad_unary_func, - .nb_positive = (unaryfunc)quad_unary_func, + .nb_positive = (unaryfunc)quad_unary_func, .nb_absolute = (unaryfunc)quad_unary_func, .nb_bool = (inquiry)quad_nonzero, }; \ No newline at end of file diff --git a/quaddtype/tests/test_quaddtype.py b/quaddtype/tests/test_quaddtype.py index 4d57dd67..20ccd91c 100644 --- a/quaddtype/tests/test_quaddtype.py +++ b/quaddtype/tests/test_quaddtype.py @@ -1,9 +1,108 @@ import pytest - +import sys import numpy as np +import operator + from quaddtype import QuadPrecDType, QuadPrecision -def test_dtype(): - a = QuadPrecision("1.63") - assert f"{np.array([a], dtype=QuadPrecDType).dtype}" == "QuadPrecDType()" +def test_create_scalar_simple(): + assert isinstance(QuadPrecision("12.0"), QuadPrecision) + assert isinstance(QuadPrecision(1.63), QuadPrecision) + assert isinstance(QuadPrecision(1), QuadPrecision) + + +def test_basic_equality(): + assert QuadPrecision("12") == QuadPrecision( + "12.0") == QuadPrecision("12.00") + + +@pytest.mark.parametrize("val", ["123532.543", "12893283.5"]) +def test_scalar_repr(val): + expected = f"QuadPrecision('{str(QuadPrecision(val))}')" + assert repr(QuadPrecision(val)) == expected + + +@pytest.mark.parametrize("op", ["add", "sub", "mul", "truediv", "pow"]) +@pytest.mark.parametrize("other", ["3.0", "12.5", "100.0"]) +def test_binary_ops(op, other): + op_func = getattr(operator, op) + quad_a = QuadPrecision("12.5") + quad_b = QuadPrecision(other) + float_a = 12.5 + float_b = float(other) + + quad_result = op_func(quad_a, quad_b) + float_result = op_func(float_a, float_b) + + assert np.abs(np.float64(quad_result) - float_result) < 1e-10 + + +@pytest.mark.parametrize("op", ["eq", "ne", "le", "lt", "ge", "gt"]) +@pytest.mark.parametrize("other", ["3.0", "12.5", "100.0"]) +def test_comparisons(op, other): + op_func = getattr(operator, op) + quad_a = QuadPrecision("12.5") + quad_b = QuadPrecision(other) + float_a = 12.5 + float_b = float(other) + + assert op_func(quad_a, quad_b) == op_func(float_a, float_b) + + +@pytest.mark.parametrize("op, val, expected", [ + ("neg", "3.0", "-3.0"), + ("neg", "-3.0", "3.0"), + ("pos", "3.0", "3.0"), + ("pos", "-3.0", "-3.0"), + ("abs", "3.0", "3.0"), + ("abs", "-3.0", "3.0"), + ("neg", "12.5", "-12.5"), + ("pos", "100.0", "100.0"), + ("abs", "-25.5", "25.5"), +]) +def test_unary_ops(op, val, expected): + quad_val = QuadPrecision(val) + expected_val = QuadPrecision(expected) + + if op == "neg": + result = -quad_val + elif op == "pos": + result = +quad_val + elif op == "abs": + result = abs(quad_val) + else: + raise ValueError(f"Unsupported operation: {op}") + + assert result == expected_val, f"{op}({val}) should be {expected}, but got {result}" + + +def test_nan_and_inf(): + assert (QuadPrecision("nan") != QuadPrecision("nan")) == ( + QuadPrecision("nan") == QuadPrecision("nan")) + assert QuadPrecision("inf") > QuadPrecision("1e1000") + assert QuadPrecision("-inf") < QuadPrecision("-1e1000") + + +def test_dtype_creation(): + dtype = QuadPrecDType() + assert isinstance(dtype, np.dtype) + assert dtype.name == 'QuadPrecDType128' + + +def test_array_creation(): + arr = np.array([1, 2, 3], dtype=QuadPrecDType()) + assert arr.dtype.name == 'QuadPrecDType128' + assert all(isinstance(x, QuadPrecision) for x in arr) + + +def test_array_operations(): + arr1 = np.array( + [QuadPrecision("1.5"), QuadPrecision("2.5"), QuadPrecision("3.5")]) + arr2 = np.array( + [QuadPrecision("0.5"), QuadPrecision("1.0"), QuadPrecision("1.5")]) + + result = arr1 + arr2 + expected = np.array( + [QuadPrecision("2.0"), QuadPrecision("3.5"), QuadPrecision("5.0")]) + assert np.all(result == expected) From 78a693179f735440ba73c807d334d40cef91c7a6 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Tue, 13 Aug 2024 21:51:39 +0530 Subject: [PATCH 36/43] defining scalar ops in struct sequence --- quaddtype/quaddtype/src/scalar_ops.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype/quaddtype/src/scalar_ops.cpp b/quaddtype/quaddtype/src/scalar_ops.cpp index f4f3ed93..a51bc69a 100644 --- a/quaddtype/quaddtype/src/scalar_ops.cpp +++ b/quaddtype/quaddtype/src/scalar_ops.cpp @@ -162,10 +162,10 @@ PyNumberMethods quad_as_scalar = { .nb_add = (binaryfunc)quad_binary_func, .nb_subtract = (binaryfunc)quad_binary_func, .nb_multiply = (binaryfunc)quad_binary_func, - .nb_true_divide = (binaryfunc)quad_binary_func, .nb_power = (ternaryfunc)quad_binary_func, .nb_negative = (unaryfunc)quad_unary_func, .nb_positive = (unaryfunc)quad_unary_func, .nb_absolute = (unaryfunc)quad_unary_func, .nb_bool = (inquiry)quad_nonzero, + .nb_true_divide = (binaryfunc)quad_binary_func, }; \ No newline at end of file From 6fbe989b4ef5c7cc828dbf202c5ed70452aae14d Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 15 Aug 2024 18:24:55 +0530 Subject: [PATCH 37/43] resolving fixing unaligned loop and casting issues --- quaddtype/quaddtype/src/casts.cpp | 79 +++++++++++++++++++------------ quaddtype/quaddtype/src/casts.h | 11 ++--- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/quaddtype/quaddtype/src/casts.cpp b/quaddtype/quaddtype/src/casts.cpp index 3fbd8502..88cbb11b 100644 --- a/quaddtype/quaddtype/src/casts.cpp +++ b/quaddtype/quaddtype/src/casts.cpp @@ -14,12 +14,13 @@ extern "C" { } #include "sleef.h" #include "sleefquad.h" -#include #include "scalar.h" #include "casts.h" #include "dtype.h" +#define NUM_CASTS 29 // 14 to_casts + 14 from_casts + 1 quad_to_quad + static NPY_CASTING quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta *NPY_UNUSED(dtypes[2]), @@ -51,14 +52,13 @@ quad_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[], char *in_ptr = data[0]; char *out_ptr = data[1]; - while (N--) { - Sleef_quad *in = (Sleef_quad *)in_ptr; - Sleef_quad *out = (Sleef_quad *)out_ptr; - - *out = *in; + npy_intp in_stride = strides[0]; + npy_intp out_stride = strides[1]; - in_ptr += strides[0]; - out_ptr += strides[1]; + while (N--) { + memcpy(out_ptr, in_ptr, sizeof(Sleef_quad)); + in_ptr += in_stride; + out_ptr += out_stride; } return 0; } @@ -172,7 +172,7 @@ numpy_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta } loop_descrs[0] = PyArray_GetDefaultDescr(dtypes[0]); - *view_offset = 0; + // *view_offset = 0; return NPY_SAFE_CASTING; } @@ -187,9 +187,12 @@ numpy_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[], char *out_ptr = data[1]; while (N--) { - T in_val = *(T *)in_ptr; - Sleef_quad *out_val = (Sleef_quad *)out_ptr; - *out_val = to_quad(in_val); + T in_val; + Sleef_quad out_val; + + memcpy(&in_val, in_ptr, sizeof(T)); + out_val = to_quad(in_val); + memcpy(out_ptr, &out_val, sizeof(Sleef_quad)); in_ptr += strides[0]; out_ptr += strides[1]; @@ -298,8 +301,8 @@ quad_to_numpy_resolve_descriptors(PyObject *NPY_UNUSED(self), PyArray_DTypeMeta loop_descrs[0] = given_descrs[0]; loop_descrs[1] = PyArray_GetDefaultDescr(dtypes[1]); - *view_offset = 0; - return NPY_SAME_KIND_CASTING; + // *view_offset = 0; + return NPY_UNSAFE_CASTING; } template @@ -323,7 +326,16 @@ quad_to_numpy_strided_loop(PyArrayMethod_Context *context, char *const data[], return 0; } -static std::vector specs; +static PyArrayMethod_Spec *specs[NUM_CASTS + 1]; // +1 for NULL terminator +static size_t spec_count = 0; + +void +add_spec(PyArrayMethod_Spec *spec) +{ + if (spec_count < NUM_CASTS) { + specs[spec_count++] = spec; + } +} // functions to add casts template @@ -337,15 +349,16 @@ add_cast_from(PyArray_DTypeMeta *to) {NPY_METH_strided_loop, (void *)&quad_to_numpy_strided_loop}, {0, nullptr}}; - specs.push_back(new PyArrayMethod_Spec{ + PyArrayMethod_Spec *spec = new PyArrayMethod_Spec{ .name = "cast_QuadPrec_to_NumPy", .nin = 1, .nout = 1, - .casting = NPY_SAME_KIND_CASTING, + .casting = NPY_UNSAFE_CASTING, .flags = (NPY_ARRAYMETHOD_FLAGS)0, .dtypes = dtypes, .slots = slots, - }); + }; + add_spec(spec); } template @@ -359,7 +372,7 @@ add_cast_to(PyArray_DTypeMeta *from) {NPY_METH_strided_loop, (void *)&numpy_to_quad_strided_loop}, {0, nullptr}}; - specs.push_back(new PyArrayMethod_Spec{ + PyArrayMethod_Spec *spec = new PyArrayMethod_Spec{ .name = "cast_NumPy_to_QuadPrec", .nin = 1, .nout = 1, @@ -367,26 +380,32 @@ add_cast_to(PyArray_DTypeMeta *from) .flags = (NPY_ARRAYMETHOD_FLAGS)0, .dtypes = dtypes, .slots = slots, - }); + }; + + add_spec(spec); } PyArrayMethod_Spec ** init_casts_internal(void) { PyArray_DTypeMeta **quad2quad_dtypes = new PyArray_DTypeMeta *[2]{nullptr, nullptr}; + PyType_Slot *quad2quad_slots = new PyType_Slot[4]{ + {NPY_METH_resolve_descriptors, (void *)&quad_to_quad_resolve_descriptors}, + {NPY_METH_strided_loop, (void *)&quad_to_quad_strided_loop}, + {NPY_METH_unaligned_strided_loop, (void *)&quad_to_quad_strided_loop}, + {0, nullptr}}; - specs.push_back(new PyArrayMethod_Spec{ + PyArrayMethod_Spec *quad2quad_spec = new PyArrayMethod_Spec{ .name = "cast_QuadPrec_to_QuadPrec", .nin = 1, .nout = 1, .casting = NPY_SAME_KIND_CASTING, .flags = NPY_METH_SUPPORTS_UNALIGNED, .dtypes = quad2quad_dtypes, - .slots = new PyType_Slot[4]{ - {NPY_METH_resolve_descriptors, (void *)&quad_to_quad_resolve_descriptors}, - {NPY_METH_strided_loop, (void *)&quad_to_quad_strided_loop}, - {NPY_METH_unaligned_strided_loop, (void *)&quad_to_quad_strided_loop}, - {0, NULL}}}); + .slots = quad2quad_slots, + }; + + add_spec(quad2quad_spec); add_cast_to(&PyArray_BoolDType); add_cast_to(&PyArray_ByteDType); @@ -418,8 +437,8 @@ init_casts_internal(void) add_cast_from(&PyArray_DoubleDType); add_cast_from(&PyArray_LongDoubleDType); - specs.push_back(nullptr); - return specs.data(); + specs[spec_count] = nullptr; + return specs; } PyArrayMethod_Spec ** @@ -428,7 +447,7 @@ init_casts(void) try { return init_casts_internal(); } - catch (const std::exception &e) { + catch (int e) { PyErr_NoMemory(); return nullptr; } @@ -445,5 +464,5 @@ free_casts(void) delete cast->slots; delete cast; } - specs.clear(); + spec_count = 0; } diff --git a/quaddtype/quaddtype/src/casts.h b/quaddtype/quaddtype/src/casts.h index 504b9cd4..7a7cd9f5 100644 --- a/quaddtype/quaddtype/src/casts.h +++ b/quaddtype/quaddtype/src/casts.h @@ -1,19 +1,18 @@ #ifndef _QUADDTYPE_CASTS_H #define _QUADDTYPE_CASTS_H -#include +#include #include "numpy/dtype_api.h" - #ifdef __cplusplus extern "C" { #endif -extern PyArrayMethod_Spec QuadtoQuadCastSpec; - -PyArrayMethod_Spec ** init_casts(void); +PyArrayMethod_Spec ** +init_casts(void); -void free_casts(void); +void +free_casts(void); #ifdef __cplusplus } From 6e124e3f55fef184595830422be1da6d30d731b5 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 15 Aug 2024 18:26:42 +0530 Subject: [PATCH 38/43] resolving error catching and removing initial data init for dtype --- quaddtype/quaddtype/src/dtype.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/quaddtype/quaddtype/src/dtype.c b/quaddtype/quaddtype/src/dtype.c index 5992b42d..a6f485bf 100644 --- a/quaddtype/quaddtype/src/dtype.c +++ b/quaddtype/quaddtype/src/dtype.c @@ -11,7 +11,6 @@ #include "numpy/arrayobject.h" #include "numpy/ndarraytypes.h" #include "numpy/dtype_api.h" -#include "numpy/_public_dtype_api_table.h" // not included in dtype_api.h #include "scalar.h" #include "casts.h" @@ -21,7 +20,6 @@ static inline int quad_load(Sleef_quad *x, char *data_ptr) { if (data_ptr == NULL || x == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid memory location"); return -1; } *x = *(Sleef_quad *)data_ptr; @@ -32,7 +30,6 @@ static inline int quad_store(char *data_ptr, Sleef_quad x) { if (data_ptr == NULL) { - PyErr_SetString(PyExc_ValueError, "Invalid memory location"); return -1; } *(Sleef_quad *)data_ptr = x; @@ -47,7 +44,6 @@ QuadPrecDTypeObject * new_quaddtype_instance(void) } new->base.elsize = sizeof(Sleef_quad); new->base.alignment = _Alignof(Sleef_quad); - new->base.flags |= NPY_NEEDS_INIT; // Indicates memory for this data-type must be initialized (set to 0) on creation. return new; } @@ -114,6 +110,9 @@ static int quadprec_setitem(QuadPrecDTypeObject *descr, PyObject *obj, char *dat if (quad_store(dataptr, value->quad.value) < 0) { Py_DECREF(value); + char error_msg[100]; + snprintf(error_msg, sizeof(error_msg), "Invalid memory location %p", (void*)dataptr); + PyErr_SetString(PyExc_ValueError, error_msg); return -1; } @@ -131,6 +130,9 @@ static PyObject * quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) if (quad_load(&new->quad.value, dataptr) < 0) { Py_DECREF(new); + char error_msg[100]; + snprintf(error_msg, sizeof(error_msg), "Invalid memory location %p", (void*)dataptr); + PyErr_SetString(PyExc_ValueError, error_msg); return NULL; } return (PyObject *)new; From 63dc446b8f44135f77cfc2b06040252ab239538b Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Thu, 15 Aug 2024 19:02:52 +0530 Subject: [PATCH 39/43] added destructor function for tp_dealloc --- quaddtype/quaddtype/src/scalar.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/quaddtype/quaddtype/src/scalar.c b/quaddtype/quaddtype/src/scalar.c index 668e90af..b4bf3a2e 100644 --- a/quaddtype/quaddtype/src/scalar.c +++ b/quaddtype/quaddtype/src/scalar.c @@ -49,11 +49,18 @@ QuadPrecisionObject * QuadPrecision_from_object(PyObject * value) } else if(PyLong_Check(value)) { - self->quad.value = Sleef_cast_from_int64q1(PyLong_AsLong(value)); + long int val = PyLong_AsLong(value); + if (val == -1) + { + PyErr_SetString(PyExc_OverflowError, "Overflow Error, value out of range"); + Py_DECREF(self); + return NULL; + } + self->quad.value = Sleef_cast_from_int64q1(val); } else { - PyErr_SetString(PyExc_TypeError, "QuadPrecision value must be a float or string"); + PyErr_SetString(PyExc_TypeError, "QuadPrecision value must be a float, int or string"); Py_DECREF(self); return NULL; } @@ -91,6 +98,12 @@ static PyObject * QuadPrecision_repr(QuadPrecisionObject* self) return res; } +static void +quad_dealloc(QuadPrecDTypeObject *self) +{ + PyArrayDescr_Type.tp_dealloc((PyObject *)self); +} + PyTypeObject QuadPrecision_Type = { PyVarObject_HEAD_INIT(NULL, 0) @@ -98,6 +111,7 @@ PyTypeObject QuadPrecision_Type = .tp_basicsize = sizeof(QuadPrecisionObject), .tp_itemsize = 0, .tp_new = QuadPrecision_new, + .tp_dealloc = (destructor)quad_dealloc, .tp_repr = (reprfunc)QuadPrecision_repr, .tp_str = (reprfunc)QuadPrecision_str, .tp_as_number = &quad_as_scalar, From d2e1dedd934227242734abcdaaafc0a1ed7ddd4e Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 16 Aug 2024 13:04:49 +0530 Subject: [PATCH 40/43] changing quad->quad cast to NPY_NO_CASTING from NPY_SAME_CASTING --- quaddtype/quaddtype/src/casts.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quaddtype/quaddtype/src/casts.cpp b/quaddtype/quaddtype/src/casts.cpp index 88cbb11b..c096fe5c 100644 --- a/quaddtype/quaddtype/src/casts.cpp +++ b/quaddtype/quaddtype/src/casts.cpp @@ -40,7 +40,7 @@ quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self), } *view_offset = 0; - return NPY_SAME_KIND_CASTING; + return NPY_NO_CASTING; } static int @@ -399,7 +399,7 @@ init_casts_internal(void) .name = "cast_QuadPrec_to_QuadPrec", .nin = 1, .nout = 1, - .casting = NPY_SAME_KIND_CASTING, + .casting = NPY_NO_CASTING, .flags = NPY_METH_SUPPORTS_UNALIGNED, .dtypes = quad2quad_dtypes, .slots = quad2quad_slots, From 96270bcedf30dcede203e3b94ec51eeda8e5df43 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 16 Aug 2024 13:05:26 +0530 Subject: [PATCH 41/43] added dtype promoter functions --- quaddtype/quaddtype/src/umath.cpp | 240 ++++++++++++++++++++++++++++-- 1 file changed, 227 insertions(+), 13 deletions(-) diff --git a/quaddtype/quaddtype/src/umath.cpp b/quaddtype/quaddtype/src/umath.cpp index c45e83d2..0da945bc 100644 --- a/quaddtype/quaddtype/src/umath.cpp +++ b/quaddtype/quaddtype/src/umath.cpp @@ -9,6 +9,7 @@ extern "C" { #include +#include #include "numpy/arrayobject.h" #include "numpy/ndarraytypes.h" @@ -16,7 +17,6 @@ extern "C" { #include "numpy/dtype_api.h" } - #include "dtype.h" #include "umath.h" #include "ops.hpp" @@ -33,8 +33,12 @@ quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, char *const d npy_intp in_stride = strides[0]; npy_intp out_stride = strides[1]; + Sleef_quad in, out; while (N--) { - unary_op((Sleef_quad *)in_ptr, (Sleef_quad *)out_ptr); + memcpy(&in, in_ptr, sizeof(Sleef_quad)); + unary_op(&in, &out); + memcpy(out_ptr, &out, sizeof(Sleef_quad)); + in_ptr += in_stride; out_ptr += out_stride; } @@ -42,9 +46,9 @@ quad_generic_unary_op_strided_loop(PyArrayMethod_Context *context, char *const d } static NPY_CASTING -quad_unary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *dtypes[], - QuadPrecDTypeObject *given_descrs[], - QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) +quad_unary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[], + PyArray_Descr *const given_descrs[], PyArray_Descr *loop_descrs[], + npy_intp *NPY_UNUSED(view_offset)) { Py_INCREF(given_descrs[0]); loop_descrs[0] = given_descrs[0]; @@ -57,7 +61,7 @@ quad_unary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *dtypes[], Py_INCREF(given_descrs[1]); loop_descrs[1] = given_descrs[1]; - return NPY_NO_CASTING; // Quad precision is always the same precision + return NPY_NO_CASTING; } template @@ -156,8 +160,12 @@ quad_generic_binop_strided_loop(PyArrayMethod_Context *context, char *const data npy_intp in2_stride = strides[1]; npy_intp out_stride = strides[2]; + Sleef_quad in1, in2, out; while (N--) { - binop((Sleef_quad *)out_ptr, (Sleef_quad *)in1_ptr, (Sleef_quad *)in2_ptr); + memcpy(&in1, in1_ptr, sizeof(Sleef_quad)); + memcpy(&in2, in2_ptr, sizeof(Sleef_quad)); + binop(&out, &in1, &in2); + memcpy(out_ptr, &out, sizeof(Sleef_quad)); in1_ptr += in1_stride; in2_ptr += in2_stride; @@ -167,9 +175,9 @@ quad_generic_binop_strided_loop(PyArrayMethod_Context *context, char *const data } static NPY_CASTING -quad_binary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *dtypes[], - QuadPrecDTypeObject *given_descrs[], - QuadPrecDTypeObject *loop_descrs[], npy_intp *unused) +quad_binary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[], + PyArray_Descr *const given_descrs[], + PyArray_Descr *loop_descrs[], npy_intp *NPY_UNUSED(view_offset)) { Py_INCREF(given_descrs[0]); loop_descrs[0] = given_descrs[0]; @@ -177,18 +185,168 @@ quad_binary_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *dtypes[], loop_descrs[1] = given_descrs[1]; if (given_descrs[2] == NULL) { + PyArray_Descr *out_descr = (PyArray_Descr *)new_quaddtype_instance(); + if (!out_descr) { + return (NPY_CASTING)-1; + } Py_INCREF(given_descrs[0]); - loop_descrs[2] = given_descrs[0]; + loop_descrs[2] = out_descr; } else { Py_INCREF(given_descrs[2]); loop_descrs[2] = given_descrs[2]; } - return NPY_NO_CASTING; // Quad precision is always the same precision + return NPY_NO_CASTING; } -// todo: skipping the promoter for now, since same type operation will be requried +// helper debugging function +static const char * +get_dtype_name(PyArray_DTypeMeta *dtype) +{ + if (dtype == &QuadPrecDType) { + return "QuadPrecDType"; + } + else if (dtype == &PyArray_BoolDType) { + return "BoolDType"; + } + else if (dtype == &PyArray_ByteDType) { + return "ByteDType"; + } + else if (dtype == &PyArray_UByteDType) { + return "UByteDType"; + } + else if (dtype == &PyArray_ShortDType) { + return "ShortDType"; + } + else if (dtype == &PyArray_UShortDType) { + return "UShortDType"; + } + else if (dtype == &PyArray_IntDType) { + return "IntDType"; + } + else if (dtype == &PyArray_UIntDType) { + return "UIntDType"; + } + else if (dtype == &PyArray_LongDType) { + return "LongDType"; + } + else if (dtype == &PyArray_ULongDType) { + return "ULongDType"; + } + else if (dtype == &PyArray_LongLongDType) { + return "LongLongDType"; + } + else if (dtype == &PyArray_ULongLongDType) { + return "ULongLongDType"; + } + else if (dtype == &PyArray_FloatDType) { + return "FloatDType"; + } + else if (dtype == &PyArray_DoubleDType) { + return "DoubleDType"; + } + else if (dtype == &PyArray_LongDoubleDType) { + return "LongDoubleDType"; + } + else { + return "UnknownDType"; + } +} + +static int +quad_ufunc_promoter(PyUFuncObject *ufunc, PyArray_DTypeMeta *op_dtypes[], + PyArray_DTypeMeta *signature[], PyArray_DTypeMeta *new_op_dtypes[]) +{ + // printf("quad_ufunc_promoter called for ufunc: %s\n", ufunc->name); + // printf("Entering quad_ufunc_promoter\n"); + // printf("Ufunc name: %s\n", ufunc->name); + // printf("nin: %d, nargs: %d\n", ufunc->nin, ufunc->nargs); + + int nin = ufunc->nin; + int nargs = ufunc->nargs; + PyArray_DTypeMeta *common = NULL; + bool has_quad = false; + + // Handle the special case for reductions + if (op_dtypes[0] == NULL) { + assert(nin == 2 && ufunc->nout == 1); /* must be reduction */ + for (int i = 0; i < 3; i++) { + Py_INCREF(op_dtypes[1]); + new_op_dtypes[i] = op_dtypes[1]; + // printf("new_op_dtypes[%d] set to %s\n", i, get_dtype_name(new_op_dtypes[i])); + } + return 0; + } + + // Check if any input or signature is QuadPrecision + for (int i = 0; i < nargs; i++) { + if ((i < nin && op_dtypes[i] == &QuadPrecDType) || (signature[i] == &QuadPrecDType)) { + has_quad = true; + // printf("QuadPrecision detected in input %d or signature\n", i); + break; + } + } + + if (has_quad) { + // If QuadPrecision is involved, use it for all arguments + common = &QuadPrecDType; + // printf("Using QuadPrecDType as common type\n"); + } + else { + // Check if output signature is homogeneous + for (int i = nin; i < nargs; i++) { + if (signature[i] != NULL) { + if (common == NULL) { + Py_INCREF(signature[i]); + common = signature[i]; + // printf("Common type set to %s from signature\n", get_dtype_name(common)); + } + else if (common != signature[i]) { + Py_CLEAR(common); // Not homogeneous, unset common + // printf("Output signature not homogeneous, cleared common type\n"); + break; + } + } + } + + // If no common output dtype, use standard promotion for inputs + if (common == NULL) { + // printf("Using standard promotion for inputs\n"); + common = PyArray_PromoteDTypeSequence(nin, op_dtypes); + if (common == NULL) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Clear(); // Do not propagate normal promotion errors + } + // printf("Exiting quad_ufunc_promoter (promotion failed)\n"); + return -1; + } + // printf("Common type after promotion: %s\n", get_dtype_name(common)); + } + } + + // Set all new_op_dtypes to the common dtype + for (int i = 0; i < nargs; i++) { + if (signature[i]) { + // If signature is specified for this argument, use it + Py_INCREF(signature[i]); + new_op_dtypes[i] = signature[i]; + // printf("new_op_dtypes[%d] set to %s (from signature)\n", i, + // get_dtype_name(new_op_dtypes[i])); + } + else { + // Otherwise, use the common dtype + Py_INCREF(common); + new_op_dtypes[i] = common; + // printf("new_op_dtypes[%d] set to %s (from common)\n", i, + // get_dtype_name(new_op_dtypes[i])); + } + } + + Py_XDECREF(common); + // printf("Exiting quad_ufunc_promoter\n"); + return 0; +} template int @@ -196,6 +354,7 @@ create_quad_binary_ufunc(PyObject *numpy, const char *ufunc_name) { PyObject *ufunc = PyObject_GetAttrString(numpy, ufunc_name); if (ufunc == NULL) { + Py_DecRef(ufunc); return -1; } @@ -220,6 +379,25 @@ create_quad_binary_ufunc(PyObject *numpy, const char *ufunc_name) return -1; } + PyObject *promoter_capsule = + PyCapsule_New((void *)&quad_ufunc_promoter, "numpy._ufunc_promoter", NULL); + if (promoter_capsule == NULL) { + return -1; + } + + PyObject *DTypes = PyTuple_Pack(3, &PyArrayDescr_Type, &PyArrayDescr_Type, &PyArrayDescr_Type); + if (DTypes == 0) { + Py_DECREF(promoter_capsule); + return -1; + } + + if (PyUFunc_AddPromoter(ufunc, DTypes, promoter_capsule) < 0) { + Py_DECREF(promoter_capsule); + Py_DECREF(DTypes); + return -1; + } + Py_DECREF(promoter_capsule); + Py_DECREF(DTypes); return 0; } @@ -272,6 +450,22 @@ quad_generic_comp_strided_loop(PyArrayMethod_Context *context, char *const data[ return 0; } +NPY_NO_EXPORT int +comparison_ufunc_promoter(PyUFuncObject *ufunc, PyArray_DTypeMeta *op_dtypes[], + PyArray_DTypeMeta *signature[], PyArray_DTypeMeta *new_op_dtypes[]) +{ + PyArray_DTypeMeta *new_signature[NPY_MAXARGS]; + + memcpy(new_signature, signature, 3 * sizeof(PyArray_DTypeMeta *)); + new_signature[2] = NULL; + int res = quad_ufunc_promoter(ufunc, op_dtypes, new_signature, new_op_dtypes); + if (res < 0) { + return -1; + } + Py_XSETREF(new_op_dtypes[2], &PyArray_BoolDType); + return 0; +} + template int create_quad_comparison_ufunc(PyObject *numpy, const char *ufunc_name) @@ -300,6 +494,26 @@ create_quad_comparison_ufunc(PyObject *numpy, const char *ufunc_name) return -1; } + PyObject *promoter_capsule = + PyCapsule_New((void *)&comparison_ufunc_promoter, "numpy._ufunc_promoter", NULL); + if (promoter_capsule == NULL) { + return -1; + } + + PyObject *DTypes = PyTuple_Pack(3, &PyArrayDescr_Type, &PyArrayDescr_Type, &PyArray_BoolDType); + if (DTypes == 0) { + Py_DECREF(promoter_capsule); + return -1; + } + + if (PyUFunc_AddPromoter(ufunc, DTypes, promoter_capsule) < 0) { + Py_DECREF(promoter_capsule); + Py_DECREF(DTypes); + return -1; + } + Py_DECREF(promoter_capsule); + Py_DECREF(DTypes); + return 0; } From fb04515ab0ec6a51e601face66981f6e8c01a0f9 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 16 Aug 2024 17:36:47 +0530 Subject: [PATCH 42/43] fixed memory issues and more ufuncs --- quaddtype/quaddtype/src/ops.hpp | 14 ++++++++++++++ quaddtype/quaddtype/src/umath.cpp | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/quaddtype/quaddtype/src/ops.hpp b/quaddtype/quaddtype/src/ops.hpp index 8cc3b401..6a3511c4 100644 --- a/quaddtype/quaddtype/src/ops.hpp +++ b/quaddtype/quaddtype/src/ops.hpp @@ -153,6 +153,20 @@ quad_mod(Sleef_quad *res, Sleef_quad *a, Sleef_quad *b) return 0; } +static inline int +quad_minimum(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) +{ + *out = Sleef_icmpleq1(*in1, *in2) ? *in1 : *in2; + return 0; +} + +static inline int +quad_maximum(Sleef_quad *out, Sleef_quad *in1, Sleef_quad *in2) +{ + *out = Sleef_icmpgeq1(*in1, *in2) ? *in1 : *in2; + return 0; +} + // comparison functions typedef npy_bool (*cmp_def)(const Sleef_quad *, const Sleef_quad *); diff --git a/quaddtype/quaddtype/src/umath.cpp b/quaddtype/quaddtype/src/umath.cpp index 0da945bc..9007f890 100644 --- a/quaddtype/quaddtype/src/umath.cpp +++ b/quaddtype/quaddtype/src/umath.cpp @@ -422,6 +422,12 @@ init_quad_binary_ops(PyObject *numpy) if (create_quad_binary_ufunc(numpy, "mod") < 0) { return -1; } + if (create_quad_binary_ufunc(numpy, "minimum") < 0) { + return -1; + } + if (create_quad_binary_ufunc(numpy, "maximum") < 0) { + return -1; + } return 0; } From c76cf36c07415f53f037c21a2c41ad0cca90bd9a Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Fri, 16 Aug 2024 18:10:51 +0530 Subject: [PATCH 43/43] fixed destructor for bad memory access --- quaddtype/quaddtype/src/scalar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype/quaddtype/src/scalar.c b/quaddtype/quaddtype/src/scalar.c index b4bf3a2e..15f727d8 100644 --- a/quaddtype/quaddtype/src/scalar.c +++ b/quaddtype/quaddtype/src/scalar.c @@ -101,7 +101,7 @@ static PyObject * QuadPrecision_repr(QuadPrecisionObject* self) static void quad_dealloc(QuadPrecDTypeObject *self) { - PyArrayDescr_Type.tp_dealloc((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } PyTypeObject QuadPrecision_Type =