56template <
class LeftTensor,
class SingularValueTensor,
class RightAdjo
intTensor,
class TruncationInfo>
72template <uni20::Real Real>
class CompensatedNonnegativeSum {
78 correction_ += (sum_ - next) +
value;
80 correction_ += (
value - next) + sum_;
84 [[nodiscard]]
Real value()
const {
return sum_ + correction_; }
91template <uni20::Real Real>
void validate_svd_truncation_policy(SvdTruncationPolicy<Real>
const& policy)
93 ERROR_IF(policy.minimum_retained_extent > policy.maximum_retained_extent,
94 "SVD minimum retained extent exceeds maximum retained extent", policy.minimum_retained_extent,
95 policy.maximum_retained_extent);
97 auto validate_nonnegative = [](std::optional<Real>
const&
value,
char const* name) {
100 validate_nonnegative(policy.singular_value_cutoff,
"SVD singular-value cutoff");
101 validate_nonnegative(policy.normalized_squared_singular_value_cutoff,
"SVD normalized squared singular-value cutoff");
102 validate_nonnegative(policy.maximum_discarded_weight,
"SVD maximum discarded weight");
105 policy.normalized_squared_singular_value_cutoff && *policy.normalized_squared_singular_value_cutoff > Real{1},
106 "SVD normalized squared singular-value cutoff exceeds one", *policy.normalized_squared_singular_value_cutoff);
107 ERROR_IF(policy.maximum_discarded_weight && *policy.maximum_discarded_weight > Real{1},
108 "SVD maximum discarded weight exceeds one", *policy.maximum_discarded_weight);
111template <uni20::Real Real,
class ValueAt>
112[[nodiscard]]
auto select_svd_truncation_values(std::size_t available_rank, ValueAt&& value_at,
113 SvdTruncationPolicy<Real>
const& policy)
115 using real_type =
Real;
116 validate_svd_truncation_policy(policy);
118 std::vector<real_type> scaled_squares(available_rank);
119 std::vector<real_type> discarded_scaled_squares(available_rank + 1, real_type{});
122 if (available_rank > 0)
124 scale = value_at(std::size_t{0});
126 CHECK(scale >= real_type{}, scale);
128 for (std::size_t index = 1; index < available_rank; ++index)
130 real_type
const previous = value_at(index - 1);
131 real_type
const value = value_at(index);
138 if (scale != real_type{})
140 CompensatedNonnegativeSum<real_type>
sum;
141 for (std::size_t index = available_rank; index > 0; --index)
143 std::size_t
const value_index = index - 1;
144 real_type
const ratio = value_at(value_index) / scale;
146 scaled_squares[value_index] =
square;
148 discarded_scaled_squares[value_index] =
sum.value();
152 real_type
const total_scaled_squared_norm = discarded_scaled_squares[0];
153 bool const has_accuracy_criterion = policy.singular_value_cutoff || policy.normalized_squared_singular_value_cutoff ||
154 policy.maximum_discarded_weight;
155 std::size_t retained_rank = has_accuracy_criterion ? 0 : available_rank;
157 if (policy.singular_value_cutoff)
159 std::size_t cutoff_rank = 0;
160 while (cutoff_rank < available_rank && value_at(cutoff_rank) >= *policy.singular_value_cutoff)
162 retained_rank = std::max(retained_rank, cutoff_rank);
165 if (policy.normalized_squared_singular_value_cutoff)
167 std::size_t cutoff_rank = 0;
168 if (total_scaled_squared_norm == real_type{})
170 if (*policy.normalized_squared_singular_value_cutoff == real_type{}) cutoff_rank = available_rank;
174 real_type
const threshold = *policy.normalized_squared_singular_value_cutoff * total_scaled_squared_norm;
175 while (cutoff_rank < available_rank && scaled_squares[cutoff_rank] >= threshold)
178 retained_rank = std::max(retained_rank, cutoff_rank);
181 if (policy.maximum_discarded_weight)
183 std::size_t error_rank = 0;
184 if (total_scaled_squared_norm != real_type{})
186 real_type
const maximum_discarded = *policy.maximum_discarded_weight * total_scaled_squared_norm;
187 while (error_rank < available_rank && discarded_scaled_squares[error_rank] > maximum_discarded)
190 retained_rank = std::max(retained_rank, error_rank);
193 retained_rank = std::max(retained_rank, std::min(policy.minimum_retained_extent, available_rank));
194 retained_rank = std::min(retained_rank, std::min(policy.maximum_retained_extent, available_rank));
196 real_type original_squared_norm{};
197 real_type discarded_weight{};
198 if (scale != real_type{})
200 original_squared_norm = scale * scale * total_scaled_squared_norm;
201 discarded_weight = discarded_scaled_squares[retained_rank] / total_scaled_squared_norm;
204 return SvdTruncationInfo<real_type>{
205 .available_rank = available_rank,
206 .retained_rank = retained_rank,
207 .original_squared_norm = original_squared_norm,
208 .discarded_weight = discarded_weight,
209 .smallest_retained_singular_value =
210 retained_rank == 0 ? std::nullopt : std::optional<real_type>{value_at(retained_rank - 1)},
211 .largest_discarded_singular_value =
212 retained_rank == available_rank ? std::nullopt : std::optional<real_type>{value_at(retained_rank)}};
215template <uni20::RankedImmediateTensorView<1> SingularValueTensor>
221 return select_svd_truncation_values<real_type>(
223 [&](std::size_t index) { return singular_values[static_cast<uni20::index_type>(index)]; }, policy);
226template <
class ExactResult>
227[[nodiscard]]
auto truncate_svd_result(
232 using exact_type = std::remove_cvref_t<ExactResult>;
237 auto truncation = select_svd_truncation(exact.singular_values, policy);
238 auto const retained_rank =
static_cast<uni20::index_type>(truncation.retained_rank);
239 auto const rows = exact.left_singular_vectors.extent(0);
240 auto const cols = exact.right_singular_vectors_adjoint.extent(1);
250 left_singular_vectors[
row, column] = exact.left_singular_vectors[
row, column];
252 right_singular_vectors_adjoint[column, output_column] =
253 exact.right_singular_vectors_adjoint[column, output_column];
256 return TruncatedSvdResult<
decltype(left_singular_vectors),
decltype(
singular_values),
257 decltype(right_singular_vectors_adjoint),
decltype(truncation)>{
258 .left_singular_vectors = std::move(left_singular_vectors),
260 .right_singular_vectors_adjoint = std::move(right_singular_vectors_adjoint),
261 .truncation = std::move(truncation)};
272template <uni20::Real Real>
276 return detail::select_svd_truncation_values<Real>(
277 singular_values.size(), [&](std::size_t index) { return singular_values[index]; }, policy);
281template <KernelBackendSelector BackendSelector, uni20::RankedTensorView<2> MatrixTensor>
285 std::optional<Precision> requested = {})
287 auto exact =
svd(std::forward<BackendSelector>(selector), matrix, SvdOptions{}, requested);
288 return detail::truncate_svd_result(std::move(exact), policy);
292template <uni20::RankedTensorView<2> MatrixTensor>
296 std::optional<Precision> requested = {})
298 auto exact =
svd(matrix, SvdOptions{}, requested);
299 return detail::truncate_svd_result(std::move(exact), policy);
306template <KernelBackendSelector BackendSelector,
class MatrixTensor>
308 (!std::is_lvalue_reference_v<MatrixTensor>) && (!std::is_const_v<std::remove_reference_t<MatrixTensor>>)
310 BackendSelector&& selector, MatrixTensor&& matrix,
312 std::optional<Precision> requested = {})
315 svd(std::forward<BackendSelector>(selector), std::forward<MatrixTensor>(matrix), SvdOptions{}, requested);
316 return detail::truncate_svd_result(std::move(exact), policy);
320template <
class MatrixTensor>
322 (!std::is_lvalue_reference_v<MatrixTensor>) && (!std::is_const_v<std::remove_reference_t<MatrixTensor>>)
325 std::optional<Precision> requested = {})
327 auto exact =
svd(std::forward<MatrixTensor>(matrix), SvdOptions{}, requested);
328 return detail::truncate_svd_result(std::move(exact), policy);
332template <RankedTensorView<2> Matrix>
340template <KernelBackendSelector Selector, RankedTensorView<2> Matrix>
345 std::optional<Precision>{
p});
AsyncTask square(ReadBuffer< int > in, WriteBuffer< int > out)
Definition async_tbb_reduction_example.cpp:39
Exact arithmetic or an explicit binary working precision.
Definition core/precision.hpp:13
General-purpose owning tensor with immediate or deferred mdspan metadata.
Definition basic_tensor.hpp:109
Tensor-level object that owns the storage described by its multidimensional view.
Definition tensor/concepts.hpp:116
Concept for real scalar types (float, double, etc.).
Definition scalar_concepts.hpp:69
locale_t value
Definition data_table.cpp:49
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
constexpr bool isfinite(I const &x) noexcept
Returns whether an integer scalar is finite.
Definition math.hpp:121
Scalar math helper utilities.
int rows(std::FILE *stream)
Return the terminal row count associated with a C stream.
Definition terminal.cpp:482
R ratio(DiagnosticMagnitude< R > const &a, DiagnosticMagnitude< R > const &b)
Definition gmres_diagnostics.hpp:125
Definition linalg/async.hpp:28
auto truncated_svd(BackendSelector selector, async::Async< MatrixTensor > const &matrix, detail::svd_truncation_policy_t< MatrixTensor > policy={}, std::optional< Precision > requested={})
Schedule a preserving truncated SVD through an explicit selector.
Definition async/truncated_svd.hpp:144
auto select_svd_truncation(std::span< Real const > singular_values, SvdTruncationPolicy< Real > const &policy={}) -> SvdTruncationInfo< Real >
Select a retained prefix from descending singular values.
Definition ops/truncated_svd.hpp:273
auto svd(BackendSelector selector, async::Async< MatrixTensor > const &matrix, SvdOptions options={}, std::optional< Precision > requested={})
Schedule a preserving exact SVD through an explicit selector.
Definition async/svd.hpp:531
auto singular_values(BackendSelector selector, async::Async< MatrixTensor > const &matrix, std::optional< Precision > requested={})
Schedule preserving exact singular values through an explicit selector.
Definition async/svd.hpp:416
Definition data_table.cpp:8
constexpr uninitialized_t uninitialized
Explicit allocation tag for overwrite outputs and scratch space.
Definition common/initialization.hpp:22
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
constexpr Precision precision_of(T const &value)
Read scalar precision without changing any scalar or provider state.
Definition runtime_precision.hpp:53
std::remove_cv_t< typename tensor_mdspec_t< T >::element_type > tensor_element_t
Scalar value type exposed by a tensor-level object.
Definition tensor/concepts.hpp:126
std::ptrdiff_t index_type
Definition types.hpp:19
Destructive and value-producing exact dense singular value decompositions.
Statistics describing a completed SVD truncation.
Definition ops/truncated_svd.hpp:44
std::size_t available_rank
Definition ops/truncated_svd.hpp:45
std::optional< Real > largest_discarded_singular_value
Definition ops/truncated_svd.hpp:50
std::optional< Real > smallest_retained_singular_value
Definition ops/truncated_svd.hpp:49
Real discarded_weight
Definition ops/truncated_svd.hpp:48
Real original_squared_norm
Definition ops/truncated_svd.hpp:47
std::size_t retained_rank
Definition ops/truncated_svd.hpp:46
Rank-selection policy for a truncated singular value decomposition.
Definition ops/truncated_svd.hpp:34
std::optional< Real > maximum_discarded_weight
Definition ops/truncated_svd.hpp:39
std::size_t minimum_retained_extent
Definition ops/truncated_svd.hpp:35
std::size_t maximum_retained_extent
Definition ops/truncated_svd.hpp:36
std::optional< Real > singular_value_cutoff
Definition ops/truncated_svd.hpp:37
std::optional< Real > normalized_squared_singular_value_cutoff
Definition ops/truncated_svd.hpp:38
Owning factors and statistics returned by truncated_svd.
Definition ops/truncated_svd.hpp:58
TruncationInfo truncation
Definition ops/truncated_svd.hpp:67
TruncationInfo truncation_info_type
Definition ops/truncated_svd.hpp:62
LeftTensor left_singular_vectors
Definition ops/truncated_svd.hpp:64
RightAdjointTensor right_singular_vector_adjoint_tensor_type
Definition ops/truncated_svd.hpp:61
SingularValueTensor singular_values
Definition ops/truncated_svd.hpp:65
LeftTensor left_singular_vector_tensor_type
Definition ops/truncated_svd.hpp:59
SingularValueTensor singular_value_tensor_type
Definition ops/truncated_svd.hpp:60
RightAdjointTensor right_singular_vectors_adjoint
Definition ops/truncated_svd.hpp:66
Return the sum of two element values.
Definition elementwise_functions.hpp:102
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16
General-purpose owning tensors with runtime extents.
#define CHECK(cond,...)
Definition trace.hpp:299
#define ERROR_IF(cond,...)
Definition trace.hpp:389