From 499163d6766577da1bf654bda747fd6982246bb9 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Tue, 14 Oct 2025 20:53:19 +0530 Subject: [PATCH 1/4] works now --- quaddtype/numpy_quaddtype/src/dtype.c | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/quaddtype/numpy_quaddtype/src/dtype.c b/quaddtype/numpy_quaddtype/src/dtype.c index 1609583a..f4243a6b 100644 --- a/quaddtype/numpy_quaddtype/src/dtype.c +++ b/quaddtype/numpy_quaddtype/src/dtype.c @@ -97,6 +97,13 @@ common_instance(QuadPrecDTypeObject *dtype1, QuadPrecDTypeObject *dtype2) static PyArray_DTypeMeta * common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) { + // Handle Python abstract dtypes (PyLongDType, PyFloatDType) + // These have type_num = -1 + if (other == &PyArray_PyLongDType || other == &PyArray_PyFloatDType) { + Py_INCREF(cls); + return cls; + } + // Promote integer and floating-point types to QuadPrecDType if (other->type_num >= 0 && (PyTypeNum_ISINTEGER(other->type_num) || PyTypeNum_ISFLOAT(other->type_num))) { @@ -261,6 +268,50 @@ quadprec_get_constant(PyArray_Descr *descr, int constant_id, void *ptr) return 1; } +/* + * Fill function. + * The buffer already has the first two elements set: + * buffer[0] = start + * buffer[1] = start + step + * We need to fill buffer[2..length-1] with the arithmetic progression. + */ +static int +quadprec_fill(void *buffer, npy_intp length, void *arr_) +{ + PyArrayObject *arr = (PyArrayObject *)arr_; + QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)PyArray_DESCR(arr); + QuadBackendType backend = descr->backend; + npy_intp i; + + if (length < 2) { + return 0; // Nothing to fill + } + + if (backend == BACKEND_SLEEF) { + Sleef_quad *buf = (Sleef_quad *)buffer; + Sleef_quad start = buf[0]; + Sleef_quad delta = Sleef_subq1_u05(buf[1], start); // delta = buf[1] - start + + for (i = 2; i < length; ++i) { + // buf[i] = start + i * delta + Sleef_quad i_quad = Sleef_cast_from_doubleq1(i); + Sleef_quad i_delta = Sleef_mulq1_u05(i_quad, delta); + buf[i] = Sleef_addq1_u05(start, i_delta); + } + } + else { + long double *buf = (long double *)buffer; + long double start = buf[0]; + long double delta = buf[1] - start; + + for (i = 2; i < length; ++i) { + buf[i] = start + i * delta; + } + } + + return 0; +} + static PyType_Slot QuadPrecDType_Slots[] = { {NPY_DT_ensure_canonical, &ensure_canonical}, {NPY_DT_common_instance, &common_instance}, @@ -270,6 +321,7 @@ static PyType_Slot QuadPrecDType_Slots[] = { {NPY_DT_getitem, &quadprec_getitem}, {NPY_DT_default_descr, &quadprec_default_descr}, {NPY_DT_get_constant, &quadprec_get_constant}, + {NPY_DT_PyArray_ArrFuncs_fill, &quadprec_fill}, {0, NULL}}; static PyObject * From e0aad30020bd0e0368ac4abd673bc7ce5474547d Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Tue, 14 Oct 2025 21:06:21 +0530 Subject: [PATCH 2/4] allow python int/float with default descr --- quaddtype/numpy_quaddtype/src/dtype.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/quaddtype/numpy_quaddtype/src/dtype.c b/quaddtype/numpy_quaddtype/src/dtype.c index f4243a6b..f1a8a433 100644 --- a/quaddtype/numpy_quaddtype/src/dtype.c +++ b/quaddtype/numpy_quaddtype/src/dtype.c @@ -123,14 +123,21 @@ common_dtype(PyArray_DTypeMeta *cls, PyArray_DTypeMeta *other) 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; + if (Py_TYPE(obj) == &QuadPrecision_Type) { + /* QuadPrecision scalar: use its backend */ + QuadPrecisionObject *quad_obj = (QuadPrecisionObject *)obj; + return (PyArray_Descr *)new_quaddtype_instance(quad_obj->backend); } - - QuadPrecisionObject *quad_obj = (QuadPrecisionObject *)obj; - - return (PyArray_Descr *)new_quaddtype_instance(quad_obj->backend); + + /* For Python int/float/other numeric types: return default descriptor */ + /* The casting machinery will handle conversion to QuadPrecision */ + if (PyLong_Check(obj) || PyFloat_Check(obj)) { + return (PyArray_Descr *)new_quaddtype_instance(BACKEND_SLEEF); + } + + /* Unknown type - let NumPy handle it or raise appropriate error */ + PyErr_SetString(PyExc_TypeError, "Can only store QuadPrecision, int, or float in a QuadPrecDType array."); + return NULL; } static int From a89be9c1c803229660c9f8cda11e1de875a6e2b9 Mon Sep 17 00:00:00 2001 From: swayaminsync Date: Tue, 14 Oct 2025 21:20:11 +0530 Subject: [PATCH 3/4] error when unknown type --- quaddtype/numpy_quaddtype/src/dtype.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quaddtype/numpy_quaddtype/src/dtype.c b/quaddtype/numpy_quaddtype/src/dtype.c index f1a8a433..5aa4a9e1 100644 --- a/quaddtype/numpy_quaddtype/src/dtype.c +++ b/quaddtype/numpy_quaddtype/src/dtype.c @@ -135,7 +135,7 @@ quadprec_discover_descriptor_from_pyobject(PyArray_DTypeMeta *NPY_UNUSED(cls), P return (PyArray_Descr *)new_quaddtype_instance(BACKEND_SLEEF); } - /* Unknown type - let NumPy handle it or raise appropriate error */ + /* Unknown type - ERROR */ PyErr_SetString(PyExc_TypeError, "Can only store QuadPrecision, int, or float in a QuadPrecDType array."); return NULL; } From cd1921204e2eaf69e3920ee2fc4ba2bfc6f51858 Mon Sep 17 00:00:00 2001 From: SwayamInSync Date: Wed, 15 Oct 2025 12:30:04 +0000 Subject: [PATCH 4/4] linspace+fill+type promotion tests: --- quaddtype/tests/test_quaddtype.py | 71 +++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/quaddtype/tests/test_quaddtype.py b/quaddtype/tests/test_quaddtype.py index a0b5335b..7914831c 100644 --- a/quaddtype/tests/test_quaddtype.py +++ b/quaddtype/tests/test_quaddtype.py @@ -591,3 +591,74 @@ def test_hyperbolic_functions(op, val): if float_result == 0.0: assert np.signbit(float_result) == np.signbit( quad_result), f"Zero sign mismatch for {op}({val})" + + +class TestTypePomotionWithPythonAbstractTypes: + """Tests for common_dtype handling of Python abstract dtypes (PyLongDType, PyFloatDType)""" + + def test_promotion_with_python_int(self): + """Test that Python int promotes to QuadPrecDType""" + # Create array from Python int + arr = np.array([1, 2, 3], dtype=QuadPrecDType) + assert arr.dtype.name == "QuadPrecDType128" + assert len(arr) == 3 + assert float(arr[0]) == 1.0 + assert float(arr[1]) == 2.0 + assert float(arr[2]) == 3.0 + + def test_promotion_with_python_float(self): + """Test that Python float promotes to QuadPrecDType""" + # Create array from Python float + arr = np.array([1.5, 2.7, 3.14], dtype=QuadPrecDType) + assert arr.dtype.name == "QuadPrecDType128" + assert len(arr) == 3 + np.testing.assert_allclose(float(arr[0]), 1.5, rtol=1e-15) + np.testing.assert_allclose(float(arr[1]), 2.7, rtol=1e-15) + np.testing.assert_allclose(float(arr[2]), 3.14, rtol=1e-15) + + def test_result_dtype_binary_ops_with_python_types(self): + """Test that binary operations between QuadPrecDType and Python scalars return QuadPrecDType""" + quad_arr = np.array([QuadPrecision("1.0"), QuadPrecision("2.0")]) + + # Addition with Python int + result = quad_arr + 5 + assert result.dtype.name == "QuadPrecDType128" + assert float(result[0]) == 6.0 + assert float(result[1]) == 7.0 + + # Multiplication with Python float + result = quad_arr * 2.5 + assert result.dtype.name == "QuadPrecDType128" + np.testing.assert_allclose(float(result[0]), 2.5, rtol=1e-15) + np.testing.assert_allclose(float(result[1]), 5.0, rtol=1e-15) + + def test_concatenate_with_python_types(self): + """Test concatenation handles Python numeric types correctly""" + quad_arr = np.array([QuadPrecision("1.0")]) + # This should work if promotion is correct + int_arr = np.array([2], dtype=np.int64) + + # The result dtype should be QuadPrecDType + result = np.concatenate([quad_arr, int_arr.astype(QuadPrecDType)]) + assert result.dtype.name == "QuadPrecDType128" + assert len(result) == 2 + + +@pytest.mark.parametrize("func,args,expected", [ + # arange tests + (np.arange, (0, 10), list(range(10))), + (np.arange, (0, 10, 2), [0, 2, 4, 6, 8]), + (np.arange, (0.0, 5.0, 0.5), [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]), + (np.arange, (10, 0, -1), [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]), + (np.arange, (-5, 5), list(range(-5, 5))), + # linspace tests + (np.linspace, (0, 10, 11), list(range(11))), + (np.linspace, (0, 1, 5), [0.0, 0.25, 0.5, 0.75, 1.0]), +]) +def test_fill_function(func, args, expected): + """Test quadprec_fill function with arange and linspace""" + arr = func(*args, dtype=QuadPrecDType()) + assert arr.dtype.name == "QuadPrecDType128" + assert len(arr) == len(expected) + for i, exp_val in enumerate(expected): + np.testing.assert_allclose(float(arr[i]), float(exp_val), rtol=1e-15, atol=1e-15)