ossify 0.2.3__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ossify-0.2.3 → ossify-0.2.4}/PKG-INFO +1 -1
- {ossify-0.2.3 → ossify-0.2.4}/pyproject.toml +2 -2
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/__init__.py +1 -1
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/file_io.py +101 -12
- {ossify-0.2.3 → ossify-0.2.4}/.github/workflows/mkdocs_publish.yml +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/.github/workflows/python-package.yml +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/.gitignore +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/.pre-commit-config.yaml +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/LICENSE +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/README.md +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/mkdocs.yml +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/__init__.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/base.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/graph.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/mapping.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/mesh.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/morph.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/points.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/_sync/table.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/algorithms.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/base.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/compartment_models/minnie65_ds15_us0_bd0.json +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/compartment_models/v1dd_ds15_us0_bd0.json +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/compartment_models/xgb/minnie65_ds15_us0_bd0.ubj +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/compartment_models/xgb/v1dd_ds15_us0_bd0.ubj +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/compartments.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/data_layers.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/graph_functions.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/plot.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/plot3d.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/plot_utils.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/structured_prediction.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/sync_classes.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/translate.py +0 -0
- {ossify-0.2.3 → ossify-0.2.4}/src/ossify/utils.py +0 -0
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
allow-direct-references = true
|
|
7
7
|
[project]
|
|
8
8
|
name = "ossify"
|
|
9
|
-
version = "0.2.
|
|
9
|
+
version = "0.2.4"
|
|
10
10
|
description = "Mesh and skeleton analysis"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.11"
|
|
@@ -95,7 +95,7 @@ default-groups = ["dev", "docs", "lint", "profile", "viz"]
|
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
[tool.bumpversion]
|
|
98
|
-
current_version = "0.2.
|
|
98
|
+
current_version = "0.2.4"
|
|
99
99
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
100
100
|
serialize = ["{major}.{minor}.{patch}"]
|
|
101
101
|
regex = false
|
|
@@ -913,6 +913,68 @@ def build_point_cloud(
|
|
|
913
913
|
return cell
|
|
914
914
|
|
|
915
915
|
|
|
916
|
+
def _exact_integer_id_coverage(link_ids: pd.Index, source_ids: pd.Index) -> bool:
|
|
917
|
+
"""Test whether two integer-ID domains are exactly the same set.
|
|
918
|
+
|
|
919
|
+
Both arguments must already be canonicalized ``int64`` pandas ``Index``
|
|
920
|
+
objects. The comparison is order-independent and stays entirely in the
|
|
921
|
+
integer domain -- it never coerces identifiers through ``float64`` -- so it
|
|
922
|
+
is safe for IDs above ``2**53``.
|
|
923
|
+
|
|
924
|
+
Parameters
|
|
925
|
+
----------
|
|
926
|
+
link_ids :
|
|
927
|
+
The linkage column for a candidate source layer.
|
|
928
|
+
source_ids :
|
|
929
|
+
That layer's vertex index.
|
|
930
|
+
|
|
931
|
+
Returns
|
|
932
|
+
-------
|
|
933
|
+
:
|
|
934
|
+
``True`` iff the two contain exactly the same set of IDs.
|
|
935
|
+
"""
|
|
936
|
+
if len(link_ids) != len(source_ids):
|
|
937
|
+
return False
|
|
938
|
+
# ``symmetric_difference`` is empty iff neither side has an ID the other
|
|
939
|
+
# lacks. Using pandas ``Index`` set ops keeps the comparison in int64 and
|
|
940
|
+
# avoids materializing Python ``set`` objects over large ID arrays.
|
|
941
|
+
return len(link_ids.symmetric_difference(source_ids)) == 0
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
def _linkage_source_error(linkage_pair, link_df, cell, sample_size: int = 10) -> str:
|
|
945
|
+
"""Build a concise diagnostic message for an unresolvable linkage source.
|
|
946
|
+
|
|
947
|
+
Reports, for each endpoint, the layer vertex count, the linkage row count,
|
|
948
|
+
the number of unique linkage IDs, and the missing/extra ID counts, plus a
|
|
949
|
+
small bounded sample of the offending IDs. This replaces the enormous
|
|
950
|
+
pandas ``KeyError`` that a bad ``.loc`` reindex would otherwise raise.
|
|
951
|
+
"""
|
|
952
|
+
lines = [
|
|
953
|
+
f"Could not infer a valid source layer for linkage "
|
|
954
|
+
f"({linkage_pair[0]!r}, {linkage_pair[1]!r}) with {len(link_df)} rows.",
|
|
955
|
+
"A source endpoint must map exactly one linkage row to each of its "
|
|
956
|
+
"vertices (unique, complete coverage). Neither endpoint qualifies:",
|
|
957
|
+
]
|
|
958
|
+
for source in linkage_pair:
|
|
959
|
+
source_ids = canonicalize_ids(
|
|
960
|
+
pd.Index(cell._all_objects[source].vertex_index), name=source
|
|
961
|
+
)
|
|
962
|
+
link_ids = canonicalize_ids(pd.Index(link_df[source]), name=source)
|
|
963
|
+
unique_link_ids = link_ids.unique()
|
|
964
|
+
missing = source_ids.difference(link_ids)
|
|
965
|
+
extra = link_ids.difference(source_ids)
|
|
966
|
+
lines.append(
|
|
967
|
+
f" - {source!r}: vertices={len(source_ids)}, "
|
|
968
|
+
f"link_rows={len(link_ids)}, unique_link_ids={len(unique_link_ids)}, "
|
|
969
|
+
f"missing_source_ids={len(missing)}, extra_link_ids={len(extra)}"
|
|
970
|
+
)
|
|
971
|
+
if len(missing):
|
|
972
|
+
lines.append(f" missing sample: {missing[:sample_size].tolist()}")
|
|
973
|
+
if len(extra):
|
|
974
|
+
lines.append(f" extra sample: {extra[:sample_size].tolist()}")
|
|
975
|
+
return "\n".join(lines)
|
|
976
|
+
|
|
977
|
+
|
|
916
978
|
def build_linkage(
|
|
917
979
|
linkage_pair,
|
|
918
980
|
tf,
|
|
@@ -922,23 +984,50 @@ def build_linkage(
|
|
|
922
984
|
link_df = load_dataframe(f"{prefix}/linkage.feather", tf)
|
|
923
985
|
# Legacy .osy files may store link columns with mixed int64/uint64 dtypes
|
|
924
986
|
# (dtype optimization only downcast signed ints, leaving uint64 untouched).
|
|
925
|
-
# Canonicalize both columns to int64 before
|
|
926
|
-
#
|
|
927
|
-
#
|
|
987
|
+
# Canonicalize both columns to int64 before any label-based reindex below so
|
|
988
|
+
# that lookup cannot collide two IDs above 2**53 through a float coercion of
|
|
989
|
+
# mismatched signed/unsigned keys.
|
|
928
990
|
for col in linkage_pair:
|
|
929
991
|
if col in link_df.columns:
|
|
930
992
|
link_df[col] = canonicalize_ids(link_df[col], name=col)
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
993
|
+
|
|
994
|
+
# The archive sorts the pair names, so serialized column order does not
|
|
995
|
+
# preserve the original source direction. Infer the source by exact
|
|
996
|
+
# source-domain validation rather than row count alone: a layer qualifies
|
|
997
|
+
# as source only when its linkage column has no missing IDs, is unique, has
|
|
998
|
+
# one row per vertex, and covers exactly that layer's vertex index. Row
|
|
999
|
+
# count alone is ambiguous whenever the two layers have equal cardinality
|
|
1000
|
+
# (e.g. a many-to-one graph <-> annotation link where the counts coincide).
|
|
1001
|
+
candidates = []
|
|
1002
|
+
for source, target in (
|
|
1003
|
+
(linkage_pair[0], linkage_pair[1]),
|
|
1004
|
+
(linkage_pair[1], linkage_pair[0]),
|
|
1005
|
+
):
|
|
1006
|
+
source_ids = canonicalize_ids(
|
|
1007
|
+
pd.Index(cell._all_objects[source].vertex_index), name=source
|
|
1008
|
+
)
|
|
1009
|
+
link_ids = canonicalize_ids(pd.Index(link_df[source]), name=source)
|
|
1010
|
+
if (
|
|
1011
|
+
len(link_ids) == len(source_ids)
|
|
1012
|
+
and link_ids.is_unique
|
|
1013
|
+
and _exact_integer_id_coverage(link_ids, source_ids)
|
|
1014
|
+
):
|
|
1015
|
+
candidates.append((source, target))
|
|
1016
|
+
|
|
1017
|
+
if not candidates:
|
|
1018
|
+
raise ValueError(_linkage_source_error(linkage_pair, link_df, cell))
|
|
1019
|
+
|
|
1020
|
+
# Zero candidates raises above. One candidate is the unambiguous source.
|
|
1021
|
+
# Two candidates means a true bijection: both columns are unique and cover
|
|
1022
|
+
# their layers exactly, so the mapping is one-to-one. MorphSync stores every
|
|
1023
|
+
# link in both directions (see ``add_link``), so either choice yields the
|
|
1024
|
+
# same bidirectional link; pick the first deterministically.
|
|
1025
|
+
source_layer, target_layer = candidates[0]
|
|
940
1026
|
|
|
941
1027
|
layer = cell._all_objects[source_layer]
|
|
1028
|
+
# Validation above guarantees the source column is unique and covers exactly
|
|
1029
|
+
# the source vertex index, so this reindex is a total, collision-free
|
|
1030
|
+
# reordering -- it can no longer raise a large KeyError.
|
|
942
1031
|
cell._all_objects[source_layer]._process_linkage(
|
|
943
1032
|
Link(
|
|
944
1033
|
link_df.set_index(source_layer).loc[layer.vertex_index][target_layer],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|