Skip to content

Enable out-of-sample transform with precomputed_affinity - #183

Open
MattScicluna wants to merge 1 commit into
KrishnaswamyLab:mainfrom
MattScicluna:enable_precomputed_affinity_transform
Open

Enable out-of-sample transform with precomputed_affinity#183
MattScicluna wants to merge 1 commit into
KrishnaswamyLab:mainfrom
MattScicluna:enable_precomputed_affinity_transform

Conversation

@MattScicluna

Copy link
Copy Markdown
Contributor

PHATE.transform() previously raised a ValueError for any precomputed graph, including knn_dist="precomputed_affinity". Unlike distances, a precomputed query-train affinity matrix is already a usable transition kernel, so no new kernel needs to be built.

transform() now recognizes this case and computes the out-of-sample embedding directly: row-normalize the query-train affinity (or, when the graph was fit with landmarks, first aggregate by landmark cluster and then row-normalize) and apply it to the fitted embedding.

Fixes #181

PHATE.transform() previously raised a ValueError for any precomputed
graph, including knn_dist="precomputed_affinity". Unlike distances,
a precomputed query-train affinity matrix is already a usable
transition kernel, so no new kernel needs to be built.

transform() now recognizes this case and computes the out-of-sample
embedding directly: row-normalize the query-train affinity (or, when
the graph was fit with landmarks, first aggregate by landmark cluster
and then row-normalize) and apply it to the fitted embedding.

Fixes KrishnaswamyLab#181
Copilot AI lite review requested due to automatic review settings August 20, 2026 16:27

Copilot AI 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.

Pull request overview

This PR updates PHATE.transform() to support out-of-sample projection when the operator was fit with a precomputed affinity graph (including landmark mode), aligning behavior with Issue #181’s requested P_query-train · Z_train / P_query-landmark · Z_landmark extension.

Changes:

  • Adds out-of-sample transform support for knn_dist="precomputed_affinity" by row-normalizing query→train affinities (and aggregating to landmarks when applicable).
  • Refactors embedding creation into _ensure_embedded() and introduces _transform_precomputed_affinity() helper.
  • Adds regression tests for out-of-sample transform with precomputed affinities (with and without landmarks).

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
Python/phate/phate.py Implements out-of-sample transform path for precomputed affinities and refactors embedding initialization.
Python/test/test_phate.py Adds tests validating out-of-sample projection for precomputed affinities (landmark + non-landmark) and preserves error for precomputed distances.
.gitignore Ignores .venv/ virtual environments.
Suppressed comments (3)

Python/phate/phate.py:986

  • The precomputed-affinity out-of-sample path is gated on TraditionalGraph, but the new helper _transform_precomputed_affinity() explicitly supports landmark graphs. If LandmarkGraph is not a TraditionalGraph (see PHATE._parse_input() which treats them separately), this branch will never run for n_landmark fits, and transform() will fall back to extend_to_data(X) which expects a feature matrix rather than a query→train affinity matrix.
            if (
                self.knn_dist == "precomputed_affinity"
                and isinstance(self.graph, graphtools.graphs.TraditionalGraph)
                and self.graph.precomputed == "affinity"
            ):

Python/phate/phate.py:1036

  • _transform_precomputed_affinity() calls _ensure_embedded() with default arguments, so transform(..., t_max=..., plot_optimal_t=..., ax=...) parameters are ignored in the precomputed-affinity out-of-sample path. This can change results when t='auto' and callers override t_max/plotting behavior.
    def _transform_precomputed_affinity(self, X):
        """Out-of-sample extension for a precomputed affinity graph

        Given a query-to-train affinity matrix `X`, computes the PHATE
        embedding of the query points as a transition-weighted combination

Python/phate/phate.py:991

  • The ValueError message in the precomputed-graph branch says “precomputed distance matrix”, but this block can also be reached for other graph.precomputed modes (e.g., affinity when knn_dist='precomputed'). The error should report the actual graph.precomputed value so it’s accurate.
                isinstance(self.graph, graphtools.graphs.TraditionalGraph)
                and self.graph.precomputed is not None
            ):

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Python/phate/phate.py
Comment on lines 977 to 981
"Pre-fit PHATE should not be used to transform a "
"new data matrix. Please fit PHATE to the new"
" data by running 'fit' with the new data.",
RuntimeWarning,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable out-of-sample projection with precomputed affinities

2 participants