Cleanup extract_knn_graph - #8272
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughRelocates KNN graph extraction utilities from ChangesKNN Graph Extraction Relocation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@python/cuml/cuml/manifold/utils.py`:
- Around line 178-188: The current validation in the error check only rejects
cases where n_neighbors is strictly greater than n_samples, but it should also
reject the edge case where n_neighbors equals n_samples. When n_neighbors ==
n_samples, the argpartition operation will select all columns including the
self-index (diagonal element) even after filling it with infinity, resulting in
self-neighbors in the KNN graph. Modify the condition in the ValueError check to
use n_samples <= n_neighbors instead of n_samples < n_neighbors to properly
reject this edge case and prevent invalid self-neighbors in the output KNN
graph.
- Around line 115-128: The current validation only checks if total non-zero
elements divide evenly by n_samples, but doesn't verify that each row has the
same number of neighbors. This allows uneven rows to pass validation and get
incorrectly reshaped, mixing neighbor entries across samples. After converting
to CSR format with tocsr() and before reshaping knn_info.indices, add validation
to confirm every row has exactly orig_n_neighbors elements by checking the
differences between consecutive indptr values in the CSR matrix are all equal to
orig_n_neighbors, and raise a ValueError if any row has a different count.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 38dbfa58-f384-455c-915f-caf77b3feaa7
📒 Files selected for processing (5)
python/cuml/cuml/common/sparsefuncs.pypython/cuml/cuml/manifold/t_sne.pyxpython/cuml/cuml/manifold/umap/umap.pyxpython/cuml/cuml/manifold/utils.pypython/cuml/tests/test_umap.py
|
Hmmm, it's not clear to me whether the precomputed KNN data should include self references (i.e. should data on a sample indicate the closest sample is itself)? Upstream UMAP wants that:
What's the expected behavior for our implementation? Do we require self references? What happens if they're not there (if we need them) / if they're there (if we don't need them)? Getting self references from a KNN graph input is a bit tricky, since the expected values are 0 and would thus be dropped by a canonical sparse array (they're also not included by default in FWIW - all tests in |
|
Yes current implementation requires self-references in the knn graph. However, it's not an algorithmic requirement, so we can update a few pieces in the current UMAP code to remove this requirement if needed(e.g. the |
|
Thanks Jinsol! I think we can leave the C++ layer alone - requiring self references is fine (it's what upstream umap does). In this PR I'll:
|
8d99652 to
95c5b6c
Compare
dantegd
left a comment
There was a problem hiding this comment.
Just have two questions, besides that it looks good to me
- Moves `extract_knn_graph` to `cuml.manifold.utils`. This function is only used by `cuml.manifold` and is too specific to belong in a general sparse utils module. - Modernizes the code. Applies new input validation, and removes use of legacy `CumlArray`/`SparseCumlArray`. - Fixes bug in handling any KNN graph input, where the order of `.data` was assumed to be correct (only true if a direct output of `kneighbors_graph`), leading to incorrect results. - Fixes bug in handling of KNN graph and tuple input, where self references were assumed to be present, but not validated or documented as required. For the tuple input we now do a cheap sanity check that self references are included, erroring if they're not present. For KNN graph inputs we properly handle all cases (never included, included but sometimes non-zero-distances, and included), and massage the input to satisfy the requirement. We need to do this for KNN inputs since a canonical KNN graph won't include true self references (which should be 0 distances sans rounding errors), but `kneighbors_graph` will include them if `include_self=True` is provided. - Fixes bug in zero-copy handling of KNN graphs. Previously we always copied to device, and also always made additional unnecessary copies. - Improves validation and error messages - Updates docstrings for `precomputed_knn` and `knn_graph` to better indicate the supported options and requirements. - Adds thorough tests for all input types and configurations
95c5b6c to
8cc749d
Compare
|
/merge |
This was motivated as a followup to some recent cleanup work (#8272 and #8269), aiming to consolidate some modules and delete some dead code, but in the process fixed a few bugs and added one feature. **Highlights a user would care about** - Fixes `PCA` on sparse inputs submitted in COO format. - Adds support for large sparse matrices to `PCA`. Fixes #8159. - Fixes a long standing bug in sparse `PCA` where the incorrect `ddof` was used when computing the covariance. **Details a maintainer would care about** - Consolidates `cuml.common.sparse_utils` and `cuml.common.sparsefuncs` into `cuml.common.sparse` - Moves `create_csr_matrix_from_count_df` to `cuml.feature_extraction._vectorizers`, the only module it's used in. - Moves `cuml.prims.stats.cov` to `cuml.common.sparse` (keeping only the sparse version, as that's the only version we use), and removes the remaining empty `cuml.prims` module. - Some mild code cleanups - Improved test coverage for remaining functions in `cuml.common.stats` - Removes duplicate versions of `csr_row_normalize_l1` and `csr_row_normalize_l2` from `cuml.thirdparty_adapters`. I ran some tests and benchmarks just to be sure - the versions in `cuml.common.sparse` using `cupy` are consistently faster (~15%) and yield equivalent results. Less code to manage, and less `numba.cuda` usage. We're _almost_ to a point where this won't be a required dep either. - Removes buggy/incorrect specialization of `cov` for COO matrices. This wasn't caught with the previous test, but was caught with a more thorough one on denser data. I spent some time trying to fix the kernel, but decided that relying on the single CSR kernel implementation was simpler, more correct, and as performant. - I attempted to remove the fallback grammian computation kernel in `cov` to rely on `X.T.dot(X)` instead in `cupy >= 14`. Removing the fallback in that case resulted in a nice speedup on small inputs, but when tested on larger data I ran into issues - unfortunately there still appears to be a bug there. I opened cupy/cupy#10033, and updated the inline comment to note the new issue. - Fixed a long standing bug in our sparse PCA implementation where the incorrect `ddof` was used when computing `cov`. I've improved the test suite to catch the issue going forward. This also fixed 300+ xfails in the sklearn compat test suite. - Added support for large (int64 indices) sparse matrices to `PCA`. This was relatively straightforward to do. Like other cases, this is hard to test in CI (cupy rightfully normalizes indices as int32 when possible), but I can confirm things work properly from local testing. There are still some dispersed sparse utilities in `cuml.thirdparty_adapters`/`cuml._thirdparty.sklearn.utils.sparsefuncs`. In the interest of time I opted to skip these for now (sans relying on any implementations in `cuml.common.sparse` instead, as noted above). Further migration work may happen in followup(s). Authors: - Jim Crist-Harif (https://github.com/jcrist) Approvers: - Dante Gama Dessavre (https://github.com/dantegd) URL: #8288
extract_knn_graphtocuml.manifold.utils. This function is only used bycuml.manifoldand is too specific to belong in a general sparse utils module.CumlArray/SparseCumlArray. Part of Transition to new array input validation system #7428..datawas assumed to be correct (only true if a direct output ofkneighbors_graph), leading to incorrect results.kneighbors_graphwill include them ifinclude_self=Trueis provided.precomputed_knnandknn_graphto better indicate the supported options and requirements.