Skip to content

Native row-major PCA#3036

Open
aamijar wants to merge 9 commits into
NVIDIA:mainfrom
aamijar:pca-row-major
Open

Native row-major PCA#3036
aamijar wants to merge 9 commits into
NVIDIA:mainfrom
aamijar:pca-row-major

Conversation

@aamijar

@aamijar aamijar commented May 26, 2026

Copy link
Copy Markdown
Contributor

Currently PCA only supports col-major which is not ideal for consumers passing in row-major datasets.
This PR exposes row-major APIs for PCA and calls the corresponding row-major raft primitives based on templated row or col layout.

This PR is backward compatible with cuml and cuvs.

@aamijar aamijar requested a review from a team as a code owner May 26, 2026 00:52
@aamijar aamijar self-assigned this May 26, 2026
@aamijar aamijar added non-breaking Non-breaking change improvement Improvement / enhancement to an existing function labels May 26, 2026
@aamijar aamijar moved this to In Progress in Unstructured Data Processing May 26, 2026
Comment thread cpp/include/raft/linalg/detail/pca.cuh Outdated
Comment thread cpp/include/raft/linalg/detail/pca.cuh Outdated
Comment on lines +247 to +248
auto components_copy_view = raft::make_device_matrix_view<math_t, idx_t, LayoutPolicy>(
components_copy.data(), n_components, n_cols);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why explicitly here instead of inline in the diff before this PR?

Comment thread cpp/include/raft/linalg/detail/pca.cuh Outdated
Comment on lines +324 to +325
auto components_copy_view = raft::make_device_matrix_view<math_t, idx_t, LayoutPolicy>(
components_copy.data(), n_components, n_cols);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as above

Comment thread cpp/tests/linalg/pca.cu Outdated
Comment on lines +351 to +363
void to_row_major(const T* col_major_src, T* row_major_dst, int n_rows, int n_cols)
{
std::vector<T> host_col(n_rows * n_cols);
std::vector<T> host_row(n_rows * n_cols);
raft::update_host(host_col.data(), col_major_src, n_rows * n_cols, stream);
RAFT_CUDA_TRY(cudaStreamSynchronize(stream));
for (int i = 0; i < n_rows; ++i) {
for (int j = 0; j < n_cols; ++j) {
host_row[i * n_cols + j] = host_col[j * n_rows + i];
}
}
raft::update_device(row_major_dst, host_row.data(), n_rows * n_cols, stream);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this needed? You're generating 1-dimensional random data. It can be interpreted as row or col major.

Comment thread cpp/include/raft/linalg/detail/pca.cuh Outdated
raft::stats::cov<input_row_major>(
handle, cov.data(), input.data_handle(), mu.data_handle(), n_cols, n_rows, true, true, stream);

// The eigendecomposition of the (symmetric) covariance matrix naturally produces a

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But since covar matrix is symmetric, it shouldn't matter if it's column or row layout, right? Why do we need to transpose at all?

Co-authored-by: Divye Gala <divyegala@gmail.com>
@aamijar aamijar requested a review from a team as a code owner June 27, 2026 17:17
@aamijar

aamijar commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Hi, all the comments should be addressed now in the recent batch of commits.

components_all.data(), n_cols, n_cols),
raft::make_device_matrix_view<math_t, idx_t, raft::col_major>(
components.data_handle(), n_components, n_cols));
if constexpr (is_row_major) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you not need to call trunc_zero_origin in this branch?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is doing the same thing as trunc_zero_origin except using a simple raft::copy.

The goal is to copy a slice of some rows from the src n_cols x n_cols matrix.
If src is row-major we can just get the prefix n_components x n_cols from memory.
If src is col-major then we need to copy some elements, skip some, and the copy some more etc. That is what trunc_zero_origin is doing.

@divyegala

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • PCA now supports both row-major and column-major matrix layouts across fit, transform, and inverse transform workflows.
    • Layout handling is now consistent through the full PCA pipeline, including reconstruction.
  • Tests

    • Expanded PCA test coverage to run advanced PCA scenarios with both storage layouts.
    • Added verification that inverse transforms accurately reconstruct the original input.

Walkthrough

PCA and tSVD internal and public functions across raft's linalg module are generalized with a LayoutPolicy template parameter, enabling row-major and column-major device matrix view support throughout cal_eig, sign_flip_components, tsvd_transform, tsvd_inverse_transform, pca_fit, pca_transform, pca_inverse_transform, and pca_fit_transform. Tests are updated to validate both layouts.

Changes

PCA/tSVD Layout Generalization

Layer / File(s) Summary
tSVD core layout-aware helpers
cpp/include/raft/linalg/detail/tsvd.cuh
cal_eig, sign_flip_components, tsvd_transform, and tsvd_inverse_transform are templated on LayoutPolicy with static asserts, layout-conditional view construction/indexing, and a simplified GEMM call; new map.cuh include added.
PCA detail helpers built on tSVD layout support
cpp/include/raft/linalg/detail/pca.cuh
trunc_comp_exp_vars, pca_fit, pca_transform, pca_inverse_transform, and pca_fit_transform are templated on LayoutPolicy, deriving input_row_major to drive mean/cov/meanCenter/meanAdd instantiations and forwarding layout to tSVD calls.
Public PCA API and doc updates
cpp/include/raft/linalg/pca.cuh
Public wrappers pca_fit, pca_fit_transform, pca_inverse_transform, pca_transform add a LayoutPolicy template parameter with updated matrix view types and Doxygen comments.
PCA test coverage for row-major and col-major layouts
cpp/tests/linalg/pca.cu
advancedTest becomes templated on LayoutPolicy, allocates data2/data2_back locally, builds layout-parameterized views, validates inverse reconstruction, and is run for both raft::row_major and raft::col_major.

Estimated code review effort: 4 (Complex) | ~60 minutes

Related issues: None found in provided context.

Related PRs: None found in provided context.

Suggested labels: enhancement, cpp

Suggested reviewers: None found in provided context.

Poem
A rabbit hops through rows and columns bright,
Templates unfurl, layouts take their flight,
PCA and tSVD now bend either way,
Row-major, col-major, both here to stay,
Tests confirm the trick works out just right. 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding native row-major PCA support.
Description check ✅ Passed The description accurately states that PCA now supports row-major APIs and dispatches to matching RAFT primitives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cpp/tests/linalg/pca.cu (1)

135-198: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

HIGH: advancedTest round-trip does not validate PCA correctness for row-major.

With n_components == params.n_col2 (full rank) and prms.whiten = false, transform followed by inverse reduces to Z·V = X_c·VᵀV = X_c for any orthonormal V. The data2 ≈ data2_back assertion therefore passes regardless of whether the row-major path produces the correct eigenvectors, the correct component ordering, or the correct sign_flip_components result. The only reference-based checks (explained_vars/components/trans_data) run through basicTest, which is col-major only.

Consider adding row-major coverage that (a) truncates (n_components < n_cols) and (b) validates against a reference (e.g. compare eigenvalues/components against the col-major result on the same data). The whitening path (whiten = true, exercising binary_div_skip_zero/binary_mult_skip_zero with LayoutPolicy views) is also untested for both layouts.

🤖 Prompt for 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.

In `@cpp/tests/linalg/pca.cu` around lines 135 - 198, `advancedTest` only checks
an inverse-transform round trip, which can succeed even if the row-major PCA
path is wrong. Update `advancedTest` to use a non-full-rank case with
`n_components < n_cols` and add a reference comparison for the row-major
`pca_fit_transform`/`pca_inverse_transform` results against the col-major path
on the same input, including `components`, `explained_vars`, and `trans_view`
ordering/sign handling. Also add coverage for the `prms.whiten = true` path so
the `LayoutPolicy`-dependent branches in
`binary_div_skip_zero`/`binary_mult_skip_zero` are exercised.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@cpp/tests/linalg/pca.cu`:
- Around line 135-198: `advancedTest` only checks an inverse-transform round
trip, which can succeed even if the row-major PCA path is wrong. Update
`advancedTest` to use a non-full-rank case with `n_components < n_cols` and add
a reference comparison for the row-major
`pca_fit_transform`/`pca_inverse_transform` results against the col-major path
on the same input, including `components`, `explained_vars`, and `trans_view`
ordering/sign handling. Also add coverage for the `prms.whiten = true` path so
the `LayoutPolicy`-dependent branches in
`binary_div_skip_zero`/`binary_mult_skip_zero` are exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d53b3a91-b4d4-40ca-a12f-a8e17df5734e

📥 Commits

Reviewing files that changed from the base of the PR and between 1240331 and 4965cf6.

📒 Files selected for processing (4)
  • cpp/include/raft/linalg/detail/pca.cuh
  • cpp/include/raft/linalg/detail/tsvd.cuh
  • cpp/include/raft/linalg/pca.cuh
  • cpp/tests/linalg/pca.cu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improvement / enhancement to an existing function non-breaking Non-breaking change

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants