Skip to content

loop densification and coherence - #110

Merged
stanfish06 merged 8 commits into
masterfrom
coherence
Sep 29, 2026
Merged

stanfish06 merged 8 commits into
masterfrom
coherence

Conversation

@stanfish06

Copy link
Copy Markdown
Owner

Closes #96

  • initial helpers for loop refinemnet
  • wire in loop refinement
  • refined loop plotting
  • old progress
  • area based coherence
  • representative validity checks
  • remove filling cross match
  • small bug densify loop

@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: 34c1ec2006

ℹ️ 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".

Comment on lines +243 to +248
result.homotopy_coherence_matched_relax = [
compute_coherence(
filling=source_fillings[source_index],
deformation=deformation["triangle_ids"],
triangle_areas=triangle_areas,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Account for relaxation edges in coherence scores

When a match is produced by the relaxed solver, its triangle chain has boundary equal to the source/target difference only after including deformation["relaxation_edge_ids"]. Computing coherence from triangle_ids alone therefore treats a chain that does not connect the two loops as a valid homotopy, producing incorrect values for relaxed matches and consequently incorrect Wilcoxon presence-test inputs when that method is selected. Incorporate the relaxation edges into the coherence calculation or exclude relaxed matches from this metric.

Useful? React with 👍 / 👎.

Comment on lines +719 to +722
if local_scale is None:
nn = NearestNeighbors(n_neighbors=k_local_scale + 1).fit(embedding)
knn_distances, _ = nn.kneighbors(embedding)
local_scale = np.asarray(knn_distances[:, 1:].mean(axis=1), dtype=np.float64)

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 Clamp local-scale neighbors to the sample count

With the default k_local_scale=5, any embedding containing fewer than six observations causes NearestNeighbors.kneighbors to raise because n_neighbors exceeds n_samples. find_loops() invokes this refinement unconditionally, even when no loop representatives were found, so otherwise valid small datasets fail at the end of the pipeline. Clamp the requested neighbor count to the available observations, and handle the single-observation case.

Useful? React with 👍 / 👎.

Comment on lines 1145 to +1147
representatives=representatives,
representatives_refined=representatives_refined,
representatives_valid=super_obj.representatives_valid,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Exclude invalid representatives from Hodge analysis

When representatives_valid contains false entries, from_super() still copies every representative and its derived coordinate, gradient, and edge arrays, while only attaching the validity mask afterward. The Hodge pipeline consequently builds masks for these invalid cycles and includes their edges in smoothing, divergence, trajectory identification, and gene-trend computations; filtering them only while plotting cannot prevent them from changing the analyzed results. Filter all per-representative inputs consistently before constructing LoopClassAnalysis.

Useful? React with 👍 / 👎.

Comment on lines +171 to +173
) -> list[tuple[Index_t, ...] | None]:
if self._cached_fillings is not None:
return self._cached_fillings

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 Key cached fillings by their computation parameters

After fillings() is called once, every later call returns the same cached chain even if it supplies a different boundary matrix, column_trim_method, or column_scores. Reusing a LoopClass for another bootstrap/equivalence analysis with different trimming settings therefore computes coherence from a filling selected under the previous settings. Either key the cache by all inputs that affect the solve or invalidate/recompute it when those inputs change.

Useful? React with 👍 / 👎.

Comment on lines +628 to +630
for u, v in zip(vertices[:-1], vertices[1:]):
poly = [u, v]
lengths = [float(np.linalg.norm(embedding[u] - embedding[v]))]

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 Densify the closing edge of each loop

Reconstructed representatives normally omit a repeated first vertex and are closed implicitly by loops_to_coords() and loops_to_edge_mask(), but this iteration processes only adjacent entries and never visits the edge from vertices[-1] back to vertices[0]. Consequently use_refined=True can still render one arbitrarily long unsplit segment—the cocycle edge that closes the loop—while every other segment is densified. Include the wraparound pair in refinement while preserving the representation's expected closure format.

Useful? React with 👍 / 👎.

@stanfish06
stanfish06 merged commit 48382a0 into master Sep 29, 2026
6 checks passed
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.

TODO: implement image-based registration as a fast fail test

1 participant