jni / skan

Python module to analyse skeleton (thin object) images
https://skeleton-analysis.org
BSD 3-Clause "New" or "Revised" License
118 stars 39 forks source link

Allow mean pixel value causes Numba type error #234

Open ns-rse opened 2 days ago

ns-rse commented 2 days ago

Thanks for the new release @jni :smile:

Unfortunately I've found a problem with a type error being raised from within Numba stemming from initalising the csr.Skeleton() class in skan-0.12.0.

Traceback

The traceback is...

tests/tracing/test_disordered_tracing.py:1265:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
topostats/tracing/disordered_tracing.py:660: in disordered_trace_grain
    "branch_types": get_skan_image(
topostats/tracing/disordered_tracing.py:697: in get_skan_image
    skan_skeleton = skan.Skeleton(skeleton_image, spacing=1e-9, value_is_height=True)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/skan/csr.py:532: in __init__
    self.nbgraph = csr_to_nbgraph(graph, self.pixel_values)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/skan/csr.py:205: in csr_to_nbgraph
    return NBGraph(
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/experimental/jitclass/base.py:124: in __call__
    return cls._ctor(*bind.args[1:], **bind.kwargs)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:442: in _compile_for_args
    raise e
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:375: in _compile_for_args
    return_val = self.compile(tuple(argtypes))
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:905: in compile
    cres = self._compiler.compile(args, return_type)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:80: in compile
    status, retval = self._compile_cached(args, return_type)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:94: in _compile_cached
    retval = self._compile_core(args, return_type)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:107: in _compile_core
    cres = compiler.compile_extra(self.targetdescr.typing_context,
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:744: in compile_extra
    return pipeline.compile_extra(func)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:438: in compile_extra
    return self._compile_bytecode()
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:506: in _compile_bytecode
    return self._compile_core()
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:481: in _compile_core
    raise e
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:472: in _compile_core
    pm.run(self.state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:364: in run
    raise e
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:356: in run
    self._runPass(idx, pass_inst, state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_lock.py:35: in _acquire_compile_lock
    return func(*args, **kwargs)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:311: in _runPass
    mutated |= check(pss.run_pass, internal_state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:273: in check
    mangled = func(compiler_state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typed_passes.py:112: in run_pass
    typemap, return_type, calltypes, errs = type_inference_stage(
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typed_passes.py:93: in type_inference_stage
    errs = infer.propagate(raise_errors=raise_errors)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:1083: in propagate
    errors = self.constraints.propagate(self)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:182: in propagate
    raise e
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:160: in propagate
    constraint(typeinfer)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:583: in __call__
    self.resolve(typeinfer, typevars, fnty)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:606: in resolve
    sig = typeinfer.resolve_call(fnty, pos_args, kw_args)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typeinfer.py:1577: in resolve_call
    return self.context.resolve_function_type(fnty, pos_args, kw_args)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typing/context.py:196: in resolve_function_type
    res = self._resolve_user_function_type(func, args, kws)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typing/context.py:248: in _resolve_user_function_type
    return func.get_call_type(self, args, kws)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/types/misc.py:441: in get_call_type
    return self.ctor_template(context).apply(args, kws)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typing/templates.py:350: in apply
    sig = generic(args, kws)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/experimental/jitclass/base.py:269: in generic
    sig = disp_type.get_call_type(self.context, boundargs, kws)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/types/functions.py:541: in get_call_type
    self.dispatcher.get_call_template(args, kws)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:318: in get_call_template
    self.compile(tuple(args))
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:905: in compile
    cres = self._compiler.compile(args, return_type)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:80: in compile
    status, retval = self._compile_cached(args, return_type)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:94: in _compile_cached
    retval = self._compile_core(args, return_type)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/dispatcher.py:107: in _compile_core
    cres = compiler.compile_extra(self.targetdescr.typing_context,
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:744: in compile_extra
    return pipeline.compile_extra(func)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:438: in compile_extra
    return self._compile_bytecode()
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:506: in _compile_bytecode
    return self._compile_core()
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:481: in _compile_core
    raise e
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler.py:472: in _compile_core
    pm.run(self.state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:364: in run
    raise e
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:356: in run
    self._runPass(idx, pass_inst, state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_lock.py:35: in _acquire_compile_lock
    return func(*args, **kwargs)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:311: in _runPass
    mutated |= check(pss.run_pass, internal_state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/compiler_machinery.py:273: in check
    mangled = func(compiler_state)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/typed_passes.py:468: in run_pass
    lower.lower()
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:187: in lower
    self.lower_normal_function(self.fndesc)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:226: in lower_normal_function
    entry_block_tail = self.lower_function_body()
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:256: in lower_function_body
    self.lower_block(block)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:270: in lower_block
    self.lower_inst(inst)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/lowering.py:564: in lower_inst
    return impl(self.builder, (target, value))
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/base.py:627: in wrapped
    return impl(self, builder, sig, args, attr)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/imputils.py:166: in res
    return real_impl(context, builder, sig, args, attr)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/experimental/jitclass/base.py:505: in set_attr_impl
    setattr(data, _mangle_attr(attr), val)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/cgutils.py:164: in __setattr__
    self[self._datamodel.get_field_position(field)] = value
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/cgutils.py:178: in __setitem__
    value = self._cast_member_from_value(index, value)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/cgutils.py:251: in _cast_member_from_value
    return model.as_data(self._builder, val)
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/numba/core/datamodel/models.py:586: in as_data
    struct = builder.insert_value(struct, el, [i])
/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/llvmlite/ir/builder.py:983: in insert_value
    instr = instructions.InsertValue(self.block, agg, value, idx, name=name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[AttributeError("'InsertValue' object has no attribute '_name'") raised in repr()] InsertValue object at 0x7ccfbd4f6b90>, parent = <ir.Block 'B0' of type 'label'>
agg = <ir.InsertValue '.258' of type '{i8*, i8*, i64, i64, double*, [1 x i64], [1 x i64]}', opname 'insertvalue', operands [...value=<llvmlite.ir.values._Undefined object at 0x7cd0142cda50>>, <ir.Argument 'arg.node_props.6.0' of type i64>]>]>]>]>
elem = <ir.ExtractValue 'extracted.data.22' of type 'float*', opname 'extractvalue', operands [<ir.InsertValue 'inserted.stri...' value=<llvmlite.ir.values._Undefined object at 0x7cd0142cda50>>, <ir.Argument 'arg.node_props.6.0' of type i64>]>]>]>
indices = [4], name = ''

    def __init__(self, parent, agg, elem, indices, name=''):
        typ = agg.type
        try:
            for i in indices:
                typ = typ.elements[i]
        except (AttributeError, IndexError):
            raise TypeError("Can't index at %r in %s"
                            % (list(indices), agg.type))
        if elem.type != typ:
>           raise TypeError("Can only insert %s at %r in %s: got %s"
                            % (typ, list(indices), agg.type, elem.type))
E           TypeError: Can only insert double* at [4] in {i8*, i8*, i64, i64, double*, [1 x i64], [1 x i64]}: got float*

/home/neil/.virtualenvs/topostats-311b/lib/python3.11/site-packages/llvmlite/ir/instructions.py:702: TypeError
__________________________________________________________________________________________ tests/test_run_topostats.py ___________________________________________________________________________________________

git bisect

I don't see this error with skan-0.11.1 and so to investigate I undertook git bisect between v0.12.0 and v0.11.1. To check each bisection I ran the test that is failing from the TopoStats main branch.

git clone git@github.com:AFM-SPM/TopoStats.git
cd TopoStats
pip install -e .[tests,dev]
pytest tests/tracing/test_disordered_tracing.py::test_disordered_trace_grain  # Test run on each bisect
git bisect start v0.12.0 v0.11.1
Bisecting: 6 revisions left to test after this (roughly 3 steps)
[1aee19416bf9674ece696e29d7c350f720dc39cb] Convert '-' to '_' in summary dataframe (#215)

git bisect bad
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[91d4e7c57b3136138321bb191b56ee6df508f791] Improved error reporting and tests for prune_paths() methods (#212)

git bisect good
Bisecting: 1 revision left to test after this (roughly 1 step)
[20ab48dd7c033c01aca0eac7a046ff4f86309632] Update formatting on pyproject.toml to match skimage (#218)

git bisect good
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[e248c5105c5bf60e82f752cbc02a43ab47f9ab75] Allow mean pixel value inference from int arrays, not just float (#220)

git bisect bad 
e248c5105c5bf60e82f752cbc02a43ab47f9ab75 is the first bad commit
commit e248c5105c5bf60e82f752cbc02a43ab47f9ab75 (HEAD)
Author: Tim Monko <47310455+TimMonko@users.noreply.github.com>
Date:   2023-10-30 02:23:40 -0500

    Allow mean pixel value inference from int arrays, not just float (#220)

    1. Now only passes None to self.pixel_values i ncsr.py if subdtype is
    np.bool_ rather than only collecting pixel_values when subdtype is
    np.float_ allowing for pixel_values to be obtained from int and uint
    images.
    2. add skeletonlabel to _testdata.py to have an example int array
    3. add test_skeletonlabel to test_csr.py to confirm int array behaves as
    expected with csr.summarize

    ---------

    Co-authored-by: Juan Nunez-Iglesias <jni@fastmail.com>

 pyproject.toml            | 4 ++--
 src/skan/_testdata.py     | 7 +++++++
 src/skan/csr.py           | 4 +++-
 src/skan/test/test_csr.py | 7 ++++++-
 4 files changed, 18 insertions(+), 4 deletions(-)

The error is different from that reported by v0.12.0 though...


self = <skan.csr.Skeleton object at 0x7b343f413210>
skeleton_image = array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0.],
       [0., 0., 0., 0..., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0.]], dtype=float32)

    def __init__(
            self,
            skeleton_image,
            *,
            spacing=1,
            source_image=None,
            keep_images=True,
            value_is_height=False,
            ):
        graph, coords = skeleton_to_csgraph(
                skeleton_image,
                spacing=spacing,
                value_is_height=value_is_height,
                )
        if np.issubdtype(skeleton_image.dtype, np.float_):
            self.pixel_values = skeleton_image[coords]
        elif np.issubdtype(skeleton_image.dtype, np.int_):
            self.pixel_values = skeleton_image.astype(float)[coords]
        elif np.issubdtype(skeleton_image.dtype, np.bool_):
            self.pixel_values = None
        self.graph = graph
>       self.nbgraph = csr_to_nbgraph(graph, self.pixel_values)
E       AttributeError: 'Skeleton' object has no attribute 'pixel_values'

../../jni/skan/src/skan/csr.py:514: AttributeError

...and sure enough if I checkout csr_to_nbgraph() the only parameters it has are csr and node_props and ooking at this commit the only change to src/skan/csr.py is...

                 )
         if np.issubdtype(skeleton_image.dtype, np.float_):
             self.pixel_values = skeleton_image[coords]
-        else:
+        elif np.issubdtype(skeleton_image.dtype, np.int_):
+            self.pixel_values = skeleton_image.astype(float)[coords]
+        elif np.issubdtype(skeleton_image.dtype, np.bool_):
             self.pixel_values = None
         self.graph = graph
         self.nbgraph = csr_to_nbgraph(graph, self.pixel_values)

NBGraph()

csr.csr_to_nbgraph() returns NBGraph which is a numba.experimental.jitclass() with csr_spec_float...

csr_spec_float = [
        ('indptr', numba.int32[:]),
        ('indices', numba.int32[:]),
        ('data', numba.float64[:]),
        ('shape', numba.int32[:]),
        ('node_properties', numba.float64[:]),
        ]  # yapf: disable
NBGraph = numba.experimental.jitclass(NBGraphBase, csr_spec_float)
NBGraphBool = numba.experimental.jitclass(NBGraphBase, csr_spec_bool)

def csr_to_nbgraph(csr, node_props=None):
    if node_props is None:
        node_props = np.broadcast_to(1., csr.shape[0])
        node_props.flags.writeable = True
    return NBGraph(
            csr.indptr, csr.indices, csr.data,
            np.array(csr.shape, dtype=np.int32), node_props
            )

I thought perhaps the numba.float64 types were the problem but looking at the numba documentation on types shows that float64 and double are equivalent. Regardless I tried switching numba.float64 > numba.double but still get the TypeError: Can only insert double* at [4] in {i8*, i8*, i64, i64, double*, [1 x i64], [1 x i64]}: got float*.

Current installed packages...

pip freeze
absl-py==2.1.0
accessible-pygments==0.0.5
afmformats==0.18.0
AFMReader==0.0.1
alabaster==1.0.0
anyio==4.6.0
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
art==6.3
astroid==3.3.5
asttokens==2.4.1
astunparse==1.6.3
async-lru==2.0.4
attrs==24.2.0
autopep8==2.0.4
babel==2.16.0
backoff==1.11.1
beautifulsoup4==4.12.3
biopython==1.84
black==24.8.0
bleach==6.1.0
cachetools==4.2.4
cattrs==24.1.2
certifi==2024.8.30
cffi==1.17.1
cfgv==3.4.0
charset-normalizer==3.3.2
cheap_repr==0.5.2
click==7.1.2
colorama==0.4.6
comm==0.2.2
contourpy==1.3.0
coverage==7.6.1
cycler==0.12.1
debugpy==1.8.6
decorator==5.1.1
defusedxml==0.7.1
dill==0.3.9
distlib==0.3.8
docstring-to-markdown==0.15
docstring_parser==0.16
docutils==0.16
et-xmlfile==1.1.0
execnet==2.1.1
executing==2.1.0
fastjsonschema==2.20.0
filelock==3.16.1
filetype==1.2.0
flake8==7.1.1
flatbuffers==24.3.25
fonttools==4.54.1
fqdn==1.5.1
gast==0.6.0
google-auth==1.35.0
google-auth-oauthlib==0.5.3
google-pasta==0.2.0
grpcio==1.66.2
h11==0.14.0
h5py==3.12.1
howfairis==0.14.2
httpcore==1.0.6
httpx==0.27.2
icdiff==2.0.7
identify==2.6.1
idna==3.10
igor2==0.5.8
imageio==2.35.1
imagesize==1.4.1
importlib_metadata==8.5.0
iniconfig==2.0.0
ipykernel==6.29.5
ipython==8.28.0
ipython-genutils==0.2.0
ipywidgets==8.1.5
isoduration==20.11.0
isort==5.13.2
jedi==0.19.1
jedi-language-server==0.41.4
Jinja2==3.1.4
joblib==1.4.2
jprops==2.0.2
json5==0.9.25
jsonpointer==3.0.0
jsonschema==4.23.0
jsonschema-specifications==2023.12.1
jupyter==1.1.1
jupyter-console==6.6.3
jupyter-events==0.10.0
jupyter-highlight-selected-word==0.2.0
jupyter-lsp==2.2.5
jupyter_client==8.6.3
jupyter_contrib_core==0.4.2
jupyter_contrib_nbextensions==0.7.0
jupyter_core==5.7.2
jupyter_nbextensions_configurator==0.6.4
jupyter_server==2.14.2
jupyter_server_terminals==0.5.3
jupyterlab==4.2.5
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==3.0.13
jupyterthemes==0.20.0
jupytext==1.16.4
keras==3.6.0
kiwisolver==1.4.7
lazy_loader==0.4
lesscpy==0.15.1
libclang==18.1.1
llvmlite==0.43.0
loguru==0.7.2
lsprotocol==2023.0.1
lxml==5.3.0
magicgui==0.9.1
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==2.1.5
matplotlib==3.9.2
matplotlib-inline==0.1.7
mccabe==0.7.0
mdit-py-plugins==0.4.2
mdurl==0.1.2
mistune==3.0.2
ml-dtypes==0.4.1
mypy==1.11.2
mypy-extensions==1.0.0
myst==1.0.4
myst-parser==4.0.0
namex==0.0.8
nbclient==0.10.0
nbconvert==7.16.4
nbformat==5.10.4
nbmultitask==0.1.0
nest-asyncio==1.6.0
networkx==3.3
nodeenv==1.9.1
notebook==7.2.2
notebook_shim==0.2.4
numba==0.60.0
numpy==1.26.4
numpydoc==1.8.0
numpyencoder==0.3.0
oauthlib==3.2.2
openpyxl==3.1.5
opt_einsum==3.4.0
optree==0.13.0
overrides==7.7.0
packaging==24.1
pandas==2.2.3
pandocfilters==1.5.1
parso==0.8.4
pathspec==0.12.1
pexpect==4.9.0
pillow==10.4.0
platformdirs==4.3.6
pluggy==1.5.0
ply==3.11
pockets==0.9.1
pprintpp==0.4.0
pre_commit==4.0.0
prometheus_client==0.21.0
prompt_toolkit==3.0.48
protobuf==4.25.5
psutil==5.9.8
psygnal==0.11.1
ptyprocess==0.7.0
pure_eval==0.2.3
pyasn1==0.6.1
pyasn1_modules==0.4.1
pycodestyle==2.12.1
pyconify==0.1.6
pycparser==2.22
pydata-sphinx-theme==0.15.4
pydocstyle==6.3.0
pyfiglet==1.0.2
pyflakes==3.2.0
pygls==1.3.1
Pygments==2.18.0
pylint==3.3.1
pylsp-mypy==0.6.9
pyparsing==3.1.4
PyQRCode==1.2.1
pyright==1.1.383
pyspm==0.6.2
pytest==8.3.3
pytest-cov==5.0.0
pytest-icdiff==0.9
pytest-mock==3.14.0
pytest-mpl==0.17.0
pytest-pylint==0.21.0
pytest-regtest==2.2.1
pytest-testmon==2.1.1
pytest-xdist==3.6.1
python-dateutil==2.9.0.post0
python-json-logger==2.0.7
python-lsp-jsonrpc==1.1.2
python-lsp-ruff==2.2.2
python-lsp-server==1.12.0
pytoolconfig==1.3.1
pytz==2024.2
PyWavelets==1.7.0
PyYAML==6.0.2
pyzmq==26.2.0
QtPy==2.4.1
ratelimit==2.2.1
referencing==0.35.1
requests==2.32.3
requests-oauthlib==2.0.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rich==13.9.2
rope==1.13.0
rpds-py==0.20.0
rsa==4.9
ruamel.yaml==0.16.13
ruamel.yaml.clib==0.2.8
ruff==0.6.9
ruff-lsp==0.0.57
schema==0.7.7
scikit-image==0.24.0
scikit-learn==1.5.2
scipy==1.14.1
seaborn==0.13.2
segno==1.6.1
Send2Trash==1.8.3
six==1.16.0
-e git+ssh://git@github.com/jni/skan.git@5184bfab84820b4d51f311255fd567376424e9a9#egg=skan
sniffio==1.3.1
snoop==0.6.0
snowballstemmer==2.2.0
soupsieve==2.6
Sphinx==8.0.2
sphinx-autoapi==3.1.0b0
sphinx-autodoc-typehints==2.5.0
sphinx-markdown-tables==0.0.17
sphinx-multiversion==0.2.4
sphinx-rtd-theme==3.0.1
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-mermaid==0.9.2
sphinxcontrib-napoleon==0.7
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
stack-data==0.6.3
superqt==0.6.7
syrupy==4.7.2
tabulate==0.9.0
tenacity==6.3.1
tensorboard==2.17.1
tensorboard-data-server==0.7.2
tensorflow==2.17.0
tensorflow-io-gcs-filesystem==0.37.1
termcolor==2.5.0
terminado==0.18.1
threadpoolctl==3.5.0
tifffile==2024.9.20
tinycss2==1.3.0
tomli==2.0.2
tomlkit==0.13.2
toolz==1.0.0
topoly==1.0.2
-e git+ssh://git@github.com/AFM-SPM/TopoStats.git@4dd47d8ddd37b6dfc0c835c92135ac322ddfd178#egg=topostats
tornado==6.4.1
tqdm==4.66.5
traitlets==5.14.3
types-python-dateutil==2.9.0.20241003
typing_extensions==4.12.2
tzdata==2024.2
ujson==5.10.0
uri-template==1.3.0
urllib3==1.26.20
virtualenv==20.26.6
voluptuous==0.11.7
wcwidth==0.2.13
webcolors==24.8.0
webencodings==0.5.1
websocket-client==1.8.0
Werkzeug==3.0.4
whatthepatch==1.0.6
widgetsnbextension==4.0.13
wrapt==1.16.0
yapf==0.40.2
zipp==3.20.2

A cleaner environment can be found in a failed GitHub workflow of the tests which failed.

I looked at the versions of skan / numba / llvmlite / scipy / numpy and checked whether they were passing...

environment skan numba llvmlite scipy numpy pass
local 0.11.1 0.60.0 0.43.0 1.14.1 1.26.4 Yes
local 0.12.0 0.60.0 0.43.0 1.14.1 1.26.4 No
github 0.11.1 0.60.0 0.43.0 1.13.1 1.26.4 Yes
github 0.12.0 0.60.0 0.43.0 1.14.1 1.26.4 No

Not sure how to continue investigating this but please let me know if there is any more information I can provide or try/do.

jni commented 1 day ago

Haha, well, shit. 😂

I'm on a bit of a conference tour for the next 4 weeks so I'm not 100% if I'll be able to fix quickly, but I'd be happy to quickly merge a fix and push a 0.12.1 tag. Looking at the error my guess is that on some systems skeleton_image.astype(float) is interpreted float32. So the first thing I would try is skeleton_image.astype(np.float64), which would exactly match the NBGraph type annotations.

ns-rse commented 1 day ago

Thanks @jni

Gave the skeleton_image.astype(np.float64) a whirl but no dice.

Spent a bit more time looking through and understanding the workflow though and think I've found the problem which was that node_props in csr_to_nbgraph() was float32. Explicitly setting the type to .astype(np.float64) seems to have solved it and PR is out (see #235).

Thanks for the pointer and enjoy the conference season. :+1: