Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions Libs/Optimize/Function/EarlyStop/MorphologicalDeviationScore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,32 @@ Eigen::VectorXd MorphologicalDeviationScore::GetMorphoDevScore(const Eigen::Matr
}
}

//---------------------------------------------------------------------------
Eigen::MatrixXd MorphologicalDeviationScore::GetPCACoefficients(const Eigen::MatrixXd& X) {
try {
if (!is_fitted_) {
throw std::runtime_error("PPCA model is not fitted on control shapes.");
}

if (all_components_.cols() == 0) {
throw std::runtime_error("PPCA basis is empty.");
}

if (X.cols() != mean_.cols()) {
throw std::runtime_error("Input feature dimension does not match fitted PPCA model.");
}

if (X.rows() == 0) {
return Eigen::MatrixXd(0, all_components_.cols());
}

Eigen::MatrixXd X_bar = X.rowwise() - mean_; // (n x d)
return X_bar * all_components_; // (n x rank) scores in PCA basis

} catch (std::exception& e) {
SW_ERROR("Exception in computing PCA coefficients for early stopping {}", e.what());
return Eigen::MatrixXd();
}
}

} // namespace shapeworks
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <Eigen/Dense>
#include <stdexcept>

namespace shapeworks {
class MorphologicalDeviationScore {
Expand All @@ -11,6 +12,14 @@ class MorphologicalDeviationScore {
/// Get Mahalanobis-based deviation score for test samples (non-fixed
/// shapes/domains)
Eigen::VectorXd GetMorphoDevScore(const Eigen::MatrixXd& X); // (n,)
Eigen::MatrixXd GetPCACoefficients(const Eigen::MatrixXd& X); // (n, rank)
void SetRetainedVarianceRatio(double ratio) {
if (ratio <= 0.0 || ratio > 1.0) {
throw std::invalid_argument("retained_variance_ratio must be in the interval (0, 1].");
}
retained_variance_ratio_ = ratio;
}
double GetRetainedVarianceRatio() const { return retained_variance_ratio_; }

private:
/// Flag to ensure control shapes are set and PCA model is in place
Expand Down
38 changes: 38 additions & 0 deletions Libs/Python/ShapeworksPython.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,28 @@ PYBIND11_MODULE(shapeworks_py, m) {
True if fitting was successful, False otherwise.
)pbdoc")

.def("SetRetainedVarianceRatio",
&MorphologicalDeviationScore::SetRetainedVarianceRatio, py::arg("ratio"),
R"pbdoc(
Set the retained variance ratio used to choose PPCA components.

Parameters
----------
ratio : float
Target cumulative variance ratio in the interval (0, 1].
)pbdoc")

.def("GetRetainedVarianceRatio",
&MorphologicalDeviationScore::GetRetainedVarianceRatio,
R"pbdoc(
Get the retained variance ratio used to choose PPCA components.

Returns
-------
float
The current retained variance ratio.
)pbdoc")

.def("GetMorphoDevScore",
&MorphologicalDeviationScore::GetMorphoDevScore, py::arg("X"),
R"pbdoc(
Expand All @@ -1758,5 +1780,21 @@ PYBIND11_MODULE(shapeworks_py, m) {
-------
numpy.ndarray
Vector of Mahalanobis distances for each sample.
)pbdoc")

.def("GetPCACoefficients",
&MorphologicalDeviationScore::GetPCACoefficients, py::arg("X"),
R"pbdoc(
Project samples onto the fitted PCA basis.

Parameters
----------
X : numpy.ndarray
Matrix of samples (n_samples x n_features)

Returns
-------
numpy.ndarray
Matrix of PCA coefficients with shape (n_samples, rank).
)pbdoc");
} // PYBIND11_MODULE(shapeworks_py)
Loading