The SVDReducer class performs dimensionality reduction using Singular Value Decomposition (SVD). Given a data matrix X ∈ R^(m×n) with rows as samples and columns as features, the thin SVD is:
X = U Σ V^T
where Σ = diag(σ₁,…,σ_r), r = min(m,n), and σ₁ ≥ … ≥ σ_r ≥ 0.
Dimensionality reduction to k < r dimensions provides the best rank-k approximation (Eckart–Young–Mirsky theorem).
Constructor
The default constructor requires no parameters.
Methods
fit
Fit the SVD model to the data matrix.
void fit(const Eigen::MatrixXd& X)
X
const Eigen::MatrixXd&
required
Data matrix where rows are samples and columns are features
Example
Eigen::MatrixXd data(100, 10); // 100 samples, 10 features
// ... populate data ...
SVDReducer svd;
svd.fit(data);
Reduce the fitted data to k dimensions.
Eigen::MatrixXd transform(std::size_t k) const
Number of dimensions to reduce to
Reduced representation Z = U_k Σ_k with shape (m×k), where m is the number of samples
Example
// Reduce to 3 dimensions
Eigen::MatrixXd reduced = svd.transform(3);
std::cout << "Reduced shape: " << reduced.rows()
<< "x" << reduced.cols() << std::endl;
Transform a new batch of data using the fitted SVD model.
Eigen::MatrixXd transform_new(const Eigen::MatrixXd& X_new, std::size_t k) const
X_new
const Eigen::MatrixXd&
required
New data matrix to transform
Number of dimensions to reduce to
Transformed new data: X_new V_k
Example
Eigen::MatrixXd new_data(50, 10); // 50 new samples, 10 features
// ... populate new_data ...
// Transform new data to 3 dimensions
Eigen::MatrixXd transformed_new = svd.transform_new(new_data, 3);
reconstruct
Reconstruct data from reduced coordinates (approximate inverse transformation).
Eigen::MatrixXd reconstruct(const Eigen::MatrixXd& Z, std::size_t k) const
Z
const Eigen::MatrixXd&
required
Reduced coordinates from transform or transform_new
Number of dimensions used in the reduction
Reconstructed data in the original dimensional space
Example
Eigen::MatrixXd reduced = svd.transform(3);
Eigen::MatrixXd reconstructed = svd.reconstruct(reduced, 3);
// reconstructed approximates the original data
Get the left singular vectors matrix.
const Eigen::MatrixXd& U() const
Matrix U of left singular vectors with shape (m×r)
Example
const Eigen::MatrixXd& U = svd.U();
std::cout << "U shape: " << U.rows() << "x" << U.cols() << std::endl;
Get the singular values vector.
const Eigen::VectorXd& S() const
Vector of singular values (σ₁,…,σ_r) in descending order
Example
const Eigen::VectorXd& S = svd.S();
std::cout << "Singular values: " << S.transpose() << std::endl;
// Analyze explained variance
double total_variance = S.array().square().sum();
for (int i = 0; i < S.size(); ++i) {
double variance_ratio = (S(i) * S(i)) / total_variance;
std::cout << "Component " << i << ": "
<< (variance_ratio * 100) << "%" << std::endl;
}
Get the right singular vectors matrix.
const Eigen::MatrixXd& V() const
Matrix V of right singular vectors with shape (n×r)
Example
const Eigen::MatrixXd& V = svd.V();
std::cout << "V shape: " << V.rows() << "x" << V.cols() << std::endl;
Complete example
#include "SVD.h"
#include <Eigen/Dense>
#include <iostream>
int main() {
// Create training data
Eigen::MatrixXd data(100, 10); // 100 samples, 10 features
data.setRandom();
// Fit SVD
SVDReducer svd;
svd.fit(data);
// Transform training data to 3 dimensions
Eigen::MatrixXd reduced = svd.transform(3);
std::cout << "Reduced shape: " << reduced.rows()
<< "x" << reduced.cols() << std::endl;
// Transform new data
Eigen::MatrixXd new_data(50, 10); // 50 new samples
new_data.setRandom();
Eigen::MatrixXd reduced_new = svd.transform_new(new_data, 3);
// Reconstruct data
Eigen::MatrixXd reconstructed = svd.reconstruct(reduced, 3);
// Access singular values and vectors
const Eigen::VectorXd& singular_values = svd.S();
std::cout << "Singular values: " << singular_values.transpose() << std::endl;
// Compute reconstruction error
double error = (data - reconstructed).norm() / data.norm();
std::cout << "Relative reconstruction error: " << error << std::endl;
return 0;
}