Skip to content

Component-overlap histories: the set is stable, not just the count - #29

Open
aurascoper wants to merge 4 commits into
docs/paraview-macos-arm64-2026-09-11from
docs/component-overlap-histories
Open

Component-overlap histories: the set is stable, not just the count#29
aurascoper wants to merge 4 commits into
docs/paraview-macos-arm64-2026-09-11from
docs/component-overlap-histories

Conversation

@aurascoper

Copy link
Copy Markdown
Owner

Stacked on #28.

"Consolidates to 10 and holds for 85 frames" was a claim about a count. Ten regions every frame is equally consistent with ten stable regions and with regions being born and dying every frame while the total happens to stay 10. A per-frame component count cannot tell those apart.

component_overlap.py intersects each component's voxel set with every component's voxel set in the next frame, inherits lineage by largest shared volume, and records births, deaths, merges and splits.

26-conn (point-sharing) 6-conn (face-sharing)
distinct lineages ever 21 24
alive at MCS 100 10 10
of those, born before MCS 15 10 10
births / deaths / splits after MCS 15 0 / 0 / 0 0 / 0 / 0
merges after MCS 15 2 (MCS 15) 3 (MCS 16, 17)

The set is stable, not merely the count, and consolidation is monotone by merge — no region ever dies and none ever splits after MCS 15. The final state is adjacency-independent; only the frame at which it is reached is not.

A correction to the existing README

Both adjacency rules are now reported side by side. The count stabilises permanently at MCS 15 under point-sharing and MCS 17 under face-sharing; the previous revision gave only the point-sharing column and wrote "15 → 100: 10, flat", which is false under face-sharing (13 at MCS 15, 12 at 16). That is the third convention-dependent number in this bundle, after the slice-plane coordinate and the threshold predicate.

Portable read-only audit

vti_read.py reads the .vti in numpy alone — the exporter writes uncompressed appended binary, so no VTK is needed. An audit of the renderer's output should not require the renderer's toolchain. Validated before being trusted for anything new: summing occupied_above_threshold reproduces metrics.json on 101/101 frames, zero mismatches.

🤖 Generated with Claude Code

https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw

…count

"Consolidates to 10 and holds for 85 frames" was a claim about a COUNT. Ten
regions every frame is equally consistent with ten stable regions and with
regions being born and dying every frame while the total happens to stay 10.
A per-frame component count cannot tell those apart.

component_overlap.py intersects each component's voxel set with every
component's voxel set in the next frame, inherits lineage by largest shared
volume, and records births, deaths, merges and splits. Result: 21 lineages
ever exist under point-sharing (24 under face-sharing), 10 are alive at MCS
100, all 10 were born before MCS 15, and there are zero births, zero deaths
and zero splits across every remaining frame under both conventions. The set
is stable, and consolidation is monotone by merge -- no region ever dies.

Both adjacency rules are now reported side by side, because they disagree.
The count stabilises permanently at MCS 15 under point-sharing and MCS 17
under face-sharing; an earlier revision gave only the point-sharing column
and wrote "15 -> 100: 10, flat", which is false under face-sharing. That is
the third convention-dependent number in this bundle, after the slice-plane
coordinate and the threshold predicate.

vti_read.py reads the .vti in numpy alone -- the exporter writes uncompressed
appended binary, so no VTK is needed, and an audit of the renderer's output
should not need the renderer's toolchain. Validated before use: summing
occupied_above_threshold reproduces metrics.json on 101/101 frames, zero
mismatches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-11T10:00:25.008242Z 1582a7e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1582a7ef24

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

counts.append(k)
cur_sizes = {i: int((lab == i).sum()) for i in range(1, k + 1)}

if prev_lab is None or k == 0:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count deaths when a frame becomes empty

When a nonempty frame is followed by a frame with k == 0, this branch initializes an empty current-lineage map but hard-codes deaths to zero, so a complete disappearance of every region is reported as no deaths. Handle only the first frame here, and let empty later frames compare against lineage_of_prev; add a nonempty-to-empty negative control so the advertised death accounting is exercised.

AGENTS.md reference: AGENTS.md:L17-L21

Useful? React with 👍 / 👎.

aurascoper and others added 3 commits September 11, 2026 05:44
…isible

`deaths` counted only predecessors with no overlapping successor. In a merge both
predecessors overlap, so neither was a death -- but `c in taken_cur` retires all
but the largest claim, and those lineages were counted nowhere. Measured against
the shipped evidence: 11 of 11 retirements (26-conn) and 14 of 14 (6-conn) were
reported as zero deaths, and README prose read that as "no region ever dies".

The conclusion survives and is now measured rather than inferred: nothing ever
disappears (0 across the whole record under both rules), and every lineage that
stops does so by merge. Component counts are unchanged -- counts_by_mcs is
byte-identical to the superseded receipt, which is retained as historical evidence.

Two further accounting defects, both latent on this dataset but real:
  - the seeding branch set frames_seen=1 before the common increment, so a lineage
    present for one frame reported two; it now seeds 0 like the birth path.
  - `prev_lab is None or k == 0` routed every EMPTY later frame through the seeding
    branch, which hard-codes deaths=0. Only the genuinely first frame takes it now.

Events are labelled by destination frame, so the old `mcs >= 15` filter included the
14->15 transition -- the one that PRODUCES the flat state rather than one inside it.
Reported now as `transition_into_frame_15` and `churn_within_frames_16_to_100`.

Added closure assertions that must hold every transition:
  carried + retired_by_merge + disappeared == previous count
  carried + births == current count

test_component_overlap.py adds 26 fixtures on synthetic frames -- single-frame
lifespan, all-vanish, two- and three-into-one merge, split, competing overlap, and a
transition mixing disappearance with merge. Against the pre-repair implementation they
report frames_seen == 1 -> 2, deaths == 1 -> 0, then a KeyError for a retirement
counter that did not exist.

The tracker measures persistence under largest-overlap greedy inheritance, not
material identity, and lineage integers are not comparable across adjacency rules.
Both are now stated in the receipt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw
The footer hard-coded 7+2+5+4+3+2+3 = 26 for 22 check() calls across 7
scenarios; the first block's count was mistyped as 7 when it has 2. A harness
that asserts its own size from memory is the same class of error the fixtures
exist to catch. It counts now, and README.md states 22.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1GLLCekStea7UHjRF51aw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant