Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 09:20:50

0001 // Copyright (c) 2025 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef _MathLin_Householder_HeaderFile
0015 #define _MathLin_Householder_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 
0021 #include <cmath>
0022 #include <algorithm>
0023 
0024 namespace MathLin
0025 {
0026 using namespace MathUtils;
0027 
0028 //! Result for QR decomposition using Householder reflections.
0029 struct QRResult
0030 {
0031   MathUtils::Status          Status = MathUtils::Status::NotConverged;
0032   std::optional<math_Matrix> Q;        //!< Orthogonal matrix Q (m x m)
0033   std::optional<math_Matrix> R;        //!< Upper triangular matrix R (m x n)
0034   int                        Rank = 0; //!< Numerical rank
0035 
0036   bool IsDone() const { return Status == MathUtils::Status::OK; }
0037 
0038   explicit operator bool() const { return IsDone(); }
0039 };
0040 
0041 //! QR decomposition using Householder reflections: A = Q * R.
0042 //!
0043 //! Decomposes an m x n matrix A (m >= n) into:
0044 //! - Q: m x m orthogonal matrix (Q^T * Q = I)
0045 //! - R: m x n upper triangular matrix
0046 //!
0047 //! The Householder method applies orthogonal transformations
0048 //! to reduce A to upper triangular form. It is more numerically
0049 //! stable than Gram-Schmidt orthogonalization.
0050 //!
0051 //! Uses: Least squares problems, orthogonalization, computing
0052 //! determinant sign.
0053 //!
0054 //! @param theA input matrix A (m x n, m >= n)
0055 //! @param theTolerance for rank determination
0056 //! @return QR decomposition result
0057 inline QRResult QR(const math_Matrix& theA, double theTolerance = 1.0e-20)
0058 {
0059   QRResult aResult;
0060 
0061   const int aRowLower = theA.LowerRow();
0062   const int aRowUpper = theA.UpperRow();
0063   const int aColLower = theA.LowerCol();
0064   const int aColUpper = theA.UpperCol();
0065   const int aM        = aRowUpper - aRowLower + 1; // Number of rows
0066   const int aN        = aColUpper - aColLower + 1; // Number of columns
0067 
0068   if (aM < aN)
0069   {
0070     aResult.Status = Status::InvalidInput;
0071     return aResult;
0072   }
0073 
0074   // Working copy of A that will become R
0075   math_Matrix aR(aRowLower, aRowUpper, aColLower, aColUpper);
0076   for (int i = aRowLower; i <= aRowUpper; ++i)
0077   {
0078     for (int j = aColLower; j <= aColUpper; ++j)
0079     {
0080       aR(i, j) = theA(i, j);
0081     }
0082   }
0083 
0084   // Q starts as identity
0085   math_Matrix aQ(aRowLower, aRowUpper, aRowLower, aRowUpper, 0.0);
0086   for (int i = aRowLower; i <= aRowUpper; ++i)
0087   {
0088     aQ(i, i) = 1.0;
0089   }
0090 
0091   // Householder vector storage
0092   math_Vector aV(aRowLower, aRowUpper);
0093 
0094   aResult.Rank = 0;
0095 
0096   // Apply Householder reflections to each column
0097   for (int j = aColLower; j <= aColUpper; ++j)
0098   {
0099     const int aJOffset = j - aColLower;
0100 
0101     // Compute norm of column below diagonal
0102     double aNorm = 0.0;
0103     for (int i = aRowLower + aJOffset; i <= aRowUpper; ++i)
0104     {
0105       aNorm += MathUtils::Sqr(aR(i, j));
0106     }
0107     aNorm = std::sqrt(aNorm);
0108 
0109     if (aNorm < theTolerance)
0110     {
0111       // Column is essentially zero, skip
0112       continue;
0113     }
0114 
0115     ++aResult.Rank;
0116 
0117     // Compute Householder vector v
0118     double aAlpha = aR(aRowLower + aJOffset, j);
0119     double aBeta  = (aAlpha >= 0.0) ? -aNorm : aNorm;
0120 
0121     // v = [0,...,0, x_j - beta, x_{j+1}, ..., x_m]
0122     for (int i = aRowLower; i < aRowLower + aJOffset; ++i)
0123     {
0124       aV(i) = 0.0;
0125     }
0126     aV(aRowLower + aJOffset) = aAlpha - aBeta;
0127     for (int i = aRowLower + aJOffset + 1; i <= aRowUpper; ++i)
0128     {
0129       aV(i) = aR(i, j);
0130     }
0131 
0132     // Compute tau = 2 / (v^T v)
0133     double aVNormSq = 0.0;
0134     for (int i = aRowLower + aJOffset; i <= aRowUpper; ++i)
0135     {
0136       aVNormSq += MathUtils::Sqr(aV(i));
0137     }
0138 
0139     if (aVNormSq < MathUtils::THE_ZERO_TOL)
0140     {
0141       continue;
0142     }
0143 
0144     double aTau = 2.0 / aVNormSq;
0145 
0146     // Apply H = I - tau * v * v^T to remaining columns of R
0147     // R := H * R
0148     for (int k = j; k <= aColUpper; ++k)
0149     {
0150       // Compute v^T * R(:,k)
0151       double aVdotRk = 0.0;
0152       for (int i = aRowLower + aJOffset; i <= aRowUpper; ++i)
0153       {
0154         aVdotRk += aV(i) * aR(i, k);
0155       }
0156 
0157       // R(:,k) := R(:,k) - tau * (v^T * R(:,k)) * v
0158       for (int i = aRowLower + aJOffset; i <= aRowUpper; ++i)
0159       {
0160         aR(i, k) -= aTau * aVdotRk * aV(i);
0161       }
0162     }
0163 
0164     // Apply H to Q: Q := Q * H = Q - tau * Q * v * v^T
0165     for (int k = aRowLower; k <= aRowUpper; ++k)
0166     {
0167       // Compute Q(k,:) * v
0168       double aQkV = 0.0;
0169       for (int i = aRowLower + aJOffset; i <= aRowUpper; ++i)
0170       {
0171         aQkV += aQ(k, i) * aV(i);
0172       }
0173 
0174       // Q(k,:) := Q(k,:) - tau * (Q(k,:) * v) * v^T
0175       for (int i = aRowLower + aJOffset; i <= aRowUpper; ++i)
0176       {
0177         aQ(k, i) -= aTau * aQkV * aV(i);
0178       }
0179     }
0180   }
0181 
0182   // Zero out below-diagonal elements of R for cleanliness
0183   for (int i = aRowLower; i <= aRowUpper; ++i)
0184   {
0185     for (int j = aColLower; j < aColLower + (i - aRowLower) && j <= aColUpper; ++j)
0186     {
0187       aR(i, j) = 0.0;
0188     }
0189   }
0190 
0191   aResult.Q      = aQ;
0192   aResult.R      = aR;
0193   aResult.Status = Status::OK;
0194   return aResult;
0195 }
0196 
0197 //! Solve overdetermined system Ax = b using QR decomposition (least squares).
0198 //!
0199 //! For m x n system with m > n, finds x that minimizes ||Ax - b||_2.
0200 //!
0201 //! Algorithm:
0202 //! 1. Decompose A = Q * R
0203 //! 2. Compute c = Q^T * b
0204 //! 3. Solve R * x = c[1:n] (back substitution)
0205 //!
0206 //! @param theA coefficient matrix (m x n, m >= n)
0207 //! @param theB right-hand side vector (length m)
0208 //! @param theTolerance for singularity detection
0209 //! @return result containing least squares solution
0210 inline LinearResult SolveQR(const math_Matrix& theA,
0211                             const math_Vector& theB,
0212                             double             theTolerance = 1.0e-20)
0213 {
0214   LinearResult aResult;
0215 
0216   const int aRowLower = theA.LowerRow();
0217   const int aRowUpper = theA.UpperRow();
0218   const int aColLower = theA.LowerCol();
0219   const int aColUpper = theA.UpperCol();
0220   const int aM        = aRowUpper - aRowLower + 1;
0221 
0222   // Check dimensions
0223   if (theB.Length() != aM)
0224   {
0225     aResult.Status = Status::InvalidInput;
0226     return aResult;
0227   }
0228 
0229   // Perform QR decomposition
0230   QRResult aQR = QR(theA, theTolerance);
0231   if (!aQR.IsDone())
0232   {
0233     aResult.Status = aQR.Status;
0234     return aResult;
0235   }
0236 
0237   const math_Matrix& aQ = *aQR.Q;
0238   const math_Matrix& aR = *aQR.R;
0239 
0240   // Compute c = Q^T * b
0241   math_Vector aC(aRowLower, aRowUpper, 0.0);
0242   for (int i = aRowLower; i <= aRowUpper; ++i)
0243   {
0244     double aSum = 0.0;
0245     for (int k = aRowLower; k <= aRowUpper; ++k)
0246     {
0247       aSum += aQ(k, i) * theB(theB.Lower() + k - aRowLower);
0248     }
0249     aC(i) = aSum;
0250   }
0251 
0252   // Back substitution: R[1:n, 1:n] * x = c[1:n]
0253   aResult.Solution = math_Vector(aColLower, aColUpper, 0.0);
0254 
0255   for (int i = aColUpper; i >= aColLower; --i)
0256   {
0257     const int aIOffset = i - aColLower;
0258     double    aDiag    = aR(aRowLower + aIOffset, i);
0259 
0260     if (std::abs(aDiag) < theTolerance)
0261     {
0262       aResult.Status = Status::Singular;
0263       return aResult;
0264     }
0265 
0266     double aSum = aC(aRowLower + aIOffset);
0267     for (int k = i + 1; k <= aColUpper; ++k)
0268     {
0269       aSum -= aR(aRowLower + aIOffset, k) * (*aResult.Solution)(k);
0270     }
0271     (*aResult.Solution)(i) = aSum / aDiag;
0272   }
0273 
0274   aResult.Status = Status::OK;
0275   return aResult;
0276 }
0277 
0278 //! Solve multiple right-hand sides using QR decomposition.
0279 //!
0280 //! @param theA coefficient matrix (m x n, m >= n)
0281 //! @param theB right-hand side matrix (m x p)
0282 //! @param theTolerance for singularity detection
0283 //! @return result containing solution matrix (n x p)
0284 inline LinearMultipleResult SolveQRMultiple(const math_Matrix& theA,
0285                                             const math_Matrix& theB,
0286                                             double             theTolerance = 1.0e-20)
0287 {
0288   LinearMultipleResult aResult;
0289 
0290   const int aRowLower = theA.LowerRow();
0291   const int aRowUpper = theA.UpperRow();
0292   const int aColLower = theA.LowerCol();
0293   const int aColUpper = theA.UpperCol();
0294 
0295   // Check dimensions
0296   if (theB.RowNumber() != theA.RowNumber())
0297   {
0298     aResult.Status = Status::InvalidInput;
0299     return aResult;
0300   }
0301 
0302   // Perform QR decomposition
0303   QRResult aQR = QR(theA, theTolerance);
0304   if (!aQR.IsDone())
0305   {
0306     aResult.Status = aQR.Status;
0307     return aResult;
0308   }
0309 
0310   const math_Matrix& aQ = *aQR.Q;
0311   const math_Matrix& aR = *aQR.R;
0312 
0313   // Solve for each column of B
0314   math_Matrix aX(aColLower, aColUpper, theB.LowerCol(), theB.UpperCol(), 0.0);
0315 
0316   for (int j = theB.LowerCol(); j <= theB.UpperCol(); ++j)
0317   {
0318     // Compute c = Q^T * b_j
0319     math_Vector aC(aRowLower, aRowUpper, 0.0);
0320     for (int i = aRowLower; i <= aRowUpper; ++i)
0321     {
0322       double aSum = 0.0;
0323       for (int k = aRowLower; k <= aRowUpper; ++k)
0324       {
0325         const int aBRow = theB.LowerRow() + (k - aRowLower);
0326         aSum += aQ(k, i) * theB(aBRow, j);
0327       }
0328       aC(i) = aSum;
0329     }
0330 
0331     // Back substitution: R[1:n,1:n] * x_j = c[1:n]
0332     for (int i = aColUpper; i >= aColLower; --i)
0333     {
0334       const int aIOffset = i - aColLower;
0335       const int aRRow    = aRowLower + aIOffset;
0336       double    aDiag    = aR(aRRow, i);
0337 
0338       if (std::abs(aDiag) < theTolerance)
0339       {
0340         aResult.Status = Status::Singular;
0341         return aResult;
0342       }
0343 
0344       double aSum = aC(aRRow);
0345       for (int k = i + 1; k <= aColUpper; ++k)
0346       {
0347         aSum -= aR(aRRow, k) * aX(k, j);
0348       }
0349       aX(i, j) = aSum / aDiag;
0350     }
0351   }
0352 
0353   aResult.Solutions = aX;
0354   aResult.Status    = Status::OK;
0355   return aResult;
0356 }
0357 
0358 } // namespace MathLin
0359 
0360 #endif // _MathLin_Householder_HeaderFile