45#ifdef MXLIB_SVD_DELETION_TEST_PRODUCER_ALIGNMENT
46static_assert( EIGEN_MAX_ALIGN_BYTES == MXLIB_SVD_DELETION_TEST_PRODUCER_ALIGNMENT,
47 "The SVD-deletion test producer must use its configured dynamic alignment." );
48static_assert( EIGEN_MAX_STATIC_ALIGN_BYTES == MXLIB_SVD_DELETION_TEST_PRODUCER_ALIGNMENT,
49 "The SVD-deletion test producer must use its configured static alignment." );
56template <
typename realT>
57using svdDeletionStorageMatrix =
58 Eigen::Array<realT, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor | Eigen::DontAlign>;
61template <
typename realT>
62using svdDeletionStorageVector = Eigen::Array<realT, Eigen::Dynamic, 1, Eigen::DontAlign>;
66template <
typename realT,
typename abiT>
71 svdDeletionStorageVector<realT> m_singularValues;
74 svdDeletionStorageVector<realT> m_squaredSingularValues;
77 svdDeletionStorageMatrix<realT> m_rotation;
86 Eigen::Index m_baseRank{ 0 };
89 Eigen::Index m_outputRank{ 0 };
92 Eigen::Index m_maximumOutputRank{ 0 };
95 Eigen::Index m_clampedEigenvalues{ 0 };
98 realT m_minimumPSDValue{ 0 };
101 MXLAPACK_INT m_lapackInfo{ 0 };
104template <
typename realT,
typename abiT>
109 svdDeletionStorageMatrix<realT> m_deletedRows;
112 svdDeletionStorageVector<realT> m_normalizedSingularValues;
115 svdDeletionStorageMatrix<realT> m_scaledDeletedTranspose;
118 svdDeletionStorageMatrix<realT> m_psdCore;
121 svdDeletionStorageMatrix<realT> m_eigenvectors;
124 svdDeletionStorageVector<realT> m_eigenvalues;
127 svdDeletionStorageMatrix<realT> m_complementRoot;
130 svdDeletionStorageMatrix<realT> m_stableCore;
133 svdDeletionStorageMatrix<realT> m_rightTranspose;
136 svdDeletionStorageVector<realT> m_stableSingularValues;
139 svdDeletionStorageVector<realT> m_secularPoles;
142 svdDeletionStorageVector<realT> m_secularUpdate;
145 svdDeletionStorageVector<realT> m_secularActivePoles;
148 svdDeletionStorageVector<realT> m_secularActiveUpdate;
151 svdDeletionStorageVector<realT> m_secularCandidateValues;
154 svdDeletionStorageVector<realT> m_secularVector;
157 std::vector<Eigen::Index> m_secularActiveIndices;
160 std::vector<Eigen::Index> m_secularDeflatedIndices;
163 std::vector<Eigen::Index> m_secularOrder;
166 std::vector<Eigen::Index> m_secularRotationFirst;
169 std::vector<Eigen::Index> m_secularRotationSecond;
172 std::vector<realT> m_secularRotationCosine;
175 std::vector<realT> m_secularRotationSine;
178 std::vector<MXLAPACK_INT> m_support;
181 std::vector<realT> m_syevrWork;
184 std::vector<MXLAPACK_INT> m_syevrIWork;
187 std::vector<realT> m_gesvdWork;
190 Eigen::Index m_baseRank{ 0 };
193 Eigen::Index m_maximumDeleted{ 0 };
199 bool m_prepared{
false };
202 MXLAPACK_INT m_lapackInfo{ 0 };
212bool validLapackDimension( Eigen::Index dimension )
214 return dimension >= 0 &&
static_cast<unsigned long long>( dimension ) <=
215 static_cast<unsigned long long>( std::numeric_limits<MXLAPACK_INT>::max() );
219bool validAbiDimension( std::int64_t dimension )
221 return dimension >= 0 &&
static_cast<std::uint64_t
>( dimension ) <=
222 static_cast<std::uint64_t
>( std::numeric_limits<Eigen::Index>::max() );
226template <
typename realT>
227bool validAbiVectorView( svdDeletionConstVectorViewV2<realT> view )
229 return validAbiDimension( view.size ) && ( view.size == 0 || view.data != nullptr ) &&
230 ( view.size == 0 ||
reinterpret_cast<std::uintptr_t
>( view.data ) %
alignof( realT ) == 0 ) &&
231 static_cast<std::uint64_t
>( view.size ) <=
232 static_cast<std::uint64_t
>( std::numeric_limits<std::ptrdiff_t>::max() ) /
sizeof( realT );
236template <
typename realT>
237bool validAbiMatrixView( svdDeletionConstMatrixViewV2<realT> view )
239 if( !validAbiDimension( view.rows ) || !validAbiDimension( view.columns ) ||
240 !validAbiDimension( view.outerStride ) )
244 if( view.rows > 0 && view.columns > 0 && view.data ==
nullptr )
248 if( view.rows > 0 && view.columns > 0 &&
reinterpret_cast<std::uintptr_t
>( view.data ) %
alignof( realT ) != 0 )
252 if( view.columns > 1 && view.outerStride < view.rows )
256 if( view.rows == 0 || view.columns == 0 )
261 const std::uint64_t rows =
static_cast<std::uint64_t
>( view.rows );
262 const std::uint64_t columns =
static_cast<std::uint64_t
>( view.columns );
263 const std::uint64_t outerStride =
static_cast<std::uint64_t
>( view.outerStride );
264 const std::uint64_t maximumScalarOffset =
265 static_cast<std::uint64_t
>( std::numeric_limits<std::ptrdiff_t>::max() ) /
sizeof( realT );
266 if( rows - 1 > maximumScalarOffset )
274 return columns - 1 <= ( maximumScalarOffset - ( rows - 1 ) ) / outerStride;
278bool validAbiIndexView( svdDeletionConstIndexViewV2 view )
280 if( !validAbiDimension( view.size ) || ( view.size > 0 && view.data ==
nullptr ) )
288 const bool supportedWidth =
289 view.elementBytes == 1 || view.elementBytes == 2 || view.elementBytes == 4 || view.elementBytes == 8;
290 if( !supportedWidth )
294 return static_cast<std::uint64_t
>( view.size - 1 ) <=
295 static_cast<std::uint64_t
>( std::numeric_limits<std::ptrdiff_t>::max() ) /
296 static_cast<std::uint64_t
>( view.elementBytes );
300std::int64_t abiIndexAt( svdDeletionConstIndexViewV2 view,
301 std::int64_t offset )
303 const std::ptrdiff_t byteOffset =
static_cast<std::ptrdiff_t
>( offset * view.elementBytes );
304 const auto *bytes =
static_cast<const unsigned char *
>( view.data ) + byteOffset;
305 switch( view.elementBytes )
309 std::int8_t value{ 0 };
310 std::memcpy( &value, bytes,
sizeof( value ) );
315 std::int16_t value{ 0 };
316 std::memcpy( &value, bytes,
sizeof( value ) );
321 std::int32_t value{ 0 };
322 std::memcpy( &value, bytes,
sizeof( value ) );
327 std::int64_t value{ 0 };
328 std::memcpy( &value, bytes,
sizeof( value ) );
336template <
typename realT>
337using svdDeletionAbiMatrixMap =
338 Eigen::Map<const svdDeletionMatrix<realT>, Eigen::Unaligned, Eigen::OuterStride<Eigen::Dynamic>>;
341template <
typename realT>
342svdDeletionAbiMatrixMap<realT>
343mapAbiMatrix( svdDeletionConstMatrixViewV2<realT> view )
345 return svdDeletionAbiMatrixMap<realT>(
347 static_cast<Eigen::Index
>( view.rows ),
348 static_cast<Eigen::Index
>( view.columns ),
349 Eigen::OuterStride<Eigen::Dynamic>(
static_cast<Eigen::Index
>( view.outerStride ) ) );
353template <
typename realT>
354Eigen::Map<const svdDeletionVector<realT>, Eigen::Unaligned>
355mapAbiVector( svdDeletionConstVectorViewV2<realT> view )
357 return Eigen::Map<const svdDeletionVector<realT>, Eigen::Unaligned>( view.data,
358 static_cast<Eigen::Index
>( view.size ) );
362template <
typename valueT>
363bool validArrayShape( Eigen::Index rows,
366 if( rows < 0 || cols < 0 || ( rows > 0 && cols > std::numeric_limits<Eigen::Index>::max() / rows ) )
370 const Eigen::Index elements = rows * cols;
371 return static_cast<unsigned long long>( elements ) <=
372 static_cast<unsigned long long>( std::numeric_limits<std::size_t>::max() /
sizeof( valueT ) );
376template <
typename valueT>
377bool validVectorSize( Eigen::Index elements )
379 return elements >= 0 &&
380 static_cast<unsigned long long>( elements ) <=
381 static_cast<unsigned long long>( std::numeric_limits<std::size_t>::max() /
sizeof( valueT ) );
385template <
typename derivedT>
386bool allFinite(
const Eigen::DenseBase<derivedT> &values )
388 for( Eigen::Index index = 0; index < values.size(); ++index )
399template <
typename derivedT>
400bool validDescendingSpectrum(
const Eigen::DenseBase<derivedT> &values )
402 for( Eigen::Index index = 0; index < values.size(); ++index )
404 const auto value = values.derived()( index );
405 if( !
math::isFinite( value ) || value < 0 || ( index > 0 && value > values.derived()( index - 1 ) ) )
414template <
typename derivedT>
415bool validAscendingSpectrum(
const Eigen::DenseBase<derivedT> &values )
417 for( Eigen::Index index = 0; index < values.size(); ++index )
419 const auto value = values.derived()( index );
420 if( !
math::isFinite( value ) || ( index > 0 && value < values.derived()( index - 1 ) ) )
429template <
typename realT>
430void callOperationHook( svdDeletionTestOperation operation )
432 auto &hooks = svdDeletionHooks<realT>();
433 if( hooks.operation )
435 hooks.operation( operation );
440template <
typename realT>
441MXLAPACK_INT callSyevr(
char jobz,
449 MXLAPACK_INT indexLow,
450 MXLAPACK_INT indexHigh,
456 MXLAPACK_INT *support,
460 MXLAPACK_INT liwork )
462 auto &hooks = svdDeletionHooks<realT>();
465 return hooks.syevr( jobz,
510template <
typename realT>
511MXLAPACK_INT callGesvd(
char jobu,
525 auto &hooks = svdDeletionHooks<realT>();
528 return hooks.gesvd( jobu, jobvt, rows, cols, matrix, lda, singular, left, ldu, rightT, ldvt, work, lwork );
531 return math::gesvd<realT>( jobu, jobvt, rows, cols, matrix, lda, singular, left, ldu, rightT, ldvt, work, lwork );
535template <
typename realT>
536MXLAPACK_INT callLaed9( realT *eigenvalues,
540 MXLAPACK_INT leadingDimension,
545 auto &hooks = svdDeletionHooks<realT>();
548 return hooks.laed9( eigenvalues, delta, eigenvectors, rank, leadingDimension, rho, poles, update );
566template <
typename realT>
567bool querySize( MXLAPACK_INT &size,
571 static_cast<long double>( query ) >
static_cast<long double>( std::numeric_limits<MXLAPACK_INT>::max() ) )
576 size =
static_cast<MXLAPACK_INT
>( std::ceil( query ) );
581template <
typename realT>
582realT psdTolerance( Eigen::Index dimension,
585 return realT( 64 ) * std::numeric_limits<realT>::epsilon() *
586 static_cast<realT
>( std::max<Eigen::Index>( 1, dimension ) ) * scale;
591template <
typename realT>
592svdDeletionTestHooks<realT> &svdDeletionHooks()
594 static svdDeletionTestHooks<realT> hooks;
599template <
typename realT>
600struct svdDeletionImplementation
602 using resultT = svdDeletionResult<realT>;
603 using workspaceT = svdDeletionWorkspace<realT>;
608 MXLAPACK_INT lapackInfo = 0 )
610 if( !result.ensureStorage() )
614 result.m_storage->m_status = status;
615 result.m_storage->m_lapackInfo = lapackInfo;
616 result.m_storage->m_clampedEigenvalues = 0;
619 result.m_storage->m_minimumPSDValue = realT( 0 );
633 Eigen::Index outputRank )
636 if( rank <= 0 || !validLapackDimension( rank ) || outputRank <= 0 || outputRank > rank ||
637 !allFinite( singularValues ) )
642 for( Eigen::Index index = 0; index < rank; ++index )
656 Eigen::Index outputRank )
659 return validSingularInputs( singularValues, outputRank ) && deletedRows.cols() == rank &&
660 validLapackDimension( deletedRows.rows() ) &&
661 deletedRows.rows() <= std::numeric_limits<MXLAPACK_INT>::max() - rank && allFinite( deletedRows );
665 static realT normalizeSingularValues( workspaceT &workspace,
669 if( scale == realT( 0 ) )
671 workspace.m_storage->m_normalizedSingularValues.setZero();
675 int scaleExponent{ 0 };
676 const realT scaleMantissa = std::frexp( scale, &scaleExponent );
677 for( Eigen::Index index = 0; index <
singularValues.size(); ++index )
679 int valueExponent{ 0 };
680 const realT valueMantissa = std::frexp(
singularValues( index ), &valueExponent );
681 workspace.m_storage->m_normalizedSingularValues( index ) =
682 std::ldexp( valueMantissa / scaleMantissa, valueExponent - scaleExponent );
688 static realT normalizedRatio( realT value,
691 int scaleExponent{ 0 };
692 int valueExponent{ 0 };
693 const realT scaleMantissa = std::frexp( scale, &scaleExponent );
694 const realT valueMantissa = std::frexp( value, &valueExponent );
695 return std::ldexp( valueMantissa / scaleMantissa, valueExponent - scaleExponent );
699 static bool rescaleSingularValue( realT &singularValue,
700 realT &squaredSingularValue,
701 realT normalizedValue,
704 const realT maximum = std::numeric_limits<realT>::max();
705 singularValue = normalizedValue * scale;
706 if( !
math::isFinite( singularValue ) || singularValue > std::sqrt( maximum ) )
710 squaredSingularValue = singularValue * singularValue;
715 static bool rescaleValue( realT &singularValue,
716 realT &squaredSingularValue,
717 realT normalizedSquaredValue,
720 return rescaleSingularValue( singularValue, squaredSingularValue, std::sqrt( normalizedSquaredValue ), scale );
725 workspaceT &workspace,
727 Eigen::Index deletedCount,
728 Eigen::Index outputRank,
737 if( !workspace.prepared() || workspace.baseRank() != rank || workspace.maximumDeleted() < deletedCount ||
738 workspace.backend() != backend )
740 status = workspace.prepare( rank, deletedCount, backend );
743 return fail( result, status, workspace.lapackInfo() );
751 static void loadDeletedRows( workspaceT &workspace,
754 if( deletedRows.rows() > 0 )
756 workspace.m_storage->m_deletedRows.matrix().topRows( deletedRows.rows() ) = deletedRows.matrix();
763 Eigen::Index outputRank,
772 result.m_storage->m_rotation.setZero();
773 for( Eigen::Index index = 0; index <
singularValues.size(); ++index )
775 result.m_storage->m_singularValues( index ) =
singularValues( index );
777 singularValues( index ) > std::sqrt( std::numeric_limits<realT>::max() ) )
782 if( index < outputRank )
784 result.m_storage->m_rotation( index, index ) = realT( 1 );
787 result.m_storage->m_backend = backend;
789 result.m_storage->m_clampedEigenvalues = 0;
793 realT minimumNormalized{ 0 };
794 if( scale != realT( 0 ) )
798 result.m_storage->m_minimumPSDValue = minimumNormalized * minimumNormalized;
802 result.m_storage->m_minimumPSDValue = realT( 1 );
804 result.m_storage->m_lapackInfo = 0;
805 return result.m_storage->m_status;
810 Eigen::Index outputRank,
813 result.m_storage->m_rotation.setZero();
814 result.m_storage->m_singularValues.setZero();
815 result.m_storage->m_squaredSingularValues.setZero();
816 for( Eigen::Index index = 0; index < outputRank; ++index )
818 result.m_storage->m_rotation( index, index ) = realT( 1 );
820 result.m_storage->m_backend = backend;
822 result.m_storage->m_clampedEigenvalues = 0;
823 result.m_storage->m_minimumPSDValue = realT( 0 );
824 result.m_storage->m_lapackInfo = 0;
825 return result.m_storage->m_status;
831 Eigen::Index deletedCount,
832 Eigen::Index outputRank,
833 workspaceT &workspace )
836 const realT scale = normalizeSingularValues( workspace, singularValues );
837 if( scale == realT( 0 ) )
842 for( Eigen::Index column = 0; column < deletedCount; ++column )
844 for( Eigen::Index row = 0; row < rank; ++row )
846 workspace.m_storage->m_scaledDeletedTranspose( row, column ) =
847 workspace.m_storage->m_normalizedSingularValues( row ) *
848 workspace.m_storage->m_deletedRows( column, row );
852 const auto scaledDeleted = workspace.m_storage->m_scaledDeletedTranspose.matrix().leftCols( deletedCount );
853 workspace.m_storage->m_psdCore.matrix().noalias() = -scaledDeleted * scaledDeleted.transpose();
854 for( Eigen::Index index = 0; index < rank; ++index )
856 workspace.m_storage->m_psdCore( index, index ) += workspace.m_storage->m_normalizedSingularValues( index ) *
857 workspace.m_storage->m_normalizedSingularValues( index );
859 MXLAPACK_INT found{ 0 };
860 const MXLAPACK_INT lapackRank =
static_cast<MXLAPACK_INT
>( rank );
861 const MXLAPACK_INT info =
862 callSyevr<realT>(
'V',
866 workspace.m_storage->m_psdCore.data(),
874 workspace.m_storage->m_eigenvalues.data(),
875 workspace.m_storage->m_eigenvectors.data(),
877 workspace.m_storage->m_support.data(),
878 workspace.m_storage->m_syevrWork.data(),
879 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_syevrWork.size() ),
880 workspace.m_storage->m_syevrIWork.data(),
881 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_syevrIWork.size() ) );
882 if( info != 0 || found != lapackRank )
886 if( !allFinite( workspace.m_storage->m_eigenvalues ) || !allFinite( workspace.m_storage->m_eigenvectors ) )
890 if( !validAscendingSpectrum( workspace.m_storage->m_eigenvalues ) )
895 result.m_storage->m_minimumPSDValue = workspace.m_storage->m_eigenvalues( 0 );
896 const realT tolerance =
897 psdTolerance<realT>( rank, realT( 1 ) +
static_cast<realT
>( scaledDeleted.squaredNorm() ) );
898 if( result.m_storage->m_minimumPSDValue < -tolerance )
903 result.m_storage->m_clampedEigenvalues = 0;
904 for( Eigen::Index index = 0; index < rank; ++index )
906 if( workspace.m_storage->m_eigenvalues( index ) < realT( 0 ) )
908 workspace.m_storage->m_eigenvalues( index ) = realT( 0 );
909 ++result.m_storage->m_clampedEigenvalues;
913 for( Eigen::Index output = 0; output < rank; ++output )
915 const Eigen::Index source = rank - 1 - output;
916 if( !rescaleValue( result.m_storage->m_singularValues( output ),
917 result.m_storage->m_squaredSingularValues( output ),
918 workspace.m_storage->m_eigenvalues( source ),
923 if( output < outputRank )
925 result.m_storage->m_rotation.matrix().col( output ) =
926 workspace.m_storage->m_eigenvectors.matrix().col( source );
931 result.m_storage->m_lapackInfo = 0;
934 return result.m_storage->m_status;
941 Eigen::Index outputRank,
942 workspaceT &workspace )
944 if( !validCoreInputs( singularValues, deletedRows, outputRank ) )
948 if( deletedRows.rows() == 0 )
960 loadDeletedRows( workspace, deletedRows );
961 return leadingLoaded( result, singularValues, deletedRows.rows(), outputRank, workspace );
967 Eigen::Index outputRank,
968 workspaceT &workspace )
971 auto &stored = *workspace.m_storage;
972 const realT scale = normalizeSingularValues( workspace, singularValues );
973 if( scale == realT( 0 ) )
978 realT updateNorm{ 0 };
979 realT maximumPole{ 0 };
980 for( Eigen::Index index = 0; index < rank; ++index )
982 const realT normalized = stored.m_normalizedSingularValues( index );
983 const realT update = normalized * stored.m_deletedRows( 0, index );
984 const realT pole = -( normalized * normalized );
989 stored.m_scaledDeletedTranspose( index, 0 ) = update;
990 stored.m_secularPoles( index ) = pole;
991 updateNorm = std::hypot( updateNorm, update );
992 maximumPole = std::max( maximumPole, std::abs( pole ) );
994 if( updateNorm == realT( 0 ) )
998 if( !
math::isFinite( updateNorm ) || updateNorm > std::sqrt( std::numeric_limits<realT>::max() ) )
1003 const realT rho = updateNorm * updateNorm;
1010 realT maximumUpdate{ 0 };
1011 for( Eigen::Index index = 0; index < rank; ++index )
1013 stored.m_secularUpdate( index ) = stored.m_scaledDeletedTranspose( index, 0 ) / updateNorm;
1014 maximumUpdate = std::max( maximumUpdate, std::abs( stored.m_secularUpdate( index ) ) );
1016 const realT deflationTolerance =
1017 realT( 8 ) * std::numeric_limits<realT>::epsilon() * std::max( maximumPole, maximumUpdate );
1018 if( rho * maximumUpdate <= deflationTolerance )
1023 Eigen::Index activeCount{ 0 };
1024 Eigen::Index deflatedCount{ 0 };
1025 Eigen::Index rotationCount{ 0 };
1026 Eigen::Index previousActive{ -1 };
1027 for( Eigen::Index next = 0; next < rank; ++next )
1029 if( rho * std::abs( stored.m_secularUpdate( next ) ) <= deflationTolerance )
1031 stored.m_secularDeflatedIndices[deflatedCount++] = next;
1034 if( previousActive < 0 )
1036 previousActive = next;
1040 const realT previousUpdate = stored.m_secularUpdate( previousActive );
1041 const realT nextUpdate = stored.m_secularUpdate( next );
1042 const realT rotationNorm = std::hypot( nextUpdate, previousUpdate );
1043 const realT cosine = nextUpdate / rotationNorm;
1044 const realT sine = -previousUpdate / rotationNorm;
1045 const realT poleDifference = stored.m_secularPoles( next ) - stored.m_secularPoles( previousActive );
1046 if( std::abs( poleDifference * cosine * sine ) <= deflationTolerance )
1048 stored.m_secularRotationFirst[rotationCount] = previousActive;
1049 stored.m_secularRotationSecond[rotationCount] = next;
1050 stored.m_secularRotationCosine[rotationCount] = cosine;
1051 stored.m_secularRotationSine[rotationCount] = sine;
1054 stored.m_secularUpdate( next ) = rotationNorm;
1055 stored.m_secularUpdate( previousActive ) = realT( 0 );
1056 const realT previousPole = stored.m_secularPoles( previousActive );
1057 const realT nextPole = stored.m_secularPoles( next );
1058 stored.m_secularPoles( previousActive ) = previousPole * cosine * cosine + nextPole * sine * sine;
1059 stored.m_secularPoles( next ) = previousPole * sine * sine + nextPole * cosine * cosine;
1060 stored.m_secularDeflatedIndices[deflatedCount++] = previousActive;
1061 previousActive = next;
1065 stored.m_secularActiveIndices[activeCount++] = previousActive;
1066 previousActive = next;
1069 if( previousActive >= 0 )
1071 stored.m_secularActiveIndices[activeCount++] = previousActive;
1073 if( activeCount <= 0 || activeCount + deflatedCount != rank )
1080 const auto poleIndexLess = [&]( Eigen::Index left, Eigen::Index right )
1082 const realT leftPole = stored.m_secularPoles( left );
1083 const realT rightPole = stored.m_secularPoles( right );
1084 return leftPole < rightPole || ( leftPole == rightPole && left < right );
1086 std::sort( stored.m_secularActiveIndices.begin(),
1087 stored.m_secularActiveIndices.begin() + activeCount,
1089 std::sort( stored.m_secularDeflatedIndices.begin(),
1090 stored.m_secularDeflatedIndices.begin() + deflatedCount,
1093 realT activeUpdateNorm{ 0 };
1094 for( Eigen::Index active = 0; active < activeCount; ++active )
1096 const Eigen::Index source = stored.m_secularActiveIndices[active];
1097 stored.m_secularActivePoles( active ) = stored.m_secularPoles( source );
1098 activeUpdateNorm = std::hypot( activeUpdateNorm, stored.m_secularUpdate( source ) );
1099 if( active > 0 && !( stored.m_secularActivePoles( active - 1 ) < stored.m_secularActivePoles( active ) ) )
1106 if( activeUpdateNorm == realT( 0 ) || !
math::isFinite( activeUpdateNorm ) )
1113 const realT activeRho = rho * activeUpdateNorm * activeUpdateNorm;
1120 if( activeRho == realT( 0 ) )
1126 for( Eigen::Index active = 0; active < activeCount; ++active )
1128 const Eigen::Index source = stored.m_secularActiveIndices[active];
1129 stored.m_secularActiveUpdate( active ) = stored.m_secularUpdate( source ) / activeUpdateNorm;
1132 const MXLAPACK_INT lapackActive =
static_cast<MXLAPACK_INT
>( activeCount );
1133 const MXLAPACK_INT leadingDimension =
static_cast<MXLAPACK_INT
>( rank );
1134 const MXLAPACK_INT info = callLaed9<realT>( stored.m_eigenvalues.data(),
1135 stored.m_psdCore.data(),
1136 stored.m_eigenvectors.data(),
1140 stored.m_secularActivePoles.data(),
1141 stored.m_secularActiveUpdate.data() );
1146 for( Eigen::Index active = 0; active < activeCount; ++active )
1152 for( Eigen::Index row = 0; row < activeCount; ++row )
1160 if( !validAscendingSpectrum( stored.m_eigenvalues.head( activeCount ) ) )
1165 const realT secularTolerance = realT( 64 ) * std::numeric_limits<realT>::epsilon() *
1166 static_cast<realT
>( std::max<Eigen::Index>( 1, rank ) ) *
1167 ( realT( 1 ) + activeRho );
1168 for( Eigen::Index active = 0; active < activeCount; ++active )
1170 const realT lower = stored.m_secularActivePoles( active );
1171 const realT upper = active + 1 < activeCount ? stored.m_secularActivePoles( active + 1 )
1172 : stored.m_secularActivePoles( active ) + activeRho;
1173 if( stored.m_eigenvalues( active ) < lower - secularTolerance ||
1174 stored.m_eigenvalues( active ) > upper + secularTolerance )
1178 stored.m_secularCandidateValues( active ) = stored.m_eigenvalues( active );
1180 for( Eigen::Index deflated = 0; deflated < deflatedCount; ++deflated )
1182 stored.m_secularCandidateValues( activeCount + deflated ) =
1183 stored.m_secularPoles( stored.m_secularDeflatedIndices[deflated] );
1186 Eigen::Index activePosition{ 0 };
1187 Eigen::Index deflatedPosition{ 0 };
1188 Eigen::Index mergedPosition{ 0 };
1189 while( activePosition < activeCount || deflatedPosition < deflatedCount )
1191 const bool takeActive = deflatedPosition >= deflatedCount ||
1192 ( activePosition < activeCount &&
1193 stored.m_secularCandidateValues( activePosition ) <=
1194 stored.m_secularCandidateValues( activeCount + deflatedPosition ) );
1195 stored.m_secularOrder[mergedPosition++] = takeActive ? activePosition++ : activeCount + deflatedPosition++;
1197 if( mergedPosition != rank )
1204 result.m_storage->m_minimumPSDValue = -stored.m_secularCandidateValues( stored.m_secularOrder[rank - 1] );
1205 const realT tolerance = psdTolerance<realT>( rank, realT( 1 ) + rho );
1206 if( result.m_storage->m_minimumPSDValue < -tolerance )
1211 result.m_storage->m_clampedEigenvalues = 0;
1212 const realT vectorTolerance = realT( 256 ) * std::numeric_limits<realT>::epsilon() *
1213 static_cast<realT
>( std::max<Eigen::Index>( 1, rank ) );
1214 const realT residualTolerance = vectorTolerance * ( realT( 1 ) + rho );
1215 for( Eigen::Index output = 0; output < rank; ++output )
1217 const Eigen::Index candidate = stored.m_secularOrder[output];
1218 realT normalizedSquaredValue = -stored.m_secularCandidateValues( candidate );
1219 if( normalizedSquaredValue < realT( 0 ) )
1221 normalizedSquaredValue = realT( 0 );
1222 ++result.m_storage->m_clampedEigenvalues;
1224 if( !rescaleValue( result.m_storage->m_singularValues( output ),
1225 result.m_storage->m_squaredSingularValues( output ),
1226 normalizedSquaredValue,
1231 if( output >= outputRank )
1236 stored.m_secularVector.setZero();
1237 if( candidate < activeCount )
1239 for( Eigen::Index active = 0; active < activeCount; ++active )
1241 stored.m_secularVector( stored.m_secularActiveIndices[active] ) =
1242 stored.m_eigenvectors( active, candidate );
1247 const Eigen::Index deflated = candidate - activeCount;
1248 stored.m_secularVector( stored.m_secularDeflatedIndices[deflated] ) = realT( 1 );
1250 for( Eigen::Index rotation = rotationCount; rotation-- > 0; )
1252 const Eigen::Index first = stored.m_secularRotationFirst[rotation];
1253 const Eigen::Index second = stored.m_secularRotationSecond[rotation];
1254 const realT cosine = stored.m_secularRotationCosine[rotation];
1255 const realT sine = stored.m_secularRotationSine[rotation];
1256 const realT firstValue = stored.m_secularVector( first );
1257 const realT secondValue = stored.m_secularVector( second );
1258 stored.m_secularVector( first ) = cosine * firstValue - sine * secondValue;
1259 stored.m_secularVector( second ) = sine * firstValue + cosine * secondValue;
1262 realT vectorNorm{ 0 };
1263 Eigen::Index signIndex{ 0 };
1264 realT signMagnitude{ 0 };
1265 for( Eigen::Index row = 0; row < rank; ++row )
1267 const realT value = stored.m_secularVector( row );
1274 vectorNorm = std::hypot( vectorNorm, value );
1275 if( std::abs( value ) > signMagnitude )
1277 signMagnitude = std::abs( value );
1281 if( !
math::isFinite( vectorNorm ) || std::abs( vectorNorm - realT( 1 ) ) > vectorTolerance )
1285 if( stored.m_secularVector( signIndex ) < realT( 0 ) )
1287 stored.m_secularVector = -stored.m_secularVector;
1290 realT updateDot{ 0 };
1291 for( Eigen::Index row = 0; row < rank; ++row )
1293 updateDot += stored.m_scaledDeletedTranspose( row, 0 ) * stored.m_secularVector( row );
1295 realT residualNorm{ 0 };
1296 for( Eigen::Index row = 0; row < rank; ++row )
1298 const realT normalized = stored.m_normalizedSingularValues( row );
1299 const realT residual = normalized * normalized * stored.m_secularVector( row ) -
1300 stored.m_scaledDeletedTranspose( row, 0 ) * updateDot -
1301 normalizedSquaredValue * stored.m_secularVector( row );
1302 residualNorm = std::hypot( residualNorm, residual );
1304 if( !
math::isFinite( residualNorm ) || residualNorm > residualTolerance )
1308 result.m_storage->m_rotation.matrix().col( output ) = stored.m_secularVector.matrix();
1312 result.m_storage->m_lapackInfo = 0;
1315 return result.m_storage->m_status;
1322 Eigen::Index outputRank,
1323 workspaceT &workspace )
1325 if( !validCoreInputs( singularValues, deletedRows, outputRank ) )
1329 if( deletedRows.rows() == 0 )
1333 if( deletedRows.rows() != 1 )
1345 loadDeletedRows( workspace, deletedRows );
1346 return rankOneLoaded( result, singularValues, outputRank, workspace );
1352 Eigen::Index deletedCount,
1353 Eigen::Index outputRank,
1354 workspaceT &workspace )
1357 const realT scale = normalizeSingularValues( workspace, singularValues );
1358 if( scale == realT( 0 ) )
1363 const auto factorRows = workspace.m_storage->m_deletedRows.matrix().topRows( deletedCount );
1364 auto complement = workspace.m_storage->m_psdCore.matrix().topLeftCorner( deletedCount, deletedCount );
1365 complement.setIdentity();
1366 complement.noalias() -= factorRows * factorRows.transpose();
1367 MXLAPACK_INT found{ 0 };
1368 const MXLAPACK_INT lapackComplement =
static_cast<MXLAPACK_INT
>( deletedCount );
1369 MXLAPACK_INT info = callSyevr<realT>(
'V',
1373 workspace.m_storage->m_psdCore.data(),
1374 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_psdCore.rows() ),
1381 workspace.m_storage->m_eigenvalues.data(),
1382 workspace.m_storage->m_eigenvectors.data(),
1383 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_eigenvectors.rows() ),
1384 workspace.m_storage->m_support.data(),
1385 workspace.m_storage->m_syevrWork.data(),
1386 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_syevrWork.size() ),
1387 workspace.m_storage->m_syevrIWork.data(),
1388 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_syevrIWork.size() ) );
1389 if( info != 0 || found != lapackComplement )
1393 for( Eigen::Index index = 0; index < deletedCount; ++index )
1395 if( !
math::isFinite( workspace.m_storage->m_eigenvalues( index ) ) )
1399 for( Eigen::Index row = 0; row < deletedCount; ++row )
1401 if( !
math::isFinite( workspace.m_storage->m_eigenvectors( row, index ) ) )
1407 if( !validAscendingSpectrum( workspace.m_storage->m_eigenvalues.head( deletedCount ) ) )
1412 result.m_storage->m_minimumPSDValue = workspace.m_storage->m_eigenvalues( 0 );
1413 const realT tolerance =
1414 psdTolerance<realT>( deletedCount, realT( 1 ) +
static_cast<realT
>( factorRows.squaredNorm() ) );
1415 if( result.m_storage->m_minimumPSDValue < -tolerance )
1420 result.m_storage->m_clampedEigenvalues = 0;
1421 for( Eigen::Index index = 0; index < deletedCount; ++index )
1423 if( workspace.m_storage->m_eigenvalues( index ) < realT( 0 ) )
1425 workspace.m_storage->m_eigenvalues( index ) = realT( 0 );
1426 ++result.m_storage->m_clampedEigenvalues;
1430 auto complementRoot =
1431 workspace.m_storage->m_complementRoot.matrix().topLeftCorner( deletedCount, deletedCount );
1432 for( Eigen::Index row = 0; row < deletedCount; ++row )
1434 const realT
root = std::sqrt( workspace.m_storage->m_eigenvalues( row ) );
1435 for( Eigen::Index column = 0; column < deletedCount; ++column )
1437 complementRoot( row, column ) =
root * workspace.m_storage->m_eigenvectors( column, row );
1441 auto topCore = workspace.m_storage->m_stableCore.matrix().topRows( rank );
1442 topCore.setIdentity();
1443 topCore.noalias() -= factorRows.transpose() * factorRows;
1444 workspace.m_storage->m_stableCore.matrix().middleRows( rank, deletedCount ).noalias() =
1445 complementRoot * factorRows;
1446 for( Eigen::Index column = 0; column < rank; ++column )
1448 workspace.m_storage->m_stableCore.matrix().col( column ).head( rank + deletedCount ) *=
1449 workspace.m_storage->m_normalizedSingularValues( column );
1452 realT unusedLeft{ 0 };
1453 const MXLAPACK_INT coreRows =
static_cast<MXLAPACK_INT
>( rank + deletedCount );
1454 const MXLAPACK_INT lapackRank =
static_cast<MXLAPACK_INT
>( rank );
1455 info = callGesvd<realT>(
'N',
1459 workspace.m_storage->m_stableCore.data(),
1460 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_stableCore.rows() ),
1461 workspace.m_storage->m_stableSingularValues.data(),
1464 workspace.m_storage->m_rightTranspose.data(),
1466 workspace.m_storage->m_gesvdWork.data(),
1467 static_cast<MXLAPACK_INT
>( workspace.m_storage->m_gesvdWork.size() ) );
1472 if( !allFinite( workspace.m_storage->m_stableSingularValues ) ||
1473 !allFinite( workspace.m_storage->m_rightTranspose ) )
1477 if( !validDescendingSpectrum( workspace.m_storage->m_stableSingularValues ) )
1482 for( Eigen::Index output = 0; output < rank; ++output )
1484 const realT normalizedValue = workspace.m_storage->m_stableSingularValues( output );
1485 if( !rescaleSingularValue( result.m_storage->m_singularValues( output ),
1486 result.m_storage->m_squaredSingularValues( output ),
1492 if( output < outputRank )
1494 result.m_storage->m_rotation.matrix().col( output ) =
1495 workspace.m_storage->m_rightTranspose.matrix().row( output ).transpose();
1500 result.m_storage->m_lapackInfo = 0;
1503 return result.m_storage->m_status;
1510 Eigen::Index outputRank,
1511 workspaceT &workspace )
1513 if( !validCoreInputs( singularValues, deletedRows, outputRank ) )
1517 if( deletedRows.rows() == 0 )
1529 loadDeletedRows( workspace, deletedRows );
1530 return stableLoaded( result, singularValues, deletedRows.rows(), outputRank, workspace );
1537 svdDeletionConstIndexViewV2 indices,
1538 Eigen::Index outputRank,
1539 workspaceT &workspace,
1543 if( !validSingularInputs( singularValues, outputRank ) || !validBackend( backend ) || factor.rows() <= 0 ||
1544 factor.rows() < rank || factor.cols() != rank || !validLapackDimension( factor.rows() ) ||
1545 !validAbiIndexView( indices ) || indices.size >=
static_cast<std::int64_t
>( factor.rows() ) ||
1546 indices.size >
static_cast<std::int64_t
>( std::numeric_limits<MXLAPACK_INT>::max() - rank ) )
1551 Eigen::Index previous = -1;
1552 for( std::int64_t offset = 0; offset < indices.size; ++offset )
1554 const std::int64_t rawIndex = abiIndexAt( indices, offset );
1555 if( !validAbiDimension( rawIndex ) )
1559 const Eigen::Index index =
static_cast<Eigen::Index
>( rawIndex );
1560 if( index < 0 || index >= factor.rows() || index <= previous )
1567 if( indices.size == 0 )
1569 return identity( result, singularValues, outputRank, backend );
1577 const Eigen::Index deletedCount =
static_cast<Eigen::Index
>( indices.size );
1578 const svdDeletionStatus status = prepare( result, workspace, rank, deletedCount, outputRank, backend );
1584 for( Eigen::Index row = 0; row < deletedCount; ++row )
1586 const Eigen::Index source =
static_cast<Eigen::Index
>( abiIndexAt( indices, row ) );
1587 for( Eigen::Index column = 0; column < rank; ++column )
1589 const realT value = factor( source, column );
1594 workspace.m_storage->m_deletedRows( row, column ) = value;
1601 return leadingLoaded( result, singularValues, deletedCount, outputRank, workspace );
1603 return stableLoaded( result, singularValues, deletedCount, outputRank, workspace );
1605 return rankOneLoaded( result, singularValues, outputRank, workspace );
1620 return "leadingCovariance";
1622 return "stableCore";
1624 return "rankOneSecular";
1634 return "notComputed";
1638 return "successWithClamping";
1640 return "invalidInput";
1642 return "allocationFailure";
1644 return "workspaceQueryFailure";
1646 return "solverFailure";
1648 return "nonFiniteOutput";
1650 return "invalidSolverOutput";
1652 return "rescalingOverflow";
1654 return "nonPositiveSemidefinite";
1656 return "factorNotOrthonormal";
1658 return "unsupportedDeletionCount";
1668template <
typename realT,
typename abiT>
1673template <
typename realT,
typename abiT>
1676template <
typename realT,
typename abiT>
1679template <
typename realT,
typename abiT>
1683template <
typename realT,
typename abiT>
1684bool svdDeletionResult<realT, abiT>::ensureStorage() noexcept
1693 detail::callOperationHook<realT>( detail::svdDeletionTestOperation::prepareResult );
1694 m_storage = std::make_unique<storage>();
1696 catch(
const std::bad_alloc & )
1700 catch(
const std::length_error & )
1707template <
typename realT,
typename abiT>
1708svdDeletionStatus svdDeletionResult<realT, abiT>::prepareAbiV2( std::int64_t abiBaseRank, std::int64_t abiOutputRank )
1710 if( !ensureStorage() )
1714 auto &stored = *m_storage;
1716 if( !detail::validAbiDimension( abiBaseRank ) || !detail::validAbiDimension( abiOutputRank ) )
1719 return stored.m_status;
1721 const Eigen::Index baseRank =
static_cast<Eigen::Index
>( abiBaseRank );
1722 const Eigen::Index outputRank =
static_cast<Eigen::Index
>( abiOutputRank );
1724 if( baseRank <= 0 || outputRank <= 0 || outputRank > baseRank || !detail::validLapackDimension( baseRank ) ||
1725 !detail::validArrayShape<realT>( baseRank, outputRank ) || !detail::validArrayShape<realT>( outputRank, 1 ) )
1728 return stored.m_status;
1733 if( stored.m_baseRank != baseRank || stored.m_maximumOutputRank < outputRank )
1735 detail::callOperationHook<realT>( detail::svdDeletionTestOperation::prepareResult );
1736 stored.m_singularValues.resize( baseRank );
1737 stored.m_squaredSingularValues.resize( baseRank );
1738 stored.m_rotation.resize( baseRank, outputRank );
1739 stored.m_maximumOutputRank = outputRank;
1742 catch(
const std::bad_alloc & )
1745 return stored.m_status;
1747 catch(
const std::length_error & )
1750 return stored.m_status;
1753 stored.m_baseRank = baseRank;
1754 stored.m_outputRank = outputRank;
1756 stored.m_lapackInfo = 0;
1757 stored.m_clampedEigenvalues = 0;
1758 stored.m_minimumPSDValue = realT( 0 );
1762template <
typename realT,
typename abiT>
1768template <
typename realT,
typename abiT>
1774template <
typename realT,
typename abiT>
1779 static_cast<std::int64_t
>( m_storage->m_singularValues.size() ) }
1780 : svdDeletionConstVectorViewV2<realT>{};
1783template <
typename realT,
typename abiT>
1787 static_cast<std::int64_t
>(
1788 m_storage->m_squaredSingularValues.size() ) }
1792template <
typename realT,
typename abiT>
1799 return { m_storage->m_rotation.data(),
1800 static_cast<std::int64_t
>( m_storage->m_rotation.rows() ),
1801 static_cast<std::int64_t
>( m_storage->m_outputRank ),
1802 static_cast<std::int64_t
>( m_storage->m_rotation.outerStride() ) };
1805template <
typename realT,
typename abiT>
1808 return m_storage ? m_storage->m_baseRank : 0;
1811template <
typename realT,
typename abiT>
1814 return m_storage ? m_storage->m_outputRank : 0;
1817template <
typename realT,
typename abiT>
1820 return m_storage ? m_storage->m_maximumOutputRank : 0;
1823template <
typename realT,
typename abiT>
1826 return m_storage ? m_storage->m_clampedEigenvalues : 0;
1829template <
typename realT,
typename abiT>
1832 return m_storage ? m_storage->m_minimumPSDValue : realT( 0 );
1835template <
typename realT,
typename abiT>
1838 return m_storage ? m_storage->m_lapackInfo : 0;
1841template <
typename realT,
typename abiT>
1846template <
typename realT,
typename abiT>
1851template <
typename realT,
typename abiT>
1854template <
typename realT,
typename abiT>
1858template <
typename realT,
typename abiT>
1859bool svdDeletionWorkspace<realT, abiT>::ensureStorage() noexcept
1868 detail::callOperationHook<realT>( detail::svdDeletionTestOperation::prepareWorkspace );
1869 m_storage = std::make_unique<storage>();
1871 catch(
const std::bad_alloc & )
1875 catch(
const std::length_error & )
1882template <
typename realT,
typename abiT>
1883svdDeletionStatus svdDeletionWorkspace<realT, abiT>::prepareAbiV2( std::int64_t abiBaseRank,
1884 std::int64_t abiMaximumDeleted,
1887 if( !ensureStorage() )
1891 auto &stored = *m_storage;
1892 stored.m_lapackInfo = 0;
1894 if( !detail::validAbiDimension( abiBaseRank ) || !detail::validAbiDimension( abiMaximumDeleted ) )
1898 const Eigen::Index baseRank =
static_cast<Eigen::Index
>( abiBaseRank );
1899 const Eigen::Index maximumDeleted =
static_cast<Eigen::Index
>( abiMaximumDeleted );
1900 if( baseRank <= 0 || maximumDeleted < 0 || !detail::svdDeletionImplementation<realT>::validBackend( backend ) ||
1901 !detail::validLapackDimension( baseRank ) || !detail::validLapackDimension( maximumDeleted ) ||
1902 maximumDeleted > std::numeric_limits<MXLAPACK_INT>::max() - baseRank )
1910 if( stored.m_prepared && stored.m_baseRank == baseRank && stored.m_maximumDeleted >= maximumDeleted &&
1911 stored.m_backend == backend )
1918 if( syevrSize > std::numeric_limits<Eigen::Index>::max() / 2 )
1922 const Eigen::Index supportSize = syevrSize > 0 ? 2 * std::max<Eigen::Index>( 1, syevrSize ) : 0;
1923 Eigen::Index minimumGesvdWork{ 0 };
1925 detail::validArrayShape<realT>( maximumDeleted, baseRank ) && detail::validArrayShape<realT>( baseRank, 1 ) &&
1926 detail::validArrayShape<realT>( eigenSize, eigenSize ) && detail::validArrayShape<realT>( eigenSize, 1 ) &&
1927 detail::validVectorSize<MXLAPACK_INT>( supportSize );
1930 validShapes = validShapes && detail::validArrayShape<realT>( baseRank, maximumDeleted );
1934 validShapes = validShapes && detail::validArrayShape<realT>( maximumDeleted, maximumDeleted ) &&
1935 detail::validArrayShape<realT>( baseRank + maximumDeleted, baseRank ) &&
1936 detail::validArrayShape<realT>( baseRank, baseRank );
1937 const unsigned long long rank =
static_cast<unsigned long long>( baseRank );
1938 const unsigned long long deleted =
static_cast<unsigned long long>( maximumDeleted );
1939 const unsigned long long lapackMaximum =
1940 static_cast<unsigned long long>( std::numeric_limits<MXLAPACK_INT>::max() );
1941 validShapes = validShapes && rank <= lapackMaximum / 5 && rank <= ( lapackMaximum - deleted ) / 4;
1944 minimumGesvdWork = std::max<Eigen::Index>( 1, std::max( 5 * baseRank, 4 * baseRank + maximumDeleted ) );
1945 validShapes = detail::validVectorSize<realT>( minimumGesvdWork );
1950 validShapes = validShapes && detail::validArrayShape<realT>( baseRank, baseRank ) &&
1951 detail::validVectorSize<realT>( baseRank ) && detail::validVectorSize<Eigen::Index>( baseRank );
1960 detail::callOperationHook<realT>( detail::svdDeletionTestOperation::prepareWorkspace );
1962 stored.m_deletedRows.resize( maximumDeleted, baseRank );
1963 stored.m_normalizedSingularValues.resize( baseRank );
1964 stored.m_scaledDeletedTranspose.resize( 0, 0 );
1965 stored.m_complementRoot.resize( 0, 0 );
1966 stored.m_stableCore.resize( 0, 0 );
1967 stored.m_rightTranspose.resize( 0, 0 );
1968 stored.m_stableSingularValues.resize( 0 );
1969 stored.m_secularPoles.resize( 0 );
1970 stored.m_secularUpdate.resize( 0 );
1971 stored.m_secularActivePoles.resize( 0 );
1972 stored.m_secularActiveUpdate.resize( 0 );
1973 stored.m_secularCandidateValues.resize( 0 );
1974 stored.m_secularVector.resize( 0 );
1975 stored.m_secularActiveIndices.clear();
1976 stored.m_secularDeflatedIndices.clear();
1977 stored.m_secularOrder.clear();
1978 stored.m_secularRotationFirst.clear();
1979 stored.m_secularRotationSecond.clear();
1980 stored.m_secularRotationCosine.clear();
1981 stored.m_secularRotationSine.clear();
1985 stored.m_scaledDeletedTranspose.resize( baseRank, maximumDeleted );
1986 stored.m_psdCore.resize( baseRank, baseRank );
1990 stored.m_psdCore.resize( maximumDeleted, maximumDeleted );
1991 stored.m_complementRoot.resize( maximumDeleted, maximumDeleted );
1992 stored.m_stableCore.resize( baseRank + maximumDeleted, baseRank );
1993 stored.m_rightTranspose.resize( baseRank, baseRank );
1994 stored.m_stableSingularValues.resize( baseRank );
1998 stored.m_scaledDeletedTranspose.resize( baseRank, 1 );
1999 stored.m_psdCore.resize( baseRank, baseRank );
2000 stored.m_secularPoles.resize( baseRank );
2001 stored.m_secularUpdate.resize( baseRank );
2002 stored.m_secularActivePoles.resize( baseRank );
2003 stored.m_secularActiveUpdate.resize( baseRank );
2004 stored.m_secularCandidateValues.resize( baseRank );
2005 stored.m_secularVector.resize( baseRank );
2006 stored.m_secularActiveIndices.resize(
static_cast<std::size_t
>( baseRank ) );
2007 stored.m_secularDeflatedIndices.resize(
static_cast<std::size_t
>( baseRank ) );
2008 stored.m_secularOrder.resize(
static_cast<std::size_t
>( baseRank ) );
2009 stored.m_secularRotationFirst.resize(
static_cast<std::size_t
>( baseRank ) );
2010 stored.m_secularRotationSecond.resize(
static_cast<std::size_t
>( baseRank ) );
2011 stored.m_secularRotationCosine.resize(
static_cast<std::size_t
>( baseRank ) );
2012 stored.m_secularRotationSine.resize(
static_cast<std::size_t
>( baseRank ) );
2015 stored.m_eigenvectors.resize( eigenSize, eigenSize );
2016 stored.m_eigenvalues.resize( eigenSize );
2017 stored.m_support.resize(
static_cast<std::size_t
>( supportSize ) );
2018 stored.m_syevrWork.clear();
2019 stored.m_syevrIWork.clear();
2020 stored.m_gesvdWork.clear();
2024 stored.m_psdCore.matrix().setIdentity();
2025 stored.m_eigenvectors.setZero();
2026 stored.m_eigenvalues.setZero();
2027 realT workQuery{ 0 };
2028 MXLAPACK_INT integerWorkQuery{ 0 };
2029 MXLAPACK_INT found{ 0 };
2030 const MXLAPACK_INT lapackEigenSize =
static_cast<MXLAPACK_INT
>( syevrSize );
2031 const MXLAPACK_INT info = detail::callSyevr<realT>(
'V',
2035 stored.m_psdCore.data(),
2043 stored.m_eigenvalues.data(),
2044 stored.m_eigenvectors.data(),
2046 stored.m_support.data(),
2051 MXLAPACK_INT realWorkSize{ 0 };
2052 if( info != 0 || !detail::querySize( realWorkSize, workQuery ) || integerWorkQuery < 1 ||
2053 !detail::validVectorSize<realT>( realWorkSize ) ||
2054 !detail::validVectorSize<MXLAPACK_INT>( integerWorkQuery ) )
2057 stored.m_lapackInfo = info;
2060 stored.m_syevrWork.resize(
static_cast<std::size_t
>( realWorkSize ) );
2061 stored.m_syevrIWork.resize(
static_cast<std::size_t
>( integerWorkQuery ) );
2066 stored.m_stableCore.setZero();
2067 stored.m_rightTranspose.setZero();
2068 stored.m_stableSingularValues.setZero();
2069 realT unusedLeft{ 0 };
2070 realT workQuery{ 0 };
2071 const MXLAPACK_INT rows =
static_cast<MXLAPACK_INT
>( baseRank + maximumDeleted );
2072 const MXLAPACK_INT cols =
static_cast<MXLAPACK_INT
>( baseRank );
2073 const MXLAPACK_INT info = detail::callGesvd<realT>(
'N',
2077 stored.m_stableCore.data(),
2079 stored.m_stableSingularValues.data(),
2082 stored.m_rightTranspose.data(),
2086 MXLAPACK_INT workSize{ 0 };
2087 if( info != 0 || !detail::querySize( workSize, workQuery ) )
2090 stored.m_lapackInfo = info;
2093 workSize = std::max( workSize,
static_cast<MXLAPACK_INT
>( minimumGesvdWork ) );
2094 if( !detail::validVectorSize<realT>( workSize ) )
2099 stored.m_gesvdWork.resize(
static_cast<std::size_t
>( workSize ) );
2102 catch(
const std::bad_alloc & )
2107 catch(
const std::length_error & )
2113 stored.m_baseRank = baseRank;
2114 stored.m_maximumDeleted = maximumDeleted;
2115 stored.m_backend = backend;
2116 stored.m_prepared =
true;
2120template <
typename realT,
typename abiT>
2128 m_storage->m_deletedRows.resize( 0, 0 );
2129 m_storage->m_normalizedSingularValues.resize( 0 );
2130 m_storage->m_scaledDeletedTranspose.resize( 0, 0 );
2131 m_storage->m_psdCore.resize( 0, 0 );
2132 m_storage->m_eigenvectors.resize( 0, 0 );
2133 m_storage->m_eigenvalues.resize( 0 );
2134 m_storage->m_complementRoot.resize( 0, 0 );
2135 m_storage->m_stableCore.resize( 0, 0 );
2136 m_storage->m_rightTranspose.resize( 0, 0 );
2137 m_storage->m_stableSingularValues.resize( 0 );
2138 m_storage->m_secularPoles.resize( 0 );
2139 m_storage->m_secularUpdate.resize( 0 );
2140 m_storage->m_secularActivePoles.resize( 0 );
2141 m_storage->m_secularActiveUpdate.resize( 0 );
2142 m_storage->m_secularCandidateValues.resize( 0 );
2143 m_storage->m_secularVector.resize( 0 );
2144 m_storage->m_secularActiveIndices.clear();
2145 m_storage->m_secularDeflatedIndices.clear();
2146 m_storage->m_secularOrder.clear();
2147 m_storage->m_secularRotationFirst.clear();
2148 m_storage->m_secularRotationSecond.clear();
2149 m_storage->m_secularRotationCosine.clear();
2150 m_storage->m_secularRotationSine.clear();
2151 m_storage->m_support.clear();
2152 m_storage->m_syevrWork.clear();
2153 m_storage->m_syevrIWork.clear();
2154 m_storage->m_gesvdWork.clear();
2155 m_storage->m_baseRank = 0;
2156 m_storage->m_maximumDeleted = 0;
2158 m_storage->m_prepared =
false;
2159 m_storage->m_lapackInfo = 0;
2162template <
typename realT,
typename abiT>
2165 return m_storage && m_storage->m_prepared;
2168template <
typename realT,
typename abiT>
2171 return m_storage ? m_storage->m_baseRank : 0;
2174template <
typename realT,
typename abiT>
2177 return m_storage ? m_storage->m_maximumDeleted : 0;
2180template <
typename realT,
typename abiT>
2186template <
typename realT,
typename abiT>
2189 return m_storage ? m_storage->m_lapackInfo : 0;
2195template <
typename realT>
2198 if( factor.rows() < factor.cols() || factor.cols() <= 0 || !detail::validLapackDimension( factor.rows() ) ||
2199 !detail::validLapackDimension( factor.cols() ) || !
math::isFinite( tolerance ) || tolerance < realT( 0 ) ||
2200 !detail::validArrayShape<realT>( factor.cols(), factor.cols() ) || !detail::allFinite( factor ) )
2207 detail::callOperationHook<realT>( detail::svdDeletionTestOperation::validateFactor );
2208 svdDeletionStorageMatrix<realT> gram( factor.cols(), factor.cols() );
2209 gram.matrix().noalias() = factor.matrix().transpose() * factor.matrix();
2210 if( !detail::allFinite( gram ) )
2214 for( Eigen::Index index = 0; index < gram.rows(); ++index )
2216 gram( index, index ) -= realT( 1 );
2219 if( tolerance == realT( 0 ) )
2221 tolerance = realT( 64 ) * std::numeric_limits<realT>::epsilon() *
2222 static_cast<realT
>( std::max( factor.rows(), factor.cols() ) );
2224 if( gram.abs().maxCoeff() > tolerance )
2229 catch(
const std::bad_alloc & )
2233 catch(
const std::length_error & )
2245 if( !detail::validAbiMatrixView( factor ) )
2249 return validateSvdDeletionFactorImpl<float>( detail::mapAbiMatrix( factor ), tolerance );
2255 if( !detail::validAbiMatrixView( factor ) )
2259 return validateSvdDeletionFactorImpl<double>( detail::mapAbiMatrix( factor ), tolerance );
2262template <
typename realT>
2266 std::int64_t outputRank,
2269 if( !detail::validAbiVectorView( singularValues ) || !detail::validAbiMatrixView( deletedRows ) ||
2270 !detail::validAbiDimension( outputRank ) )
2274 return detail::svdDeletionImplementation<realT>::leading( result,
2275 detail::mapAbiVector( singularValues ),
2276 detail::mapAbiMatrix( deletedRows ),
2277 static_cast<Eigen::Index
>( outputRank ),
2281template <
typename realT>
2285 std::int64_t outputRank,
2288 if( !detail::validAbiVectorView( singularValues ) || !detail::validAbiMatrixView( deletedRows ) ||
2289 !detail::validAbiDimension( outputRank ) )
2293 return detail::svdDeletionImplementation<realT>::stable( result,
2294 detail::mapAbiVector( singularValues ),
2295 detail::mapAbiMatrix( deletedRows ),
2296 static_cast<Eigen::Index
>( outputRank ),
2300template <
typename realT>
2304 std::int64_t outputRank,
2308 if( !detail::validAbiVectorView( singularValues ) || !detail::validAbiMatrixView( deletedRows ) ||
2309 !detail::validAbiDimension( outputRank ) || !detail::svdDeletionImplementation<realT>::validBackend( backend ) )
2316 return detail::svdDeletionImplementation<realT>::leading( result,
2317 detail::mapAbiVector( singularValues ),
2318 detail::mapAbiMatrix( deletedRows ),
2319 static_cast<Eigen::Index
>( outputRank ),
2322 return detail::svdDeletionImplementation<realT>::stable( result,
2323 detail::mapAbiVector( singularValues ),
2324 detail::mapAbiMatrix( deletedRows ),
2325 static_cast<Eigen::Index
>( outputRank ),
2328 return detail::svdDeletionImplementation<realT>::rankOne( result,
2329 detail::mapAbiVector( singularValues ),
2330 detail::mapAbiMatrix( deletedRows ),
2331 static_cast<Eigen::Index
>( outputRank ),
2339template <
typename realT>
2344 std::int64_t outputRank,
2348 if( !detail::validAbiVectorView( singularValues ) || !detail::validAbiMatrixView( leftFactor ) ||
2349 !detail::validAbiIndexView( deletedIndices ) || !detail::validAbiDimension( outputRank ) )
2353 return detail::svdDeletionImplementation<realT>::remove( result,
2354 detail::mapAbiVector( singularValues ),
2355 detail::mapAbiMatrix( leftFactor ),
2357 static_cast<Eigen::Index
>( outputRank ),
2362template <
typename realT>
2367 std::int64_t outputRank,
2371 if( !detail::validAbiVectorView( singularValues ) || !detail::validAbiMatrixView( rightFactor ) ||
2372 !detail::validAbiIndexView( deletedIndices ) || !detail::validAbiDimension( outputRank ) )
2376 return detail::svdDeletionImplementation<realT>::remove( result,
2377 detail::mapAbiVector( singularValues ),
2378 detail::mapAbiMatrix( rightFactor ),
2380 static_cast<Eigen::Index
>( outputRank ),
2455template detail::svdDeletionTestHooks<float> &detail::svdDeletionHooks<float>();
2456template detail::svdDeletionTestHooks<double> &detail::svdDeletionHooks<double>();
std::string singularValues(const std::string &dmName, bool create=false)
The path for the deformable mirror (DM) influence function pseudo-inverse singular values.
Result of deleting rows or columns from a represented thin SVD.
svdDeletionResult & operator=(const svdDeletionResult &other)=delete
Results cannot be copy-assigned across the mxlib ABI boundary.
svdDeletionStatus status() const noexcept
Return the most recent operation status.
realT minimumPSDValue() const noexcept
Return the smallest pre-clamp eigenvalue from the normalized backend PSD validation core.
svdDeletionResult()
Construct an empty result.
std::int64_t clampedEigenvalues() const noexcept
Return the number of roundoff-scale negative eigenvalues clamped to zero.
std::int64_t outputRank() const noexcept
Return the requested published rank.
std::int64_t maximumOutputRank() const noexcept
Return the allocated rotation-column capacity for the current base rank.
~svdDeletionResult()
Release result storage using mxlib's Eigen allocation configuration.
std::int64_t baseRank() const noexcept
Return the base factorization rank for which storage is prepared.
MXLAPACK_INT lapackInfo() const noexcept
Return the underlying LAPACK status from the most recent failed query or solve.
svdDeletionBackend backend() const noexcept
Return the backend that produced the current result.
Reusable, non-shared storage for SVD deletion operations.
bool prepared() const noexcept
Report whether this workspace has completed preparation.
svdDeletionWorkspace()
Construct an empty workspace.
MXLAPACK_INT lapackInfo() const noexcept
Return the underlying LAPACK status from the most recent failed workspace query.
void clear() noexcept
Release all prepared storage and reset dimensions.
std::int64_t baseRank() const noexcept
Return the prepared base rank.
svdDeletionBackend backend() const noexcept
Return the prepared numerical backend.
std::int64_t maximumDeleted() const noexcept
Return the prepared maximum deletion count.
~svdDeletionWorkspace()
Release owned scratch storage.
svdDeletionWorkspace & operator=(const svdDeletionWorkspace &other)=delete
Workspaces cannot be copy-assigned.
Floating-point classification utilities that remain reliable under fast-math optimization.
bool isFinite(realT value)
Test whether a floating-point value is finite, including under finite-math-only optimization.
std::string root(const std::string &basisName, bool create=false)
The root path for basis files.
const char * svdDeletionStatusName(svdDeletionStatus status)
Return a stable text representation of an SVD deletion status.
svdDeletionStatus
Completion status for an SVD deletion operation.
svdDeletionBackend
Numerical backend used to delete rows or columns from thin-SVD factors.
const char * svdDeletionBackendName(svdDeletionBackend backend)
Return a stable text representation of an SVD deletion backend.
bool svdDeletionSucceeded(svdDeletionStatus status) noexcept
Return true when a status represents usable numerical output.
Eigen::Ref< const svdDeletionVector< realT > > svdDeletionConstVectorRef
Non-owning read-only reference to a compatible SVD deletion vector.
Eigen::Ref< const svdDeletionMatrix< realT > > svdDeletionConstMatrixRef
Non-owning read-only reference to a compatible column-major SVD deletion matrix.
@ unsupportedDeletionCount
The selected backend cannot process the requested number of deleted rows.
@ success
The operation completed without numerical clamping.
@ rescalingOverflow
A finite normalized result cannot be represented after restoring input scale.
@ nonFiniteOutput
LAPACK returned a non-finite singular system.
@ notComputed
No operation has published a result.
@ invalidInput
Dimensions, values, indices, or requested output rank are invalid.
@ factorNotOrthonormal
A requested singular-factor validation failed.
@ nonPositiveSemidefinite
A theoretically PSD core has a materially negative eigenvalue.
@ successWithClamping
The operation completed after clamping roundoff-scale negative eigenvalues.
@ allocationFailure
Result or workspace allocation failed.
@ workspaceQueryFailure
LAPACK returned an invalid or failed workspace query.
@ invalidSolverOutput
LAPACK returned a finite spectrum with invalid ordering or sign.
@ solverFailure
LAPACK failed during the numerical solve.
@ stableCore
Complement-preserving small SVD; avoids squaring singular-value conditioning.
@ leadingCovariance
Symmetric leading-spectrum core; fastest when small singular values are not required.
@ rankOneSecular
Structured covariance eigensolve for deleting exactly one singular-factor row.
MXLAPACK_INT syevr(char JOBZ, char RANGE, char UPLO, MXLAPACK_INT N, dataT *A, MXLAPACK_INT LDA, dataT VL, dataT VU, MXLAPACK_INT IL, MXLAPACK_INT IU, dataT ABSTOL, MXLAPACK_INT *M, dataT *W, dataT *Z, MXLAPACK_INT LDZ, MXLAPACK_INT *ISUPPZ, dataT *WORK, MXLAPACK_INT LWORK, MXLAPACK_INT *IWORK, MXLAPACK_INT LIWORK)
Compute selected eigenvalues and, optionally, eigenvectors of a real symmetric matrix.
dataT lamch(char CMACH)
Determine machine parameters.
MXLAPACK_INT gesvd(char JOBU, char JOBVT, MXLAPACK_INT M, MXLAPACK_INT N, dataT *A, MXLAPACK_INT LDA, dataT *S, dataT *U, MXLAPACK_INT LDU, dataT *VT, MXLAPACK_INT LDVT, dataT *WORK, MXLAPACK_INT LWORK)
Compute the singular value decomposition (SVD) of a real matrix.
MXLAPACK_INT laed9(dataT *D, dataT *Q, dataT *S, MXLAPACK_INT K, MXLAPACK_INT KSTART, MXLAPACK_INT KSTOP, MXLAPACK_INT N, MXLAPACK_INT LDQ, dataT RHO, dataT *DLAMDA, dataT *W, MXLAPACK_INT LDS)
Solve selected roots of a diagonal-plus-rank-one secular equation and form its eigenvectors.
ABI-stable borrowed signed-index storage descriptor.
ABI-stable borrowed column-major matrix storage descriptor.
ABI-stable borrowed contiguous-vector storage descriptor.
Reusable row and column deletion updates for thin singular value decompositions.