Skip to content

Commit 2cbc0c6

Browse files
authored
Merge pull request #220 from SwayamInSync/219
2 parents 8487df3 + 70f5fdc commit 2cbc0c6

File tree

3 files changed

+200
-1
lines changed

3 files changed

+200
-1
lines changed

quaddtype/numpy_quaddtype/src/casts.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,9 @@ template <>
502502
inline npy_byte
503503
from_quad<npy_byte>(quad_value x, QuadBackendType backend)
504504
{
505+
// runtime warnings often comes from/to casting of NaN, inf
506+
// casting is used by ops at several positions leading to warnings
507+
// fix can be catching the cases and returning corresponding type value without casting
505508
if (backend == BACKEND_SLEEF) {
506509
return (npy_byte)Sleef_cast_to_int64q1(x.sleef_value);
507510
}

quaddtype/numpy_quaddtype/src/umath/comparison_ops.cpp

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
#include "binary_ops.h"
2424
#include "comparison_ops.h"
2525

26-
2726
static NPY_CASTING
2827
quad_comparison_op_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[],
2928
PyArray_Descr *const given_descrs[],
@@ -145,6 +144,117 @@ quad_generic_comp_strided_loop_aligned(PyArrayMethod_Context *context, char *con
145144
}
146145
return 0;
147146
}
147+
// todo: It'll be better to generate separate templates for aligned and unaligned loops
148+
// Resolve desc and strided loops for logical reduction (Bool, Quad) => Bool
149+
static NPY_CASTING
150+
quad_comparison_reduce_resolve_descriptors(PyObject *self, PyArray_DTypeMeta *const dtypes[],
151+
PyArray_Descr *const given_descrs[],
152+
PyArray_Descr *loop_descrs[],
153+
npy_intp *NPY_UNUSED(view_offset))
154+
{
155+
NPY_CASTING casting = NPY_SAFE_CASTING;
156+
157+
for (int i = 0; i < 2; i++) {
158+
Py_INCREF(given_descrs[i]);
159+
loop_descrs[i] = given_descrs[i];
160+
}
161+
162+
// Set up output descriptor
163+
loop_descrs[2] = PyArray_DescrFromType(NPY_BOOL);
164+
if (!loop_descrs[2]) {
165+
return (NPY_CASTING)-1;
166+
}
167+
return casting;
168+
}
169+
170+
template <cmp_quad_def sleef_comp, cmp_londouble_def ld_comp>
171+
int
172+
quad_reduce_comp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data[],
173+
npy_intp const dimensions[], npy_intp const strides[],
174+
NpyAuxData *auxdata)
175+
{
176+
npy_intp N = dimensions[0];
177+
char *in1_ptr = data[0]; // bool
178+
char *in2_ptr = data[1]; // quad
179+
char *out_ptr = data[2]; // bool
180+
npy_intp in1_stride = strides[0];
181+
npy_intp in2_stride = strides[1];
182+
npy_intp out_stride = strides[2];
183+
184+
QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[1];
185+
QuadBackendType backend = descr->backend;
186+
while (N--) {
187+
npy_bool in1 = *(npy_bool *)in1_ptr;
188+
quad_value in1_quad;
189+
quad_value in2;
190+
191+
npy_bool result;
192+
193+
if (backend == BACKEND_SLEEF) {
194+
in1_quad.sleef_value = Sleef_cast_from_int64q1(in1);
195+
in2.sleef_value = *(Sleef_quad *)in2_ptr;
196+
result = sleef_comp(&in1_quad.sleef_value, &in2.sleef_value);
197+
}
198+
else {
199+
in1_quad.longdouble_value = static_cast<long double>(in1);
200+
in2.longdouble_value = *(long double *)in2_ptr;
201+
result = ld_comp(&in1_quad.longdouble_value, &in2.longdouble_value);
202+
}
203+
204+
*(npy_bool *)out_ptr = result;
205+
206+
in1_ptr += in1_stride;
207+
in2_ptr += in2_stride;
208+
out_ptr += out_stride;
209+
}
210+
return 0;
211+
}
212+
213+
template <cmp_quad_def sleef_comp, cmp_londouble_def ld_comp>
214+
int
215+
quad_reduce_comp_strided_loop_unaligned(PyArrayMethod_Context *context, char *const data[],
216+
npy_intp const dimensions[], npy_intp const strides[],
217+
NpyAuxData *auxdata)
218+
{
219+
npy_intp N = dimensions[0];
220+
char *in1_ptr = data[0]; // bool
221+
char *in2_ptr = data[1]; // quad
222+
char *out_ptr = data[2]; // bool
223+
npy_intp in1_stride = strides[0];
224+
npy_intp in2_stride = strides[1];
225+
npy_intp out_stride = strides[2];
226+
227+
QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[1];
228+
QuadBackendType backend = descr->backend;
229+
size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double);
230+
231+
npy_bool in1;
232+
quad_value in1_quad, in2;
233+
while (N--) {
234+
memcpy(&in1, in1_ptr, sizeof(npy_bool));
235+
if(backend == BACKEND_SLEEF)
236+
in1_quad.sleef_value = Sleef_cast_from_int64q1(in1);
237+
else
238+
in1_quad.longdouble_value = static_cast<long double>(in1);
239+
memcpy(&in2, in2_ptr, elem_size);
240+
npy_bool result;
241+
242+
if (backend == BACKEND_SLEEF) {
243+
result = sleef_comp(&in1_quad.sleef_value, &in2.sleef_value);
244+
}
245+
else {
246+
result = ld_comp(&in1_quad.longdouble_value, &in2.longdouble_value);
247+
}
248+
249+
memcpy(out_ptr, &result, sizeof(npy_bool));
250+
251+
in1_ptr += in1_stride;
252+
in2_ptr += in2_stride;
253+
out_ptr += out_stride;
254+
}
255+
return 0;
256+
}
257+
148258

