Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
matrix_free.hpp
Go to the documentation of this file.
1#pragma once
2
4#include <uni20/core/math.hpp>
5
6#include <algorithm>
7#include <cmath>
8#include <complex>
9#include <concepts>
10#include <cstddef>
11#include <limits>
12#include <optional>
13#include <stdexcept>
14#include <string>
15#include <type_traits>
16#include <vector>
17
18namespace uni20::krylov
19{
20
34
37{
38 None,
39 Summary,
40 Full
41};
42
53
66
68enum class RitzReality
69{
70 Real,
73};
74
84
87
94
97
118
121template <uni20::Real Scalar> struct NonsymmetricArnoldiDiagnostics
122{
124 int op_count = 0;
128 std::vector<uni20::complex<Scalar>> final_ritz_values;
129 std::vector<Scalar> final_ritz_bounds;
130 std::vector<RitzReality> final_ritz_reality;
131};
132
142template <uni20::Real Scalar, typename Vector> struct NonsymmetricEigenResult
143{
144 std::vector<uni20::complex<Scalar>> eigenvalues;
145 std::vector<Scalar> residual_bounds;
146 std::vector<Vector> right_eigenvectors;
147 std::vector<RitzReality> reality;
152 std::optional<NonsymmetricArnoldiDiagnostics<Scalar>> diagnostics;
153};
154
180
187[[nodiscard]] inline int default_symmetric_krylov_dimension(int eigenvalue_count, std::size_t problem_dimension)
188{
189 if (eigenvalue_count <= 0)
190 {
191 throw std::invalid_argument("default symmetric Krylov dimension requires a positive eigenvalue count");
192 }
193 if (problem_dimension > static_cast<std::size_t>(uni20::numeric_limits<int>::max()))
194 {
195 throw std::overflow_error("Krylov problem dimension exceeds the supported int range");
196 }
197 int const dimension = static_cast<int>(problem_dimension);
198 int const target = std::max(20, 2 * eigenvalue_count + 1);
199 return std::min(dimension, target);
200}
201
208[[nodiscard]] inline int default_nonsymmetric_krylov_dimension(int eigenvalue_count, std::size_t problem_dimension)
209{
210 if (eigenvalue_count <= 0)
211 {
212 throw std::invalid_argument("default nonsymmetric Krylov dimension requires a positive eigenvalue count");
213 }
214 if (problem_dimension > static_cast<std::size_t>(uni20::numeric_limits<int>::max()))
215 {
216 throw std::overflow_error("Krylov problem dimension exceeds the supported int range");
217 }
218 int const dimension = static_cast<int>(problem_dimension);
219 int const target = std::max(20, 6 * eigenvalue_count + 8);
220 return std::min(dimension, target);
221}
222
224template <uni20::Real Scalar>
226 std::size_t problem_dimension)
227{
228 return params.krylov_dimension > 0 ? params.krylov_dimension
229 : default_symmetric_krylov_dimension(params.eigenvalue_count, problem_dimension);
230}
231
233template <uni20::Real Scalar>
235 std::size_t problem_dimension)
236{
237 return params.krylov_dimension > 0
238 ? params.krylov_dimension
239 : default_nonsymmetric_krylov_dimension(params.eigenvalue_count, problem_dimension);
240}
241
248[[nodiscard]] inline int default_symmetric_retained_ritz_count(int eigenvalue_count, int krylov_dimension)
249{
250 if (eigenvalue_count <= 0)
251 {
252 throw std::invalid_argument("default symmetric retained Ritz count requires a positive eigenvalue count");
253 }
254 if (krylov_dimension <= eigenvalue_count)
255 {
256 return eigenvalue_count;
257 }
258 return eigenvalue_count;
259}
260
267[[nodiscard]] inline int default_nonsymmetric_retained_ritz_count(int eigenvalue_count, int krylov_dimension)
268{
269 if (eigenvalue_count <= 0)
270 {
271 throw std::invalid_argument("default nonsymmetric retained Ritz count requires a positive eigenvalue count");
272 }
273 if (krylov_dimension <= eigenvalue_count)
274 {
275 return eigenvalue_count;
276 }
277 int const target = std::max(2 * eigenvalue_count, eigenvalue_count + 4);
278 return std::min(krylov_dimension - 1, target);
279}
280
282template <uni20::Real Scalar>
284 int krylov_dimension)
285{
286 int const retained_count = params.retained_ritz_count > 0
287 ? params.retained_ritz_count
289 if (retained_count < params.eigenvalue_count)
290 {
291 throw std::invalid_argument("retained Ritz count must be at least the requested eigenvalue count");
292 }
293 if (retained_count >= krylov_dimension)
294 {
295 throw std::invalid_argument("retained Ritz count must be smaller than the Krylov dimension");
296 }
297 return retained_count;
298}
299
301template <uni20::Real Scalar>
303 int krylov_dimension)
304{
305 int const retained_count = params.retained_ritz_count > 0
306 ? params.retained_ritz_count
308 if (retained_count < params.eigenvalue_count)
309 {
310 throw std::invalid_argument("nonsymmetric retained Ritz count is below eigenvalue count");
311 }
312 if (retained_count >= krylov_dimension)
313 {
314 throw std::invalid_argument("nonsymmetric retained Ritz count must be smaller than the Krylov dimension");
315 }
316 return retained_count;
317}
318
321template <uni20::Real Scalar> struct SymmetricRestartCycleDiagnostics
322{
323 int cycle = 0;
325 std::size_t projected_dimension = 0;
326 std::size_t retained_count = 0;
327 std::size_t shift_count = 0;
331 std::vector<Scalar> ritz_values;
332 std::vector<Scalar> ritz_bounds;
333 std::vector<Scalar> shifts;
334 std::vector<std::size_t> wanted_indices;
335 std::vector<std::size_t> shift_indices;
336 std::vector<bool> wanted_converged;
337};
338
341template <uni20::Real Scalar> struct SymmetricLanczosDiagnostics
342{
344 int op_count = 0;
347 std::vector<Scalar> final_ritz_values;
348 std::vector<Scalar> final_ritz_bounds;
349 std::vector<std::size_t> final_selected_indices;
350 std::vector<SymmetricRestartCycleDiagnostics<Scalar>> restart_cycles;
351};
352
354template <typename Scalar, typename Vector> struct EigenResult
355{
356 std::vector<Scalar> eigenvalues;
357 std::vector<Scalar> residual_bounds;
358 std::vector<Vector> eigenvectors;
362 int status = 0;
363 std::optional<SymmetricLanczosDiagnostics<Scalar>> diagnostics;
364};
365
381template <typename Ops, typename Vector, typename Scalar>
382concept KrylovVectorOps = requires(Ops ops, Vector x, Vector y, Scalar alpha) {
383 { ops.problem_dimension() } -> std::convertible_to<std::size_t>;
384 { ops.vector_dimension(x) } -> std::convertible_to<std::size_t>;
385 { ops.allocate_like(x) } -> std::same_as<Vector>;
386 { ops.copy(y, x) } -> std::same_as<void>;
387 { ops.axpy(y, alpha, x) } -> std::same_as<void>;
388 { ops.scal(y, alpha) } -> std::same_as<void>;
389 { ops.set_zero(y) } -> std::same_as<void>;
390 { ops.inner_product(x, y) } -> std::convertible_to<Scalar>;
391};
392
394template <typename Op, typename Vector>
395concept KrylovOperator = requires(Op op, Vector x, Vector y) {
396 { op.matvec(y, x) } -> std::same_as<void>;
397};
398
405template <typename Ops, typename Vector, typename Scalar>
407
409template <typename Ops, typename Vector, typename Scalar>
411
420template <typename Ops, typename Vector, typename Scalar>
421concept KrylovMetricInnerProduct = requires(Ops ops, Vector x, Vector y) {
422 { ops.metric_inner_product(x, y) } -> std::convertible_to<Scalar>;
423};
424
426template <typename Ops, typename Vector, typename Scalar>
428
436template <typename Ops, typename Vector, typename Scalar>
437concept KrylovNorm = requires(Ops ops, Vector x) {
438 { ops.norm(x) } -> std::convertible_to<uni20::make_real_t<Scalar>>;
439};
440
447template <typename Scalar, typename Vector, typename Ops>
449{
450 using RealScalar = uni20::make_real_t<Scalar>;
452 {
453 return static_cast<RealScalar>(ops.norm(x));
454 }
455 else
456 {
457 Scalar const norm_squared = ops.inner_product(x, x);
458 RealScalar const real_norm_squared = static_cast<RealScalar>(uni20::math::real(norm_squared));
459 return real_norm_squared > RealScalar{} ? uni20::math::sqrt(real_norm_squared) : RealScalar{};
460 }
461}
462
466template <uni20::Real Scalar>
467 requires requires { uni20::numeric_limits<Scalar>::epsilon(); }
472
475template <uni20::Real Scalar> Scalar default_complex_pair_tolerance(Scalar const& exemplar)
476{
478}
479
498template <uni20::Complex C>
501 uni20::make_real_t<C> ambiguity_factor = uni20::make_real_t<C>{10})
502{
503 using Scalar = uni20::make_real_t<C>;
504 Scalar const effective_tolerance = tolerance > Scalar{} ? tolerance : default_complex_pair_tolerance(theta.real());
505 if constexpr (requires {
506 theta.is_exact();
507 theta.at(effective_tolerance.precision());
508 })
509 {
510 if (theta.is_exact()) theta = theta.at(effective_tolerance.precision());
511 }
512 Scalar const effective_scale = std::max({Scalar{1}, uni20::math::abs(theta), uni20::math::abs(scale)});
513 Scalar const threshold = effective_tolerance * effective_scale;
514 Scalar const imaginary = uni20::math::abs(theta.imag());
515 if (imaginary <= threshold)
516 {
517 return RitzReality::Real;
518 }
519 if (imaginary <= ambiguity_factor * threshold)
520 {
522 }
524}
525
532template <typename Scalar, typename Vector, typename MetricOps, typename BOps, typename InnerOps>
533Scalar metric_inner_product_or_apply(MetricOps& metric_ops, BOps& b_ops, InnerOps& inner_ops, Vector const& x,
534 Vector const& y, Vector& scratch)
535{
537 {
538 return metric_ops.metric_inner_product(x, y);
539 }
540 else
541 {
542 b_ops.matvec(scratch, y);
543 return inner_ops.inner_product(x, scratch);
544 }
545}
546
548template <typename Ops, typename Vector>
549std::size_t validate_matrix_free_dimensions(Ops const& ops, Vector const& vector, char const* context)
550{
551 std::size_t const problem_dimension = ops.problem_dimension();
552 std::size_t const vector_dimension = ops.vector_dimension(vector);
553 if (problem_dimension == 0)
554 {
555 throw std::invalid_argument(std::string(context) + " requires a non-empty problem dimension");
556 }
557 if (vector_dimension != problem_dimension)
558 {
559 throw std::invalid_argument(std::string(context) + " vector dimension does not match operator dimension");
560 }
561 return problem_dimension;
562}
563
564} // namespace uni20::krylov
Concept for complex scalar types.
Definition scalar_concepts.hpp:75
Concept for numeric scalar types (integer, real, or complex).
Definition scalar_concepts.hpp:81
Composite concept for current adapters that own vector ops and OP*x.
Definition matrix_free.hpp:406
Optional customization point for backend-owned metric inner products.
Definition matrix_free.hpp:421
Optional customization point for direct vector norms.
Definition matrix_free.hpp:437
Concept for an output-first Krylov operator application y <- OP*x.
Definition matrix_free.hpp:395
Concept for opaque vector-space operations required by Krylov solvers.
Definition matrix_free.hpp:382
Backward-compatible name for the combined vector/operator concept.
Definition matrix_free.hpp:410
Backward-compatible name for direct metric inner-product customization.
Definition matrix_free.hpp:427
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
Scalar math helper utilities.
Definition block_tensor_vector.hpp:23
int default_nonsymmetric_krylov_dimension(int eigenvalue_count, std::size_t problem_dimension)
Return the default nonsymmetric Arnoldi subspace dimension.
Definition matrix_free.hpp:208
int effective_nonsymmetric_retained_ritz_count(NonsymmetricEigenParams< Scalar > const &params, int krylov_dimension)
Return the effective nonsymmetric retained Ritz or Schur count.
Definition matrix_free.hpp:302
uni20::make_real_t< Scalar > norm_or_inner_product(Ops &ops, Vector const &x)
Evaluate a vector norm, using a backend override when available.
Definition matrix_free.hpp:448
int effective_symmetric_krylov_dimension(SymmetricEigenParams< Scalar > const &params, std::size_t problem_dimension)
Return the effective symmetric/Hermitian Krylov subspace dimension.
Definition matrix_free.hpp:225
KrylovDiagnosticsLevel
Amount of solver-internal diagnostic state to retain.
Definition matrix_free.hpp:37
SymmetricTransformKind
Transform represented by the caller-supplied symmetric Krylov operator.
Definition matrix_free.hpp:77
int default_symmetric_krylov_dimension(int eigenvalue_count, std::size_t problem_dimension)
Return the default symmetric/Hermitian Krylov subspace dimension.
Definition matrix_free.hpp:187
Scalar metric_inner_product_or_apply(MetricOps &metric_ops, BOps &b_ops, InnerOps &inner_ops, Vector const &x, Vector const &y, Vector &scratch)
Evaluate a metric inner product, using a backend override when available.
Definition matrix_free.hpp:533
RitzReality
Numerical reality classification for a complex Ritz value from a real Arnoldi solve.
Definition matrix_free.hpp:69
int default_symmetric_retained_ritz_count(int eigenvalue_count, int krylov_dimension)
Return the default symmetric/Hermitian retained Ritz count.
Definition matrix_free.hpp:248
SpectrumPart
Spectrum selector for matrix-free eigenvalue solvers.
Definition matrix_free.hpp:23
int default_nonsymmetric_retained_ritz_count(int eigenvalue_count, int krylov_dimension)
Return the default nonsymmetric retained Schur dimension.
Definition matrix_free.hpp:267
int effective_symmetric_retained_ritz_count(SymmetricEigenParams< Scalar > const &params, int krylov_dimension)
Return the effective symmetric/Hermitian retained Ritz count.
Definition matrix_free.hpp:283
RealNonsymmetricPolicy
Policy for real nonsymmetric solves when wanted Ritz values may be complex.
Definition matrix_free.hpp:45
@ RequireRealEigenpairs
Accept only Ritz values that are numerically real; report genuinely complex wanted pairs.
@ AllowRealSchurPairs
Allow real Schur two-plane output for complex conjugate pairs once that output is implemented.
@ PromoteToComplexSuggested
Stop with a promotion recommendation when wanted Ritz values are genuinely complex.
int effective_nonsymmetric_krylov_dimension(NonsymmetricEigenParams< Scalar > const &params, std::size_t problem_dimension)
Return the effective nonsymmetric Arnoldi subspace dimension.
Definition matrix_free.hpp:234
RitzReality classify_ritz_reality(C theta, uni20::make_real_t< C > tolerance=uni20::make_real_t< C >{}, uni20::make_real_t< C > scale=uni20::make_real_t< C >{1}, uni20::make_real_t< C > ambiguity_factor=uni20::make_real_t< C >{10})
Classify whether a complex Ritz value is numerically real.
Definition matrix_free.hpp:499
std::size_t validate_matrix_free_dimensions(Ops const &ops, Vector const &vector, char const *context)
Validate that an initial/prototype vector matches the matrix-free problem dimension.
Definition matrix_free.hpp:549
NonsymmetricStatus
Native nonsymmetric solver status.
Definition matrix_free.hpp:56
Scalar default_complex_pair_tolerance()
Default imaginary-part tolerance for deciding whether a real Arnoldi Ritz value is real.
Definition matrix_free.hpp:468
constexpr detail::scalar_math::sqrt_fn sqrt
Square root; exact inputs requiring approximation need an explicit finite precision.
Definition math.hpp:461
constexpr detail::scalar_math::abs_fn abs
Absolute value or complex magnitude, preserving the selected scalar implementation.
Definition math.hpp:459
constexpr detail::scalar_math::real_fn real
Read the real component by value; real and integral scalars retain their type.
Definition math.hpp:671
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
Matrix-free eigenvalue result with solver-owned vector objects.
Definition matrix_free.hpp:355
std::vector< Scalar > residual_bounds
Definition matrix_free.hpp:357
std::vector< Scalar > eigenvalues
Definition matrix_free.hpp:356
int status
Definition matrix_free.hpp:362
std::vector< Vector > eigenvectors
Definition matrix_free.hpp:358
int matvec_count
Definition matrix_free.hpp:361
int converged_count
Definition matrix_free.hpp:359
int iteration_count
Definition matrix_free.hpp:360
std::optional< SymmetricLanczosDiagnostics< Scalar > > diagnostics
Definition matrix_free.hpp:363
Optional diagnostic state for nonsymmetric Arnoldi solves.
Definition matrix_free.hpp:122
int restart_count
Definition matrix_free.hpp:123
std::vector< uni20::complex< Scalar > > final_ritz_values
Definition matrix_free.hpp:128
int op_count
Definition matrix_free.hpp:124
int final_projected_dimension
Definition matrix_free.hpp:125
Scalar final_residual_norm
Definition matrix_free.hpp:126
std::vector< Scalar > final_ritz_bounds
Definition matrix_free.hpp:129
Scalar projected_departure_from_normality
Definition matrix_free.hpp:127
std::vector< RitzReality > final_ritz_reality
Definition matrix_free.hpp:130
Parameters for a native nonsymmetric eigenvalue solve.
Definition matrix_free.hpp:106
Scalar complex_pair_tolerance
Definition matrix_free.hpp:112
int max_iterations
Definition matrix_free.hpp:110
SpectrumPart spectrum
Definition matrix_free.hpp:113
RealNonsymmetricPolicy real_policy
Definition matrix_free.hpp:115
KrylovDiagnosticsLevel diagnostics
Definition matrix_free.hpp:116
int krylov_dimension
Definition matrix_free.hpp:109
bool compute_eigenvectors
Definition matrix_free.hpp:114
int retained_ritz_count
Definition matrix_free.hpp:108
Scalar tolerance
Definition matrix_free.hpp:111
int eigenvalue_count
Definition matrix_free.hpp:107
Matrix-free nonsymmetric eigenvalue result.
Definition matrix_free.hpp:143
std::vector< Scalar > residual_bounds
Definition matrix_free.hpp:145
int converged_count
Definition matrix_free.hpp:148
int matvec_count
Definition matrix_free.hpp:150
std::vector< Vector > right_eigenvectors
Definition matrix_free.hpp:146
NonsymmetricStatus status
Definition matrix_free.hpp:151
int iteration_count
Definition matrix_free.hpp:149
std::vector< uni20::complex< Scalar > > eigenvalues
Definition matrix_free.hpp:144
std::optional< NonsymmetricArnoldiDiagnostics< Scalar > > diagnostics
Definition matrix_free.hpp:152
std::vector< RitzReality > reality
Definition matrix_free.hpp:147
Common parameters for a symmetric or Hermitian eigenvalue solve.
Definition matrix_free.hpp:157
KrylovDiagnosticsLevel diagnostics
Definition matrix_free.hpp:178
int eigenvalue_count
Number of converged eigenpairs requested by the caller.
Definition matrix_free.hpp:159
bool compute_eigenvectors
Definition matrix_free.hpp:177
SpectrumPart spectrum
Definition matrix_free.hpp:176
Scalar tolerance
Definition matrix_free.hpp:170
int max_iterations
Definition matrix_free.hpp:169
int krylov_dimension
Maximum Arnoldi/Lanczos subspace dimension.
Definition matrix_free.hpp:168
Scalar breakdown_tolerance
Relative threshold for declaring Lanczos invariant-subspace breakdown.
Definition matrix_free.hpp:175
int retained_ritz_count
Optional number of Ritz vectors retained internally after restart.
Definition matrix_free.hpp:166
Optional diagnostic state for symmetric Lanczos solves.
Definition matrix_free.hpp:342
Scalar final_residual_norm
Definition matrix_free.hpp:346
int final_projected_dimension
Definition matrix_free.hpp:345
int op_count
Definition matrix_free.hpp:344
std::vector< SymmetricRestartCycleDiagnostics< Scalar > > restart_cycles
Definition matrix_free.hpp:350
int restart_count
Definition matrix_free.hpp:343
std::vector< std::size_t > final_selected_indices
Definition matrix_free.hpp:349
std::vector< Scalar > final_ritz_bounds
Definition matrix_free.hpp:348
std::vector< Scalar > final_ritz_values
Definition matrix_free.hpp:347
Diagnostic state for one symmetric Lanczos restart cycle.
Definition matrix_free.hpp:322
std::size_t projected_dimension
Definition matrix_free.hpp:325
std::vector< std::size_t > wanted_indices
Definition matrix_free.hpp:334
bool enlarged_for_partial_convergence
Definition matrix_free.hpp:329
int cycle
Definition matrix_free.hpp:323
std::vector< Scalar > shifts
Definition matrix_free.hpp:333
std::size_t shift_count
Definition matrix_free.hpp:327
std::vector< Scalar > ritz_values
Definition matrix_free.hpp:331
std::vector< std::size_t > shift_indices
Definition matrix_free.hpp:335
std::size_t protected_zero_bound_count
Definition matrix_free.hpp:328
Scalar residual_norm
Definition matrix_free.hpp:330
std::size_t retained_count
Definition matrix_free.hpp:326
std::vector< Scalar > ritz_bounds
Definition matrix_free.hpp:332
std::vector< bool > wanted_converged
Definition matrix_free.hpp:336
int converged_count
Definition matrix_free.hpp:324
Native symmetric transform options for mapping OP Ritz values.
Definition matrix_free.hpp:90
SymmetricTransformKind transform
Definition matrix_free.hpp:91
Scalar sigma
Definition matrix_free.hpp:92
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16