Skip to content

Example 4: Pipelines: which kernels run, and trying every one

Every object has a pipeline: one value per processing step (decode, mask, untranspose, collect, and for matrix objects route and dot). None means the best guess for this CPU and data; it is resolved when the object is made and kept in .pipeline. describe() names the values, and with no argument lists every value and whether this machine can run it. This example runs every usable value of every step, alone with the rest chosen automatically, and checks that each gives exactly the pixels the default gives. (The combinations are tested by the test-suite, test_every_pipeline.py.)

Source: examples/ex4_pipelines.py, run as python examples/ex4_pipelines.py, or download the notebook (figures and output as run when this page was built).

Output, from running it when this page was built, on a synthetic stand-in for your files (powder rings with noise on an Eiger-like detector, tools/standin.py; with your own data the numbers are yours):

import h5py
import hdf5plugin  # registers the bitshuffle filter, so that h5py can decompress too
import numpy as np

import bslz4_to_sparse
h5filename = "eiger_0000.h5"  # your file: bitshuffle-compressed, one frame per chunk
h5dataset = "/entry_0000/ESRF-ID11/eiger/data"  # the (nframes, ni, nj) dataset inside it
mask_filename = (
    "mask.h5"  # your detector mask: a dataset "mask", pyFAI's convention (non-zero = masked)
)
with h5py.File(h5filename, "r") as h5:
    ds = h5[h5dataset]
    dtype, codec = ds.dtype, bslz4_to_sparse.detect_codec(ds)
    chunks = [ds.id.read_direct_chunk((i, 0, 0))[1] for i in range(4)]
with h5py.File(mask_filename, "r") as h5:
    bad = h5["mask"][()] != 0  # the masked pixels
mask = (~bad).astype(np.uint8)  # ours: 1 = use this pixel
cut = 3
c2s = bslz4_to_sparse.chunk2sparse(mask, dtype=dtype, codec=codec)
print("automatic pipeline:", bslz4_to_sparse.describe(c2s.pipeline))
npx, (values, indices) = c2s.multi(chunks, cut)
want = [(values[i, : npx[i]].copy(), indices[i, : npx[i]].copy()) for i in range(len(chunks))]
automatic pipeline: {'decode': 'lz4-band', 'mask': 'pixel', 'untranspose': 'lowplanes-vbmi', 'collect': 'avx512cs', 'route': None, 'dot': None}
print(
    "\nevery value of every step this machine can run, alone (step, value, and whether its pixels equal the default's):"
)
tried = same = 0
for step, values_of_step in bslz4_to_sparse.describe().items():
    if step in (
        "route",
        "dot",
    ):  # these steps belong to the matrix product: see test_every_pipeline.py
        print("  %-12s (only for chunk2sparseCSC objects, which need a matrix)" % step)
        continue
    for _, name, usable, needs in values_of_step:
        if not usable:
            print("  %-12s %-18s not on this CPU or build (%s)" % (step, name, needs))
            continue
        try:
            c = bslz4_to_sparse.chunk2sparse(mask, dtype=dtype, codec=codec, pipeline={step: name})
        except ValueError as e:  # not for this data (the dtype, say), or for matrix objects only
            print("  %-12s %-18s refused: %s" % (step, name, str(e)[:60]))
            continue
        n, (v, ix) = c.multi(chunks, cut)
        ok = all(
            (v[i, : n[i]] == want[i][0]).all() and (ix[i, : n[i]] == want[i][1]).all()
            for i in range(len(chunks))
        )
        tried += 1
        same += ok
        print("  %-12s %-18s %s" % (step, name, "same" if ok else "DIFFERENT"))
print("\n%d pipelines run, %d identical to the default" % (tried, same))
assert tried == same
every value of every step this machine can run, alone (step, value, and whether its pixels equal the default's):
  decode       lz4-band           same
  decode       lz4-zero           same
  decode       lz4-stock          same
  decode       zstd               refused: pipeline['decode']='zstd': the data is lz4 compressed (use l
  decode       pixels             refused: pipeline['decode']='pixels' is for uncompressed frames: call
  mask         pixel              same
  mask         planes             same
  mask         none               refused: pipeline['mask']='none': the mask has 38110 masked pixels (u
  untranspose  lowplanes-vbmi     same
  untranspose  lowplanes-avx2     same
  untranspose  lowplanes-vsx      not on this CPU or build (u16 pixels; POWER8 VSX)
  untranspose  lowplanes-c        same
  untranspose  kcb                same
  untranspose  neon               not on this CPU or build (bitshuffle's NEON kernels; aarch64)
  untranspose  scalar             same
  collect      avx512cs           same
  collect      avx2cs             same
  collect      vsx                not on this CPU or build (u16/u32 pixels; POWER VSX)
  collect      neon               not on this CPU or build (u16/u32 pixels; aarch64 NEON)
  collect      sse2               same
  collect      scalar             same
  route        (only for chunk2sparseCSC objects, which need a matrix)
  dot          (only for chunk2sparseCSC objects, which need a matrix)

14 pipelines run, 14 identical to the default

a value the CPU or the data cannot use is refused when the object is made

try:
    bslz4_to_sparse.chunk2sparse(mask, dtype=np.uint32, pipeline={"untranspose": "lowplanes-c"})
except ValueError as e:
    print("\nrefused:", e)
refused: pipeline['untranspose']='lowplanes-c' is for u16 pixels, not uint32 (use kcb)

Generated by tools/generate_docs.py from bslz4_to_sparse 0.0.22a2 (git v0.0.22-13-g610ed27, sources sha256 6929bd75ab7a).