Back to home page

EIC code displayed by LXR

 
 

    


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

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 _MathOpt_Uzawa_HeaderFile
0015 #define _MathOpt_Uzawa_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <math_Vector.hxx>
0020 #include <math_Matrix.hxx>
0021 #include <MathLin_Crout.hxx>
0022 
0023 #include <cmath>
0024 
0025 namespace MathOpt
0026 {
0027 using namespace MathUtils;
0028 
0029 //! Result for Uzawa constrained optimization.
0030 struct UzawaResult
0031 {
0032   MathUtils::Status          Status = MathUtils::Status::NotConverged;
0033   std::optional<math_Vector> Solution;     //!< Solution vector X
0034   std::optional<math_Vector> Dual;         //!< Dual (Lagrange) variables
0035   std::optional<math_Vector> Error;        //!< X - X0 (difference from starting point)
0036   std::optional<math_Vector> InitialError; //!< C*X0 - S (initial constraint violation)
0037   std::optional<math_Matrix> InverseCTC;   //!< (C * C^T)^-1 for gradient computation
0038   int                        NbIterations = 0;
0039 
0040   bool IsDone() const { return Status == MathUtils::Status::OK; }
0041 
0042   explicit operator bool() const { return IsDone(); }
0043 };
0044 
0045 //! Configuration for Uzawa algorithm.
0046 struct UzawaConfig
0047 {
0048   double EpsLix        = 1.0e-6; //!< Tolerance for X convergence
0049   double EpsLic        = 1.0e-6; //!< Tolerance for dual variable convergence
0050   int    MaxIterations = 500;    //!< Maximum iterations
0051 };
0052 
0053 //! Solve constrained least squares using Uzawa algorithm.
0054 //!
0055 //! Solves: min ||X - X0||^2 subject to C*X = S
0056 //!
0057 //! For equality constraints only, uses direct Crout decomposition.
0058 //! For mixed equality/inequality constraints, uses iterative Uzawa method.
0059 //!
0060 //! The Uzawa algorithm is a dual decomposition method that:
0061 //! 1. Updates primal variables X to minimize Lagrangian
0062 //! 2. Updates dual variables (Lagrange multipliers) for constraint violations
0063 //!
0064 //! @param theCont constraint matrix C (Nce+Nci rows x N cols)
0065 //! @param theSecont right-hand side S
0066 //! @param theStartingPoint initial point X0
0067 //! @param theNce number of equality constraints (first rows)
0068 //! @param theNci number of inequality constraints (last rows, C*X <= S)
0069 //! @param theConfig algorithm configuration
0070 //! @return UzawaResult with solution and auxiliary data
0071 inline UzawaResult Uzawa(const math_Matrix& theCont,
0072                          const math_Vector& theSecont,
0073                          const math_Vector& theStartingPoint,
0074                          int                theNce,
0075                          int                theNci,
0076                          const UzawaConfig& theConfig = UzawaConfig())
0077 {
0078   UzawaResult aResult;
0079 
0080   const int aNlig = theCont.RowNumber();
0081   const int aNcol = theCont.ColNumber();
0082 
0083   // Validate dimensions
0084   if (theSecont.Length() != aNlig || (theNce + theNci) != aNlig
0085       || theStartingPoint.Length() != aNcol)
0086   {
0087     aResult.Status = Status::InvalidInput;
0088     return aResult;
0089   }
0090 
0091   const int aRowLower = theCont.LowerRow();
0092   const int aColLower = theCont.LowerCol();
0093   const int aSecLower = theSecont.Lower();
0094   const int aXLower   = theStartingPoint.Lower();
0095 
0096   // Compute initial error: C*X0 - S
0097   math_Vector aErrinit(1, aNlig);
0098   for (int i = 1; i <= aNlig; ++i)
0099   {
0100     double aSum = 0.0;
0101     for (int j = 1; j <= aNcol; ++j)
0102     {
0103       aSum += theCont(i + aRowLower - 1, j + aColLower - 1) * theStartingPoint(j + aXLower - 1);
0104     }
0105     aErrinit(i) = aSum - theSecont(i + aSecLower - 1);
0106   }
0107 
0108   aResult.InitialError = aErrinit;
0109 
0110   // Initialize dual variables and error
0111   math_Vector aVardua(1, aNlig);
0112   math_Vector aErruza(1, aNcol, 0.0);
0113   math_Matrix aCTCinv(1, aNlig, 1, aNlig, 0.0);
0114 
0115   if (theNci == 0)
0116   {
0117     // Direct solution for equality constraints only
0118     aResult.NbIterations = 1;
0119 
0120     // Compute C * C^T (symmetric)
0121     for (int i = 1; i <= aNlig; ++i)
0122     {
0123       for (int j = 1; j <= i; ++j)
0124       {
0125         double aSum = 0.0;
0126         for (int k = 1; k <= aNcol; ++k)
0127         {
0128           aSum += theCont(i + aRowLower - 1, k + aColLower - 1)
0129                   * theCont(j + aRowLower - 1, k + aColLower - 1);
0130         }
0131         aCTCinv(i, j) = aSum;
0132         if (i != j)
0133         {
0134           aCTCinv(j, i) = aSum;
0135         }
0136       }
0137     }
0138 
0139     // Invert using Crout
0140     MathLin::InverseResult aInvResult = MathLin::InvertCrout(aCTCinv);
0141     if (!aInvResult.IsDone())
0142     {
0143       aResult.Status = Status::Singular;
0144       return aResult;
0145     }
0146     aCTCinv = *aInvResult.Inverse;
0147 
0148     // Compute dual variables: (C*C^T)^-1 * (C*X0 - S)
0149     for (int i = 1; i <= aNlig; ++i)
0150     {
0151       double aSum = 0.0;
0152       for (int j = 1; j <= aNlig; ++j)
0153       {
0154         aSum += aCTCinv(i, j) * aErrinit(j);
0155       }
0156       aVardua(i) = aSum;
0157     }
0158 
0159     // Compute error: -C^T * dual
0160     for (int i = 1; i <= aNcol; ++i)
0161     {
0162       double aSum = 0.0;
0163       for (int j = 1; j <= aNlig; ++j)
0164       {
0165         aSum -= theCont(j + aRowLower - 1, i + aColLower - 1) * aVardua(j);
0166       }
0167       aErruza(i) = aSum;
0168     }
0169 
0170     // Compute solution
0171     math_Vector aResul(1, aNcol);
0172     for (int i = 1; i <= aNcol; ++i)
0173     {
0174       aResul(i) = theStartingPoint(i + aXLower - 1) + aErruza(i);
0175     }
0176 
0177     aResult.Solution   = aResul;
0178     aResult.Dual       = aVardua;
0179     aResult.Error      = aErruza;
0180     aResult.InverseCTC = aCTCinv;
0181     aResult.Status     = Status::OK;
0182     return aResult;
0183   }
0184 
0185   // Iterative Uzawa for mixed equality/inequality constraints
0186   // Initialize dual variables
0187   for (int i = 1; i <= aNlig; ++i)
0188   {
0189     aVardua(i) = (i <= theNce) ? 0.0 : 1.0;
0190   }
0191 
0192   // Compute step size rho
0193   double aNormat = 0.0;
0194   for (int i = 1; i <= aNlig; ++i)
0195   {
0196     double aNormli = 0.0;
0197     for (int j = 1; j <= aNcol; ++j)
0198     {
0199       double aVal = theCont(i + aRowLower - 1, j + aColLower - 1);
0200       aNormli += aVal * aVal;
0201     }
0202     aNormat += aNormli;
0203   }
0204   const double aRho = 1.0 / (std::sqrt(2.0) * aNormat);
0205 
0206   // Uzawa iterations
0207   for (int anIter = 1; anIter <= theConfig.MaxIterations; ++anIter)
0208   {
0209     aResult.NbIterations = anIter;
0210 
0211     double aXmax = 0.0;
0212 
0213     // Update primal: X = X0 - C^T * dual
0214     for (int i = 1; i <= aNcol; ++i)
0215     {
0216       double aXprev = aErruza(i);
0217       double aSum   = 0.0;
0218       for (int j = 1; j <= aNlig; ++j)
0219       {
0220         aSum -= theCont(j + aRowLower - 1, i + aColLower - 1) * aVardua(j);
0221       }
0222       aErruza(i) = aSum;
0223 
0224       if (anIter > 1)
0225       {
0226         aXmax = std::max(aXmax, std::abs(aErruza(i) - aXprev));
0227       }
0228     }
0229 
0230     // Update dual variables and compute constraint error
0231     double aErrMax = 0.0;
0232     for (int i = 1; i <= aNlig; ++i)
0233     {
0234       // Constraint violation: C*X - S
0235       double aErr = aErrinit(i);
0236       for (int j = 1; j <= aNcol; ++j)
0237       {
0238         aErr += theCont(i + aRowLower - 1, j + aColLower - 1) * aErruza(j);
0239       }
0240 
0241       double aErr1;
0242       if (i <= theNce)
0243       {
0244         // Equality constraint: update freely
0245         aVardua(i) += aRho * aErr;
0246         aErr1 = std::abs(aRho * aErr);
0247       }
0248       else
0249       {
0250         // Inequality constraint: project to non-negative
0251         double aXmuPrev = aVardua(i);
0252         aVardua(i)      = std::max(0.0, aVardua(i) + aRho * aErr);
0253         aErr1           = std::abs(aVardua(i) - aXmuPrev);
0254       }
0255       aErrMax = std::max(aErrMax, aErr1);
0256     }
0257 
0258     // Check convergence
0259     if (anIter > 1)
0260     {
0261       if (aXmax <= theConfig.EpsLix)
0262       {
0263         if (aErrMax <= theConfig.EpsLic)
0264         {
0265           // Converged
0266           math_Vector aResul(1, aNcol);
0267           for (int i = 1; i <= aNcol; ++i)
0268           {
0269             aResul(i) = theStartingPoint(i + aXLower - 1) + aErruza(i);
0270           }
0271 
0272           aResult.Solution = aResul;
0273           aResult.Dual     = aVardua;
0274           aResult.Error    = aErruza;
0275           aResult.Status   = Status::OK;
0276           return aResult;
0277         }
0278         else
0279         {
0280           // Dual did not converge
0281           aResult.Status = Status::NotConverged;
0282           return aResult;
0283         }
0284       }
0285     }
0286   }
0287 
0288   aResult.Status = Status::MaxIterations;
0289   return aResult;
0290 }
0291 
0292 //! Solve constrained least squares with equality constraints only.
0293 //!
0294 //! Convenience function for C*X = S with min ||X - X0||.
0295 //!
0296 //! @param theCont constraint matrix C
0297 //! @param theSecont right-hand side S
0298 //! @param theStartingPoint initial point X0
0299 //! @param theConfig algorithm configuration
0300 //! @return UzawaResult with solution
0301 inline UzawaResult UzawaEquality(const math_Matrix& theCont,
0302                                  const math_Vector& theSecont,
0303                                  const math_Vector& theStartingPoint,
0304                                  const UzawaConfig& theConfig = UzawaConfig())
0305 {
0306   return Uzawa(theCont, theSecont, theStartingPoint, theCont.RowNumber(), 0, theConfig);
0307 }
0308 
0309 } // namespace MathOpt
0310 
0311 #endif // _MathOpt_Uzawa_HeaderFile