149259
NPY_NO_EXPORT int
150260
comparison_ufunc_promoter(PyUFuncObject *ufunc, PyArray_DTypeMeta *op_dtypes[],
@@ -194,6 +304,31 @@ create_quad_comparison_ufunc(PyObject *numpy, const char *ufunc_name)
194304
return -1;
195305
}
196306

307+
// registering the reduce methods
308+
PyArray_DTypeMeta *dtypes_reduce[3] = {&PyArray_BoolDType, &QuadPrecDType, &PyArray_BoolDType};
309+
310+
PyType_Slot slots_reduce[] = {
311+
{NPY_METH_resolve_descriptors, (void *)&quad_comparison_reduce_resolve_descriptors},
312+
{NPY_METH_strided_loop,
313+
(void *)&quad_reduce_comp_strided_loop_aligned<sleef_comp, ld_comp>},
314+
{NPY_METH_unaligned_strided_loop,
315+
(void *)&quad_reduce_comp_strided_loop_unaligned<sleef_comp, ld_comp>},
316+
{0, NULL}};
317+
318+
PyArrayMethod_Spec Spec_reduce = {
319+
.name = "quad_comp",
320+
.nin = 2,
321+
.nout = 1,
322+
.casting = NPY_SAFE_CASTING,
323+
.flags = NPY_METH_SUPPORTS_UNALIGNED,
324+
.dtypes = dtypes_reduce,
325+
.slots = slots_reduce,
326+
};
327+
328+
if (PyUFunc_AddLoopFromSpec(ufunc, &Spec_reduce) < 0) {
329+
return -1;
330+
}
331+
197332
PyObject *promoter_capsule =
198333
PyCapsule_New((void *)&comparison_ufunc_promoter, "numpy._ufunc_promoter", NULL);
199334
if (promoter_capsule == NULL) {

quaddtype/tests/test_quaddtype.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,67 @@ def test_array_minmax(op, a, b):
507507
assert np.signbit(float_res) == np.signbit(
508508
quad_res), f"Zero sign mismatch for {op}({a}, {b})"
509509

510+
class TestComparisonReductionOps:
511+
"""Test suite for comparison reduction operations on QuadPrecision arrays."""
512+
513+
@pytest.mark.parametrize("op", ["all", "any"])
514+
@pytest.mark.parametrize("input_array", [
515+
(["1.0", "2.0", "3.0"]),
516+
(["1.0", "0.0", "3.0"]),
517+
(["0.0", "0.0", "0.0"]),
518+
# Including negative zero
519+
(["-0.0", "0.0"]),
520+
# Including NaN (should be treated as true)
521+
(["nan", "1.0"]),
522+
(["nan", "0.0"]),
523+
(["nan", "nan"]),
524+
# inf cases
525+
(["inf", "1.0"]),
526+
(["-inf", "0.0"]),
527+
(["inf", "-inf"]),
528+
# Mixed cases
529+
(["1.0", "-0.0", "nan", "inf"]),
530+
(["0.0", "-0.0", "nan", "-inf"]),
531+
])
532+
def test_reduction_ops(self, op, input_array):
533+
"""Test all and any reduction operations."""
534+
quad_array = np.array([QuadPrecision(x) for x in input_array])
535+
float_array = np.array([float(x) for x in input_array])
536+
op = getattr(np, op)
537+
result = op(quad_array)
538+
expected = op(float_array)
539+
540+
assert result == expected, (
541+
f"Reduction op '{op}' failed for input {input_array}: "
542+
f"expected {expected}, got {result}"
543+
)
544+
545+
@pytest.mark.parametrize("val_str", [
546+
"0.0",
547+
"-0.0",
548+
"1.0",
549+
"-1.0",
550+
"nan",
551+
"inf",
552+
"-inf",
553+
])
554+
def test_scalar_reduction_ops(self, val_str):
555+
"""Test reduction operations on scalar QuadPrecision values."""
556+
quad_val = QuadPrecision(val_str)
557+
float_val = np.float64(val_str)
558+
559+
result_all = quad_val.all()
560+
expected_all_result = float_val.all()
561+
assert result_all == expected_all_result, (
562+
f"Scalar all failed for {val_str}: expected {expected_all_result}, got {result_all}"
563+
)
564+
565+
result_any = quad_val.any()
566+
expected_any_result = float_val.any()
567+
assert result_any == expected_any_result, (
568+
f"Scalar any failed for {val_str}: expected {expected_any_result}, got {result_any}"
569+
)
570+
510571

511572
# Logical operations tests
512573
@pytest.mark.parametrize("op", ["logical_and", "logical_or", "logical_xor"])

0 commit comments

Comments
 (0)