|
8 | 8 | This is a diagnostic, not a gate: it never raises to the shell (always exits 0), |
9 | 9 | so both wheels' output is captured in the CI log. |
10 | 10 | """ |
| 11 | +import os |
| 12 | +import subprocess |
11 | 13 | import sys |
12 | 14 |
|
| 15 | +try: |
| 16 | + sys.stdout.reconfigure(line_buffering=True) # keep subprocess output in order |
| 17 | +except Exception: # noqa: BLE001 |
| 18 | + pass |
| 19 | + |
13 | 20 | import numpy as np |
14 | 21 | import numpy_quaddtype as nq |
15 | 22 | from numpy_quaddtype import QuadPrecision as Q |
@@ -60,6 +67,30 @@ def run(label, fn): |
60 | 67 | run("float(Q('inf'))==float('inf')", lambda: float(Q("inf")) == float("inf")) |
61 | 68 | run("type(float(Q('inf')))", lambda: type(float(Q("inf"))).__name__) |
62 | 69 |
|
| 70 | +# ---- BRANCH LOCALIZATION: same np.isinf, different input stride/shape --------- |
| 71 | +# The CPU-dispatched DOUBLE_isinf loop branches on the INPUT stride: |
| 72 | +# 0-D scalar -> input stride 0 -> NCONTIG branch |
| 73 | +# 1-D contig -> stride == itemsize -> CONTIG branch (this one works on FT-win) |
| 74 | +# 1-D non-cont -> stride != itemsize, !0 -> NCONTIG branch (stride != 0) |
| 75 | +# Comparing these tells us whether the fault is stride==0 only, any NCONTIG, or the |
| 76 | +# small-count scalar tail in general. |
| 77 | +print("[%s] BRANCH LOCALIZATION (isinf, varying input stride/shape):" % TAG) |
| 78 | +run("0-D isinf(np.array(np.inf))", lambda: bool(np.isinf(np.array(np.inf)))) |
| 79 | +run("1-Dx1 isinf([inf])[0]", lambda: bool(np.isinf(np.array([np.inf]))[0])) |
| 80 | +run("1-Dx2 isinf([inf,inf])[0]", lambda: bool(np.isinf(np.array([np.inf, np.inf]))[0])) |
| 81 | +run("1-Dx9 isinf([inf]*9).all()", lambda: bool(np.isinf(np.array([np.inf] * 9)).all())) |
| 82 | +run("1-Dnc isinf([inf,1][::2])[0]", lambda: bool(np.isinf(np.array([np.inf, 1.0])[::2])[0])) |
| 83 | + |
| 84 | +# Same-dtype unary ufuncs go through the SCALAR FAST PATH (try_trivial_scalar_call), |
| 85 | +# unlike isinf/isnan (bool output) which bail out of it. If these are ALSO wrong, |
| 86 | +# the fast path is implicated; if they are fine, the fault is the normal 0-D path. |
| 87 | +print("[%s] SAME-DTYPE fast-path scalar ufuncs (should be unaffected if 0-D path):" % TAG) |
| 88 | +run("negative(np.inf)", lambda: float(np.negative(np.inf))) |
| 89 | +run("negative(np.float64 inf)", lambda: float(np.negative(np.float64("inf")))) |
| 90 | +run("absolute(np.float64 -inf)", lambda: float(np.absolute(np.float64("-inf")))) |
| 91 | +run("sqrt(np.float64 inf)", lambda: float(np.sqrt(np.float64("inf")))) |
| 92 | +run("signbit(np.float64 -inf)", lambda: bool(np.signbit(np.float64("-inf")))) |
| 93 | + |
63 | 94 | print("[%s] string -> quad (special values + a couple normals):" % TAG) |
64 | 95 | for s in ["inf", "-inf", "nan", "-nan", "Infinity", "1.5", "0.1"]: |
65 | 96 | run("Q(%r)" % s, lambda s=s: "float=%s bits=%s" % (fval(Q(s)), bits(Q(s)))) |
@@ -94,4 +125,33 @@ def _mm(): |
94 | 125 | return fval(np.matmul(A, Im)[0, 0]) |
95 | 126 | run("matmul(inf-matrix, I)[0,0]", _mm) |
96 | 127 |
|
| 128 | +# ---- SIMD-DISABLED re-check: force the baseline (non-dispatched) loop ----------- |
| 129 | +# NPY_DISABLE_CPU_FEATURES is read at import, so re-run the key scalars in a fresh |
| 130 | +# subprocess with every dispatched CPU feature disabled. If scalar isinf/isnan then |
| 131 | +# become correct on FT-Windows, the fault is the CPU-dispatched SIMD DOUBLE_isinf |
| 132 | +# codegen; if still wrong, it is the baseline/generic path. |
| 133 | +print("[%s] SIMD-DISABLED re-check (baseline loop via NPY_DISABLE_CPU_FEATURES):" % TAG) |
| 134 | +try: |
| 135 | + import numpy._core._multiarray_umath as _mu |
| 136 | + _feats = " ".join(getattr(_mu, "__cpu_dispatch__", []) or []) |
| 137 | +except Exception as e: # noqa: BLE001 |
| 138 | + _feats = "" |
| 139 | + print(" could not read __cpu_dispatch__: %s" % e) |
| 140 | +print(" dispatched features being disabled: %s" % (_feats or "(none)")) |
| 141 | +_mini = ( |
| 142 | + "import numpy as np;" |
| 143 | + "print(' [nosimd] numpy', np.__version__);" |
| 144 | + "print(' [nosimd] isinf(np.inf) =', np.isinf(np.inf));" |
| 145 | + "print(' [nosimd] isnan(np.float64 nan) =', np.isnan(np.float64('nan')));" |
| 146 | + "print(' [nosimd] isinf([inf])[0] =', np.isinf(np.array([np.inf]))[0])" |
| 147 | +) |
| 148 | +_env = dict(os.environ) |
| 149 | +if _feats: |
| 150 | + _env["NPY_DISABLE_CPU_FEATURES"] = _feats |
| 151 | +sys.stdout.flush() |
| 152 | +try: |
| 153 | + subprocess.run([sys.executable, "-c", _mini], env=_env, check=False) |
| 154 | +except Exception as e: # noqa: BLE001 |
| 155 | + print(" subprocess failed: %s" % e) |
| 156 | + |
97 | 157 | print("########## end probe [%s] ##########\n" % TAG) |
0 commit comments