|
| 1 | +"""Windows free-threaded isolation probe for numpy-quaddtype. |
| 2 | +
|
| 3 | +Prints the ACTUAL computed values for the operations that fail on the Windows |
| 4 | +cp313t (free-threaded) wheel but pass on cp313 (GIL) and on Linux/macOS. Run the |
| 5 | +SAME script under both interpreters and diff the ``[GIL]`` vs ``[FT]`` blocks to |
| 6 | +see exactly what diverges. |
| 7 | +
|
| 8 | +This is a diagnostic, not a gate: it never raises to the shell (always exits 0), |
| 9 | +so both wheels' output is captured in the CI log. |
| 10 | +""" |
| 11 | +import sys |
| 12 | + |
| 13 | +import numpy as np |
| 14 | +import numpy_quaddtype as nq |
| 15 | +from numpy_quaddtype import QuadPrecision as Q |
| 16 | + |
| 17 | + |
| 18 | +def gil_enabled(): |
| 19 | + f = getattr(sys, "_is_gil_enabled", None) |
| 20 | + return bool(f()) if f is not None else True |
| 21 | + |
| 22 | + |
| 23 | +def fval(x): |
| 24 | + try: |
| 25 | + return float(x) |
| 26 | + except Exception as e: # noqa: BLE001 |
| 27 | + return "<err %s>" % type(e).__name__ |
| 28 | + |
| 29 | + |
| 30 | +def bits(q): |
| 31 | + try: |
| 32 | + return q.__reduce__()[1][0].hex() |
| 33 | + except Exception: # noqa: BLE001 |
| 34 | + return "?" |
| 35 | + |
| 36 | + |
| 37 | +def run(label, fn): |
| 38 | + try: |
| 39 | + print(" %-46s -> %s" % (label, fn())) |
| 40 | + except Exception as e: # noqa: BLE001 |
| 41 | + print(" %-46s -> ERROR %s: %s" % (label, type(e).__name__, e)) |
| 42 | + |
| 43 | + |
| 44 | +TAG = "FT" if not gil_enabled() else "GIL" |
| 45 | + |
| 46 | +print("\n########## quaddtype win-ft probe [%s] ##########" % TAG) |
| 47 | +print("python : %s gil_enabled=%s" % (sys.version.split()[0], gil_enabled())) |
| 48 | +print("numpy : %s" % np.__version__) |
| 49 | +print("quaddtype : %s" % getattr(nq, "__version__", "?")) |
| 50 | +run("is_longdouble_128", nq.is_longdouble_128) |
| 51 | + |
| 52 | +print("[%s] string -> quad (special values + a couple normals):" % TAG) |
| 53 | +for s in ["inf", "-inf", "nan", "-nan", "Infinity", "1.5", "0.1"]: |
| 54 | + run("Q(%r)" % s, lambda s=s: "float=%s bits=%s" % (fval(Q(s)), bits(Q(s)))) |
| 55 | + |
| 56 | +print("[%s] float() cast of constructed specials:" % TAG) |
| 57 | +run("float(Q('inf'))", lambda: fval(Q("inf"))) |
| 58 | +run("np.isinf(float(Q('inf')))", lambda: np.isinf(fval(Q("inf")))) |
| 59 | +run("np.isnan(float(Q('nan')))", lambda: np.isnan(fval(Q("nan")))) |
| 60 | + |
| 61 | +print("[%s] frexp (quad mantissa/exp vs numpy float64):" % TAG) |
| 62 | +for v in ["0.1", "0.9", "1.5", "inf", "nan"]: |
| 63 | + def _fx(v=v): |
| 64 | + q = Q(v) |
| 65 | + qm, qe = np.frexp(q) |
| 66 | + fm, fe = np.frexp(np.float64(fval(q))) |
| 67 | + return "quad=(%s, %s) np=(%s, %s)" % (fval(qm), qe, fm, fe) |
| 68 | + run("frexp(Q(%s))" % v, _fx) |
| 69 | + |
| 70 | +print("[%s] ldexp overflow (expect inf):" % TAG) |
| 71 | +for base, e in [("1.5", 16384), ("2.0", 20000)]: |
| 72 | + run("ldexp(Q(%s), %d)" % (base, e), |
| 73 | + lambda base=base, e=e: fval(np.ldexp(Q(base), e))) |
| 74 | + |
| 75 | +print("[%s] modf:" % TAG) |
| 76 | +run("modf(Q('-0.001'))", |
| 77 | + lambda: tuple(fval(x) for x in np.modf(Q("-0.001")))) |
| 78 | + |
| 79 | +print("[%s] matmul special (Windows uses the naive kernel; QBLAS disabled):" % TAG) |
| 80 | +def _mm(): |
| 81 | + A = np.array([[Q("inf"), Q("1")], [Q("2"), Q("3")]]) |
| 82 | + Im = np.array([[Q("1"), Q("0")], [Q("0"), Q("1")]]) |
| 83 | + return fval(np.matmul(A, Im)[0, 0]) |
| 84 | +run("matmul(inf-matrix, I)[0,0]", _mm) |
| 85 | + |
| 86 | +print("########## end probe [%s] ##########\n" % TAG) |
0 commit comments