// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2011 Konstantinos Poulios * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #ifdef _MSC_VER # pragma warning(disable : 4251) # pragma warning(disable : 4244) # pragma warning(disable : 4996) #endif #undef _GCS_DEBUG #undef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX #undef _DEBUG_TO_FILE // This has to be included BEFORE any EIGEN include // This format is Sage compatible, so you can just copy/paste the matrix into Sage #ifdef _GCS_DEBUG # define EIGEN_DEFAULT_IO_FORMAT Eigen::IOFormat(3, 0, ",", ",\n", "[", "]", "[", "]") /* Parameters: * * StreamPrecision, * int _flags = 0, * const std::string & _coeffSeparator = " ", * const std::string & _rowSeparator = "\n", * const std::string & _rowPrefix = "", * const std::string & _rowSuffix = "", * const std::string & _matPrefix = "", * const std::string & _matSuffix = "" )*/ #endif #include #include #include #include #include #include "GCS.h" #include "qp_eq.h" // NOTE: In CMakeList.txt -DEIGEN_NO_DEBUG is set (it does not work with a define here), to solve // this: this is needed to fix this SparseQR crash // https://forum.freecad.org/viewtopic.php?f=10&t=11341&p=92146#p92146, until Eigen library fixes // its own problem with the assertion (definitely not solved in 3.2.0 branch) NOTE2: solved in // eigen3.3 // Extraction of Q matrix for Debugging used to crash #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX # if EIGEN_VERSION >= 30304 # define SPARSE_Q_MATRIX # endif #endif #if EIGEN_VERSION > 30290 // This regulates that only starting in Eigen 3.3, the problem with // https://forum.freecad.org/viewtopic.php?f=3&t=4651&start=40 // was solved in Eigen: // https://forum.freecad.org/viewtopic.php?f=10&t=12769&start=60#p106492 // https://forum.kde.org/viewtopic.php?f=74&t=129439 # define EIGEN_STOCK_FULLPIVLU_COMPUTE #endif // #undef EIGEN_SPARSEQR_COMPATIBLE #ifdef EIGEN_SPARSEQR_COMPATIBLE # include #endif // _GCS_EXTRACT_SOLVER_SUBSYSTEM_ to be enabled in Constraints.h when needed. #if defined(_GCS_EXTRACT_SOLVER_SUBSYSTEM_) || defined(_DEBUG_TO_FILE) # include # define CASE_NOT_IMP(X) \ case X: { \ subsystemfile << "//" #X "not yet implemented" << std::endl; \ break; \ } #endif #include #include #include #include using MatrixIndexType = Eigen::FullPivHouseholderQR::IntDiagSizeVectorType; #ifndef EIGEN_STOCK_FULLPIVLU_COMPUTE namespace Eigen { using MatrixdType = Matrix; template<> FullPivLU& FullPivLU::compute(const MatrixdType& matrix) { m_isInitialized = true; m_lu = matrix; const Index size = matrix.diagonalSize(); const Index rows = matrix.rows(); const Index cols = matrix.cols(); // will store the transpositions, before we accumulate them at the end. // can't accumulate on-the-fly because that will be done in reverse order for the rows. m_rowsTranspositions.resize(matrix.rows()); m_colsTranspositions.resize(matrix.cols()); // number of NONTRIVIAL transpositions, i.e. m_rowsTranspositions[i]!=i Index number_of_transpositions = 0; // the generic case is that in which all pivots are nonzero (invertible case) m_nonzero_pivots = size; m_maxpivot = RealScalar(0); RealScalar cutoff(0); for (Index k = 0; k < size; ++k) { // First, we need to find the pivot. // biggest coefficient in the remaining bottom-right corner (starting at row k, col k) Index row_of_biggest_in_corner, col_of_biggest_in_corner; RealScalar biggest_in_corner; biggest_in_corner = m_lu.bottomRightCorner(rows - k, cols - k) .cwiseAbs() .maxCoeff(&row_of_biggest_in_corner, &col_of_biggest_in_corner); // correct the values! since they were computed in the corner, row_of_biggest_in_corner += k; col_of_biggest_in_corner += k; // need to add k to them. // when k==0, biggest_in_corner is the biggest coeff absolute value in the original // matrix if (k == 0) { cutoff = biggest_in_corner * NumTraits::epsilon(); } // if the pivot (hence the corner) is "zero", terminate to avoid generating nan/inf // values. Notice that using an exact comparison (biggest_in_corner==0) here, as // Golub-van Loan do in their pseudo-code, results in numerical instability! The cutoff // here has been validated by running the unit test 'lu' with many repetitions. if (biggest_in_corner < cutoff) { // before exiting, make sure to initialize the still uninitialized transpositions // in a sane state without destroying what we already have. m_nonzero_pivots = k; for (Index i = k; i < size; ++i) { m_rowsTranspositions.coeffRef(i) = i; m_colsTranspositions.coeffRef(i) = i; } break; } if (biggest_in_corner > m_maxpivot) { m_maxpivot = biggest_in_corner; } // Now that we've found the pivot, we need to apply the row/col swaps to // bring it to the location (k,k). m_rowsTranspositions.coeffRef(k) = row_of_biggest_in_corner; m_colsTranspositions.coeffRef(k) = col_of_biggest_in_corner; if (k != row_of_biggest_in_corner) { m_lu.row(k).swap(m_lu.row(row_of_biggest_in_corner)); ++number_of_transpositions; } if (k != col_of_biggest_in_corner) { m_lu.col(k).swap(m_lu.col(col_of_biggest_in_corner)); ++number_of_transpositions; } // Now that the pivot is at the right location, we update the remaining // bottom-right corner by Gaussian elimination. if (k < rows - 1) { m_lu.col(k).tail(rows - k - 1) /= m_lu.coeff(k, k); } if (k < size - 1) { m_lu.block(k + 1, k + 1, rows - k - 1, cols - k - 1).noalias() -= m_lu.col(k).tail(rows - k - 1) * m_lu.row(k).tail(cols - k - 1); } } // the main loop is over, we still have to accumulate the transpositions to find the // permutations P and Q m_p.setIdentity(rows); for (Index k = size - 1; k >= 0; --k) { m_p.applyTranspositionOnTheRight(k, m_rowsTranspositions.coeff(k)); } m_q.setIdentity(cols); for (Index k = 0; k < size; ++k) { m_q.applyTranspositionOnTheRight(k, m_colsTranspositions.coeff(k)); } m_det_pq = (number_of_transpositions % 2) ? -1 : 1; return *this; } } // namespace Eigen #endif namespace GCS { class SolverReportingManager { public: SolverReportingManager(SolverReportingManager const&) = delete; SolverReportingManager(SolverReportingManager&&) = delete; SolverReportingManager& operator=(SolverReportingManager const&) = delete; SolverReportingManager& operator=(SolverReportingManager&&) = delete; static SolverReportingManager& Manager(); inline void LogString(const std::string& str); inline void LogToConsole(const std::string& str); inline void LogToFile(const std::string& str); void LogQRSystemInformation(const System& system, int paramsNum = 0, int constrNum = 0, int rank = 0); void LogGroupOfConstraints( const std::string& str, std::vector> constraintgroups ); void LogSetOfConstraints(const std::string& str, std::set constraintset); void LogGroupOfParameters(const std::string& str, std::vector> parametergroups); void LogMatrix(const std::string str, Eigen::MatrixXd matrix); void LogMatrix(const std::string str, MatrixIndexType matrix); private: SolverReportingManager(); ~SolverReportingManager(); inline void initStream(); inline void flushStream(); private: #ifdef _DEBUG_TO_FILE std::ofstream stream; #endif }; SolverReportingManager::SolverReportingManager() { initStream(); } SolverReportingManager::~SolverReportingManager() { #ifdef _DEBUG_TO_FILE stream.flush(); stream.close(); #endif } void SolverReportingManager::initStream() { #ifdef _DEBUG_TO_FILE if (!stream.is_open()) { stream.open("GCS_debug.txt", std::ofstream::out | std::ofstream::app); } #endif } void SolverReportingManager::flushStream() { // Akwardly in some systems flushing does not force the write to the file, requiring a close #ifdef _DEBUG_TO_FILE stream.flush(); stream.close(); #endif } SolverReportingManager& SolverReportingManager::Manager() { static SolverReportingManager theInstance; return theInstance; } void SolverReportingManager::LogToConsole(const std::string& str) { Base::Console().log(str.c_str()); } void SolverReportingManager::LogToFile(const std::string& str) { #ifdef _DEBUG_TO_FILE initStream(); stream << str << std::endl; flushStream(); #else (void)(str); // silence unused parameter LogToConsole("Debugging to file not enabled!"); #endif } void SolverReportingManager::LogString(const std::string& str) { LogToConsole(str); #ifdef _DEBUG_TO_FILE LogToFile(str); #endif } void SolverReportingManager::LogQRSystemInformation( const System& system, int paramsNum, int constrNum, int rank ) { std::stringstream tempstream; tempstream << (system.qrAlgorithm == EigenSparseQR ? "EigenSparseQR" : (system.qrAlgorithm == EigenDenseQR ? "DenseQR" : "")); if (paramsNum > 0) { tempstream #ifdef EIGEN_SPARSEQR_COMPATIBLE << ", Threads: " << Eigen::nbThreads() #endif #ifdef EIGEN_VECTORIZE << ", Vectorization: On" #endif << ", Pivot Threshold: " << system.qrpivotThreshold << ", Params: " << paramsNum << ", Constr: " << constrNum << ", Rank: " << rank << std::endl; } else { tempstream #ifdef EIGEN_SPARSEQR_COMPATIBLE << ", Threads: " << Eigen::nbThreads() #endif #ifdef EIGEN_VECTORIZE << ", Vectorization: On" #endif << ", Empty Sketch, nothing to solve" << std::endl; } LogString(tempstream.str()); } void SolverReportingManager::LogGroupOfConstraints( const std::string& str, std::vector> constraintgroups ) { std::stringstream tempstream; tempstream << str << ":" << '\n'; for (const auto& group : constraintgroups) { tempstream << "["; for (auto c : group) { tempstream << c->getTag() << " "; } tempstream << "]" << '\n'; } LogString(tempstream.str()); } void SolverReportingManager::LogSetOfConstraints( const std::string& str, std::set constraintset ) { std::stringstream tempstream; tempstream << str << ": ["; for (auto c : constraintset) { tempstream << c->getTag() << " "; } tempstream << "]" << '\n'; LogString(tempstream.str()); } void SolverReportingManager::LogGroupOfParameters( const std::string& str, std::vector> parametergroups ) { std::stringstream tempstream; tempstream << str << ":" << '\n'; for (size_t i = 0; i < parametergroups.size(); i++) { tempstream << "["; for (auto p : parametergroups[i]) { tempstream << std::hex << p << " "; } tempstream << "]" << '\n'; } LogString(tempstream.str()); } #ifdef _GCS_DEBUG void SolverReportingManager::LogMatrix(const std::string str, Eigen::MatrixXd matrix) { std::stringstream tempstream; tempstream << '\n' << " " << str << " =" << '\n'; tempstream << "[" << '\n'; tempstream << matrix << '\n'; tempstream << "]" << '\n'; LogString(tempstream.str()); } void SolverReportingManager::LogMatrix(const std::string str, MatrixIndexType matrix) { std::stringstream tempstream; stream << '\n' << " " << str << " =" << '\n'; stream << "[" << '\n'; stream << matrix << '\n'; stream << "]" << '\n'; LogString(tempstream.str()); } #endif using Graph = boost::adjacency_list; /////////////////////////////////////// // Solver /////////////////////////////////////// // System System::System() : plist(0) , pdrivenlist(0) , pDependentParameters(0) , clist(0) , c2p() , p2c() , subSystems(0) , subSystemsAux(0) , reference(0) , dofs(0) , hasUnknowns(false) , hasDiagnosis(false) , isInit(false) , emptyDiagnoseMatrix(true) , maxIter(100) , maxIterRedundant(100) , sketchSizeMultiplier(false) , sketchSizeMultiplierRedundant(false) , convergence(1e-10) , convergenceRedundant(1e-10) , qrAlgorithm(EigenSparseQR) , autoChooseAlgorithm(true) , autoQRThreshold(1000) , dogLegGaussStep(FullPivLU) , qrpivotThreshold(1E-13) , debugMode(Minimal) , LM_eps(1E-10) , LM_eps1(1E-80) , LM_tau(1E-3) , DL_tolg(1E-80) , DL_tolx(1E-80) , DL_tolf(1E-10) , LM_epsRedundant(1E-10) , LM_eps1Redundant(1E-80) , LM_tauRedundant(1E-3) , DL_tolgRedundant(1E-80) , DL_tolxRedundant(1E-80) , DL_tolfRedundant(1E-10) { // currently Eigen only supports multithreading for multiplications // There is no appreciable gain from using more threads #ifdef EIGEN_SPARSEQR_COMPATIBLE Eigen::setNbThreads(1); #endif } System::~System() { clear(); } void System::clear() { plist.clear(); pdrivenlist.clear(); pIndex.clear(); pDependentParameters.clear(); pDependentParametersGroups.clear(); hasUnknowns = false; hasDiagnosis = false; emptyDiagnoseMatrix = true; redundant.clear(); conflictingTags.clear(); redundantTags.clear(); partiallyRedundantTags.clear(); reference.clear(); clearSubSystems(); deleteAllContent(clist); c2p.clear(); p2c.clear(); } void System::invalidatedDiagnosis() { hasDiagnosis = false; pDependentParameters.clear(); pDependentParametersGroups.clear(); } void System::clearByTag(int tagId) { std::vector constrvec; for (const auto& constr : clist) { if (constr->getTag() == tagId) { constrvec.push_back(constr); } } for (const auto& constr : constrvec) { removeConstraint(constr); } } int System::addConstraint(Constraint* constr) { isInit = false; if (constr->getTag() >= 0) { // negatively tagged constraints have no impact hasDiagnosis = false; // on the diagnosis } clist.push_back(constr); VEC_pD constr_params = constr->params(); for (const auto& param : constr_params) { // jacobi.set(constr, *param, 0.); c2p[constr].push_back(param); p2c[param].push_back(constr); } return clist.size() - 1; } void System::removeConstraint(Constraint* constr) { std::vector::iterator it; it = std::ranges::find(clist, constr); if (it == clist.end()) { return; } clist.erase(it); if (constr->getTag() >= 0) { hasDiagnosis = false; } clearSubSystems(); for (const auto& param : c2p[constr]) { p2c[param].erase(std::ranges::find(p2c[param], constr)); } c2p.erase(constr); delete (constr); } // basic constraints int System::addConstraintEqual( double* param1, double* param2, int tagId, bool driving, Constraint::Alignment internalalignment ) { Constraint* constr = new ConstraintEqual(param1, param2); constr->setTag(tagId); constr->setDriving(driving); constr->setInternalAlignment(internalalignment); return addConstraint(constr); } int System::addConstraintProportional(double* param1, double* param2, double ratio, int tagId, bool driving) { Constraint* constr = new ConstraintEqual(param1, param2, ratio); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintDifference(double* param1, double* param2, double* difference, int tagId, bool driving) { Constraint* constr = new ConstraintDifference(param1, param2, difference); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintP2PDistance(Point& p1, Point& p2, double* distance, int tagId, bool driving) { Constraint* constr = new ConstraintP2PDistance(p1, p2, distance); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintP2PAngle(Point& p1, Point& p2, double* angle, double incrAngle, int tagId, bool driving) { Constraint* constr = new ConstraintP2PAngle(p1, p2, angle, incrAngle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintP2PAngle(Point& p1, Point& p2, double* angle, int /*tagId*/, bool driving) { return addConstraintP2PAngle(p1, p2, angle, 0., 0, driving); } int System::addConstraintP2LDistance(Point& p, Line& l, double* distance, int tagId, bool driving) { Constraint* constr = new ConstraintP2LDistance(p, l, distance); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnLine(Point& p, Line& l, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnLine(p, l); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnLine(Point& p, Point& lp1, Point& lp2, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnLine(p, lp1, lp2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnPerpBisector(Point& p, Line& l, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnPerpBisector(p, l); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnPerpBisector(Point& p, Point& lp1, Point& lp2, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnPerpBisector(p, lp1, lp2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintParallel(Line& l1, Line& l2, int tagId, bool driving) { Constraint* constr = new ConstraintParallel(l1, l2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPerpendicular(Line& l1, Line& l2, int tagId, bool driving) { Constraint* constr = new ConstraintPerpendicular(l1, l2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPerpendicular( Point& l1p1, Point& l1p2, Point& l2p1, Point& l2p2, int tagId, bool driving ) { Constraint* constr = new ConstraintPerpendicular(l1p1, l1p2, l2p1, l2p2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintL2LAngle(Line& l1, Line& l2, double* angle, int tagId, bool driving) { Constraint* constr = new ConstraintL2LAngle(l1, l2, angle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintL2LAngle( Point& l1p1, Point& l1p2, Point& l2p1, Point& l2p2, double* angle, int tagId, bool driving ) { Constraint* constr = new ConstraintL2LAngle(l1p1, l1p2, l2p1, l2p2, angle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintAngleViaPoint(Curve& crv1, Curve& crv2, Point& p, double* angle, int tagId, bool driving) { Constraint* constr = new ConstraintAngleViaPoint(crv1, crv2, p, angle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintAngleViaTwoPoints( Curve& crv1, Curve& crv2, Point& p1, Point& p2, double* angle, int tagId, bool driving ) { Constraint* constr = new ConstraintAngleViaTwoPoints(crv1, crv2, p1, p2, angle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintAngleViaPointAndParam( Curve& crv1, Curve& crv2, Point& p, double* cparam, double* angle, int tagId, bool driving ) { Constraint* constr = new ConstraintAngleViaPointAndParam(crv1, crv2, p, cparam, angle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintAngleViaPointAndTwoParams( Curve& crv1, Curve& crv2, Point& p, double* cparam1, double* cparam2, double* angle, int tagId, bool driving ) { Constraint* constr = new ConstraintAngleViaPointAndTwoParams(crv1, crv2, p, cparam1, cparam2, angle); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintMidpointOnLine(Line& l1, Line& l2, int tagId, bool driving) { Constraint* constr = new ConstraintMidpointOnLine(l1, l2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintMidpointOnLine( Point& l1p1, Point& l1p2, Point& l2p1, Point& l2p2, int tagId, bool driving ) { Constraint* constr = new ConstraintMidpointOnLine(l1p1, l1p2, l2p1, l2p2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintTangentCircumf( Point& p1, Point& p2, double* rad1, double* rad2, bool internal, int tagId, bool driving ) { Constraint* constr = new ConstraintTangentCircumf(p1, p2, rad1, rad2, internal); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintTangentAtBSplineKnot( BSpline& b, Line& l, unsigned int knotindex, int tagId, bool driving ) { Constraint* constr = new ConstraintSlopeAtBSplineKnot(b, l, knotindex); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintC2CDistance(Circle& c1, Circle& c2, double* dist, int tagId, bool driving) { Constraint* constr = new ConstraintC2CDistance(c1, c2, dist); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintC2LDistance(Circle& c, Line& l, double* dist, int tagId, bool driving) { Constraint* constr = new ConstraintC2LDistance(c, l, dist); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintP2CDistance(Point& p, Circle& c, double* distance, int tagId, bool driving) { Constraint* constr = new ConstraintP2CDistance(p, c, distance); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintArcLength(Arc& a, double* distance, int tagId, bool driving) { Constraint* constr = new ConstraintArcLength(a, distance); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } // derived constraints int System::addConstraintP2PCoincident(Point& p1, Point& p2, int tagId, bool driving) { addConstraintEqual(p1.x, p2.x, tagId, driving); return addConstraintEqual(p1.y, p2.y, tagId, driving); } int System::addConstraintHorizontal(Line& l, int tagId, bool driving) { return addConstraintEqual(l.p1.y, l.p2.y, tagId, driving); } int System::addConstraintHorizontal(Point& p1, Point& p2, int tagId, bool driving) { return addConstraintEqual(p1.y, p2.y, tagId, driving); } int System::addConstraintVertical(Line& l, int tagId, bool driving) { return addConstraintEqual(l.p1.x, l.p2.x, tagId, driving); } int System::addConstraintVertical(Point& p1, Point& p2, int tagId, bool driving) { return addConstraintEqual(p1.x, p2.x, tagId, driving); } int System::addConstraintCoordinateX(Point& p, double* x, int tagId, bool driving) { return addConstraintEqual(p.x, x, tagId, driving); } int System::addConstraintCoordinateY(Point& p, double* y, int tagId, bool driving) { return addConstraintEqual(p.y, y, tagId, driving); } int System::addConstraintArcRules(Arc& a, int tagId, bool driving) { addConstraintCurveValue(a.start, a, a.startAngle, tagId, driving); return addConstraintCurveValue(a.end, a, a.endAngle, tagId, driving); } int System::addConstraintPointOnCircle(Point& p, Circle& c, int tagId, bool driving) { return addConstraintP2PDistance(p, c.center, c.rad, tagId, driving); } int System::addConstraintPointOnEllipse(Point& p, Ellipse& e, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnEllipse(p, e); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnHyperbolicArc(Point& p, ArcOfHyperbola& e, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnHyperbola(p, e); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnParabolicArc(Point& p, ArcOfParabola& e, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnParabola(p, e); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintPointOnBSpline(Point& p, BSpline& b, double* pointparam, int tagId, bool driving) { Constraint* constr = new ConstraintPointOnBSpline(p.x, pointparam, 0, b); constr->setTag(tagId); constr->setDriving(driving); addConstraint(constr); constr = new ConstraintPointOnBSpline(p.y, pointparam, 1, b); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintArcOfEllipseRules(ArcOfEllipse& a, int tagId, bool driving) { addConstraintCurveValue(a.start, a, a.startAngle, tagId, driving); return addConstraintCurveValue(a.end, a, a.endAngle, tagId, driving); } int System::addConstraintCurveValue(Point& p, Curve& a, double* u, int tagId, bool driving) { Constraint* constr = new ConstraintCurveValue(p, p.x, a, u); constr->setTag(tagId); constr->setDriving(driving); addConstraint(constr); constr = new ConstraintCurveValue(p, p.y, a, u); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintArcOfHyperbolaRules(ArcOfHyperbola& a, int tagId, bool driving) { addConstraintCurveValue(a.start, a, a.startAngle, tagId, driving); return addConstraintCurveValue(a.end, a, a.endAngle, tagId, driving); } int System::addConstraintArcOfParabolaRules(ArcOfParabola& a, int tagId, bool driving) { addConstraintCurveValue(a.start, a, a.startAngle, tagId, driving); return addConstraintCurveValue(a.end, a, a.endAngle, tagId, driving); } int System::addConstraintPointOnArc(Point& p, Arc& a, int tagId, bool driving) { return addConstraintP2PDistance(p, a.center, a.rad, tagId, driving); } int System::addConstraintPerpendicularLine2Arc(Point& p1, Point& p2, Arc& a, int tagId, bool driving) { using std::numbers::pi; addConstraintP2PCoincident(p2, a.start, tagId, driving); double dx = *(p2.x) - *(p1.x); double dy = *(p2.y) - *(p1.y); if (dx * cos(*(a.startAngle)) + dy * sin(*(a.startAngle)) > 0) { return addConstraintP2PAngle(p1, p2, a.startAngle, 0, tagId, driving); } else { return addConstraintP2PAngle(p1, p2, a.startAngle, pi, tagId, driving); } } int System::addConstraintPerpendicularArc2Line(Arc& a, Point& p1, Point& p2, int tagId, bool driving) { using std::numbers::pi; addConstraintP2PCoincident(p1, a.end, tagId, driving); double dx = *(p2.x) - *(p1.x); double dy = *(p2.y) - *(p1.y); if (dx * cos(*(a.endAngle)) + dy * sin(*(a.endAngle)) > 0) { return addConstraintP2PAngle(p1, p2, a.endAngle, 0, tagId, driving); } else { return addConstraintP2PAngle(p1, p2, a.endAngle, pi, tagId, driving); } } int System::addConstraintPerpendicularCircle2Arc(Point& center, double* radius, Arc& a, int tagId, bool driving) { using std::numbers::pi; addConstraintP2PDistance(a.start, center, radius, tagId, driving); double incrAngle = *(a.startAngle) < *(a.endAngle) ? pi / 2 : -pi / 2; double tangAngle = *a.startAngle + incrAngle; double dx = *(a.start.x) - *(center.x); double dy = *(a.start.y) - *(center.y); if (dx * cos(tangAngle) + dy * sin(tangAngle) > 0) { return addConstraintP2PAngle(center, a.start, a.startAngle, incrAngle, tagId, driving); } else { return addConstraintP2PAngle(center, a.start, a.startAngle, -incrAngle, tagId, driving); } } int System::addConstraintPerpendicularArc2Circle(Arc& a, Point& center, double* radius, int tagId, bool driving) { using std::numbers::pi; addConstraintP2PDistance(a.end, center, radius, tagId, driving); double incrAngle = *(a.startAngle) < *(a.endAngle) ? -pi / 2 : pi / 2; double tangAngle = *a.endAngle + incrAngle; double dx = *(a.end.x) - *(center.x); double dy = *(a.end.y) - *(center.y); if (dx * cos(tangAngle) + dy * sin(tangAngle) > 0) { return addConstraintP2PAngle(center, a.end, a.endAngle, incrAngle, tagId, driving); } else { return addConstraintP2PAngle(center, a.end, a.endAngle, -incrAngle, tagId, driving); } } int System::addConstraintPerpendicularArc2Arc( Arc& a1, bool reverse1, Arc& a2, bool reverse2, int tagId, bool driving ) { Point& p1 = reverse1 ? a1.start : a1.end; Point& p2 = reverse2 ? a2.end : a2.start; addConstraintP2PCoincident(p1, p2, tagId, driving); return addConstraintPerpendicular(a1.center, p1, a2.center, p2, tagId, driving); } int System::addConstraintTangent(Line& l, Circle& c, int tagId, bool driving) { return addConstraintP2LDistance(c.center, l, c.rad, tagId, driving); } int System::addConstraintTangent(Line& l, Ellipse& e, int tagId, bool driving) { Constraint* constr = new ConstraintEllipseTangentLine(l, e); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintTangent(Line& l, Arc& a, int tagId, bool driving) { return addConstraintP2LDistance(a.center, l, a.rad, tagId, driving); } int System::addConstraintTangent(Circle& c1, Circle& c2, int tagId, bool driving) { double dx = *(c2.center.x) - *(c1.center.x); double dy = *(c2.center.y) - *(c1.center.y); double d = sqrt(dx * dx + dy * dy); return addConstraintTangentCircumf( c1.center, c2.center, c1.rad, c2.rad, (d < *c1.rad || d < *c2.rad), tagId, driving ); } int System::addConstraintTangent(Arc& a1, Arc& a2, int tagId, bool driving) { double dx = *(a2.center.x) - *(a1.center.x); double dy = *(a2.center.y) - *(a1.center.y); double d = sqrt(dx * dx + dy * dy); return addConstraintTangentCircumf( a1.center, a2.center, a1.rad, a2.rad, (d < *a1.rad || d < *a2.rad), tagId, driving ); } int System::addConstraintTangent(Circle& c, Arc& a, int tagId, bool driving) { double dx = *(a.center.x) - *(c.center.x); double dy = *(a.center.y) - *(c.center.y); double d = sqrt(dx * dx + dy * dy); return addConstraintTangentCircumf( c.center, a.center, c.rad, a.rad, (d < *c.rad || d < *a.rad), tagId, driving ); } int System::addConstraintCircleRadius(Circle& c, double* radius, int tagId, bool driving) { return addConstraintEqual(c.rad, radius, tagId, driving); } int System::addConstraintArcRadius(Arc& a, double* radius, int tagId, bool driving) { return addConstraintEqual(a.rad, radius, tagId, driving); } int System::addConstraintCircleDiameter(Circle& c, double* diameter, int tagId, bool driving) { return addConstraintProportional(c.rad, diameter, 0.5, tagId, driving); } int System::addConstraintArcDiameter(Arc& a, double* diameter, int tagId, bool driving) { return addConstraintProportional(a.rad, diameter, 0.5, tagId, driving); } int System::addConstraintEqualLength(Line& l1, Line& l2, int tagId, bool driving) { Constraint* constr = new ConstraintEqualLineLength(l1, l2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintEqualRadius(Circle& c1, Circle& c2, int tagId, bool driving) { return addConstraintEqual(c1.rad, c2.rad, tagId, driving); } int System::addConstraintEqualRadii(Ellipse& e1, Ellipse& e2, int tagId, bool driving) { addConstraintEqual(e1.radmin, e2.radmin, tagId, driving); Constraint* constr = new ConstraintEqualMajorAxesConic(&e1, &e2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintEqualRadii(ArcOfHyperbola& a1, ArcOfHyperbola& a2, int tagId, bool driving) { addConstraintEqual(a1.radmin, a2.radmin, tagId, driving); Constraint* constr = new ConstraintEqualMajorAxesConic(&a1, &a2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintEqualFocus(ArcOfParabola& a1, ArcOfParabola& a2, int tagId, bool driving) { Constraint* constr = new ConstraintEqualFocalDistance(&a1, &a2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintEqualRadius(Circle& c1, Arc& a2, int tagId, bool driving) { return addConstraintEqual(c1.rad, a2.rad, tagId, driving); } int System::addConstraintEqualRadius(Arc& a1, Arc& a2, int tagId, bool driving) { return addConstraintEqual(a1.rad, a2.rad, tagId, driving); } int System::addConstraintP2PSymmetric(Point& p1, Point& p2, Line& l, int tagId, bool driving) { addConstraintPerpendicular(p1, p2, l.p1, l.p2, tagId, driving); return addConstraintMidpointOnLine(p1, p2, l.p1, l.p2, tagId, driving); } int System::addConstraintP2PSymmetric(Point& p1, Point& p2, Point& p, int tagId, bool driving) { addConstraintPointOnPerpBisector(p, p1, p2, tagId, driving); return addConstraintPointOnLine(p, p1, p2, tagId, driving); } int System::addConstraintSnellsLaw( Curve& ray1, Curve& ray2, Curve& boundary, Point p, double* n1, double* n2, bool flipn1, bool flipn2, int tagId, bool driving ) { Constraint* constr = new ConstraintSnell(ray1, ray2, boundary, p, n1, n2, flipn1, flipn2); constr->setTag(tagId); constr->setDriving(driving); return addConstraint(constr); } int System::addConstraintInternalAlignmentPoint2Ellipse( Ellipse& e, Point& p1, InternalAlignmentType alignmentType, int tagId, bool driving ) { Constraint* constr = new ConstraintInternalAlignmentPoint2Ellipse(e, p1, alignmentType); constr->setTag(tagId); constr->setDriving(driving); constr->setInternalAlignment(Constraint::Alignment::InternalAlignment); return addConstraint(constr); } int System::addConstraintInternalAlignmentPoint2Hyperbola( Hyperbola& e, Point& p1, InternalAlignmentType alignmentType, int tagId, bool driving ) { Constraint* constr = new ConstraintInternalAlignmentPoint2Hyperbola(e, p1, alignmentType); constr->setTag(tagId); constr->setDriving(driving); constr->setInternalAlignment(Constraint::Alignment::InternalAlignment); return addConstraint(constr); } int System::addConstraintInternalAlignmentEllipseMajorDiameter( Ellipse& e, Point& p1, Point& p2, int tagId, bool driving ) { double X_1 = *p1.x; double Y_1 = *p1.y; double X_2 = *p2.x; double Y_2 = *p2.y; double X_c = *e.center.x; double Y_c = *e.center.y; double X_F1 = *e.focus1.x; double Y_F1 = *e.focus1.y; double b = *e.radmin; double closertopositivemajor = pow(X_1 - X_c - (X_F1 - X_c) * sqrt(pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(X_2 - X_c - (X_F1 - X_c) * sqrt(pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) + pow(Y_1 - Y_c - (Y_F1 - Y_c) * sqrt(pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(Y_2 - Y_c - (Y_F1 - Y_c) * sqrt(pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2); if (closertopositivemajor > 0) { // p2 is closer to positivemajor. Assign constraints back-to-front. addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipsePositiveMajorX, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipsePositiveMajorY, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipseNegativeMajorX, tagId, driving); return addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipseNegativeMajorY, tagId, driving); } else { // p1 is closer to positivemajor addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipsePositiveMajorX, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipsePositiveMajorY, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipseNegativeMajorX, tagId, driving); return addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipseNegativeMajorY, tagId, driving); } } int System::addConstraintInternalAlignmentEllipseMinorDiameter( Ellipse& e, Point& p1, Point& p2, int tagId, bool driving ) { double X_1 = *p1.x; double Y_1 = *p1.y; double X_2 = *p2.x; double Y_2 = *p2.y; double X_c = *e.center.x; double Y_c = *e.center.y; double X_F1 = *e.focus1.x; double Y_F1 = *e.focus1.y; double b = *e.radmin; double closertopositiveminor = pow(X_1 - X_c + b * (Y_F1 - Y_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(X_2 - X_c + b * (Y_F1 - Y_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) + pow(-Y_1 + Y_c + b * (X_F1 - X_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(-Y_2 + Y_c + b * (X_F1 - X_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2); if (closertopositiveminor > 0) { addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipsePositiveMinorX, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipsePositiveMinorY, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipseNegativeMinorX, tagId, driving); return addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipseNegativeMinorY, tagId, driving); } else { addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipsePositiveMinorX, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipsePositiveMinorY, tagId, driving); addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipseNegativeMinorX, tagId, driving); return addConstraintInternalAlignmentPoint2Ellipse(e, p2, EllipseNegativeMinorY, tagId, driving); } } int System::addConstraintInternalAlignmentEllipseFocus1(Ellipse& e, Point& p1, int tagId, bool driving) { addConstraintEqual(e.focus1.x, p1.x, tagId, driving, Constraint::Alignment::InternalAlignment); return addConstraintEqual(e.focus1.y, p1.y, tagId, driving, Constraint::Alignment::InternalAlignment); } int System::addConstraintInternalAlignmentEllipseFocus2(Ellipse& e, Point& p1, int tagId, bool driving) { addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipseFocus2X, tagId, driving); return addConstraintInternalAlignmentPoint2Ellipse(e, p1, EllipseFocus2Y, tagId, driving); } int System::addConstraintInternalAlignmentHyperbolaMajorDiameter( Hyperbola& e, Point& p1, Point& p2, int tagId, bool driving ) { double X_1 = *p1.x; double Y_1 = *p1.y; double X_2 = *p2.x; double Y_2 = *p2.y; double X_c = *e.center.x; double Y_c = *e.center.y; double X_F1 = *e.focus1.x; double Y_F1 = *e.focus1.y; double b = *e.radmin; double closertopositivemajor = pow(-X_1 + X_c + (X_F1 - X_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(-X_2 + X_c + (X_F1 - X_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) + pow(-Y_1 + Y_c + (Y_F1 - Y_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(-Y_2 + Y_c + (Y_F1 - Y_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2); if (closertopositivemajor > 0) { // p2 is closer to positivemajor. Assign constraints back-to-front. addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaPositiveMajorX, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaPositiveMajorY, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaNegativeMajorX, tagId, driving); return addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaNegativeMajorY, tagId, driving); } else { // p1 is closer to positivemajor addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaPositiveMajorX, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaPositiveMajorY, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaNegativeMajorX, tagId, driving); return addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaNegativeMajorY, tagId, driving); } } int System::addConstraintInternalAlignmentHyperbolaMinorDiameter( Hyperbola& e, Point& p1, Point& p2, int tagId, bool driving ) { double X_1 = *p1.x; double Y_1 = *p1.y; double X_2 = *p2.x; double Y_2 = *p2.y; double X_c = *e.center.x; double Y_c = *e.center.y; double X_F1 = *e.focus1.x; double Y_F1 = *e.focus1.y; double b = *e.radmin; double closertopositiveminor = pow(-X_1 + X_c + b * (Y_F1 - Y_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) + (X_F1 - X_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(-X_2 + X_c + b * (Y_F1 - Y_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) + (X_F1 - X_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) + pow(-Y_1 + Y_c - b * (X_F1 - X_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) + (Y_F1 - Y_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2) - pow(-Y_2 + Y_c - b * (X_F1 - X_c) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) + (Y_F1 - Y_c) * (-pow(b, 2) + pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)) / sqrt(pow(X_F1 - X_c, 2) + pow(Y_F1 - Y_c, 2)), 2); if (closertopositiveminor < 0) { addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaPositiveMinorX, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaPositiveMinorY, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaNegativeMinorX, tagId, driving); return addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaNegativeMinorY, tagId, driving); } else { addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaPositiveMinorX, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p1, HyperbolaPositiveMinorY, tagId, driving); addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaNegativeMinorX, tagId, driving); return addConstraintInternalAlignmentPoint2Hyperbola(e, p2, HyperbolaNegativeMinorY, tagId, driving); } } int System::addConstraintInternalAlignmentHyperbolaFocus(Hyperbola& e, Point& p1, int tagId, bool driving) { addConstraintEqual(e.focus1.x, p1.x, tagId, driving, Constraint::Alignment::InternalAlignment); return addConstraintEqual(e.focus1.y, p1.y, tagId, driving, Constraint::Alignment::InternalAlignment); } int System::addConstraintInternalAlignmentParabolaFocus(Parabola& e, Point& p1, int tagId, bool driving) { addConstraintEqual(e.focus1.x, p1.x, tagId, driving, Constraint::Alignment::InternalAlignment); return addConstraintEqual(e.focus1.y, p1.y, tagId, driving, Constraint::Alignment::InternalAlignment); } int System::addConstraintInternalAlignmentBSplineControlPoint( BSpline& b, Circle& c, unsigned int poleindex, int tagId, bool driving ) { addConstraintEqual( b.poles[poleindex].x, c.center.x, tagId, driving, Constraint::Alignment::InternalAlignment ); addConstraintEqual( b.poles[poleindex].y, c.center.y, tagId, driving, Constraint::Alignment::InternalAlignment ); return addConstraintEqual( b.weights[poleindex], c.rad, tagId, driving, Constraint::Alignment::InternalAlignment ); } int System::addConstraintInternalAlignmentKnotPoint( BSpline& b, Point& p, unsigned int knotindex, int tagId, bool driving ) { if (b.periodic && knotindex == 0) { // This is done here since knotpoints themselves aren't stored addConstraintP2PCoincident(p, b.start, tagId, driving); addConstraintP2PCoincident(p, b.end, tagId, driving); } size_t numpoles = b.degree - b.mult[knotindex] + 1; if (numpoles == 0) { numpoles = 1; } // `startpole` is the first pole affecting the knot with `knotindex` size_t startpole = 0; std::vector pvec; pvec.push_back(p.x); std::vector factors(numpoles, 1.0 / numpoles); // Only poles with indices `[i, i+1,... i+b.degree]` affect the interval // `flattenedknots[b.degree+i]` to `flattenedknots[b.degree+i+1]`. // When a knot has higher multiplicity, it can be seen as spanning // multiple of these intervals, and thus is only affected by an // intersection of the poles that affect it. // The `knotindex` gives us the intervals, so work backwards and find // the affecting poles. // Note that this works also for periodic B-splines, just that the poles wrap around if needed. for (size_t j = 1; j <= knotindex; ++j) { startpole += b.mult[j]; } // For the last knot, even the upper limit of the last interval range // is included. So offset for that. // For periodic B-splines the `flattenedknots` are defined differently, // so this is not needed for them. if (!b.periodic && startpole >= b.poles.size()) { startpole = b.poles.size() - 1; } // Calculate the factors to be passed to weighted linear combination constraint. // The if condition has a small performance benefit, but that is not why it is here. // One case when numpoles <= 1 is for the last knot of a non-periodic B-spline. // In this case, the interval `k` passed to `getLinCombFactor` is degenerate, and this is the // cleanest way to handle it. if (numpoles > 1) { for (size_t i = 0; i < numpoles; ++i) { factors[i] = b.getLinCombFactor(*(b.knots[knotindex]), startpole + b.degree, startpole + i); } } // The mod operation is to adjust for periodic B-splines. // This can be separated for performance reasons but it will be less readable. for (size_t i = 0; i < numpoles; ++i) { pvec.push_back(b.poles[(startpole + i) % b.poles.size()].x); } for (size_t i = 0; i < numpoles; ++i) { pvec.push_back(b.weights[(startpole + i) % b.poles.size()]); } Constraint* constr = new ConstraintWeightedLinearCombination(numpoles, pvec, factors); constr->setTag(tagId); constr->setDriving(driving); constr->setInternalAlignment(Constraint::Alignment::InternalAlignment); addConstraint(constr); pvec.clear(); pvec.push_back(p.y); for (size_t i = 0; i < numpoles; ++i) { pvec.push_back(b.poles[(startpole + i) % b.poles.size()].y); } for (size_t i = 0; i < numpoles; ++i) { pvec.push_back(b.weights[(startpole + i) % b.poles.size()]); } constr = new ConstraintWeightedLinearCombination(numpoles, pvec, factors); constr->setTag(tagId); constr->setDriving(driving); constr->setInternalAlignment(Constraint::Alignment::InternalAlignment); return addConstraint(constr); } // calculates angle between two curves at point of their intersection p. If two // points are supplied, p is used for first curve and p2 for second, yielding a // remote angle computation (this is useful when the endpoints haven't) been // made coincident yet double System::calculateAngleViaPoint(const Curve& crv1, const Curve& crv2, Point& p) const { return calculateAngleViaPoint(crv1, crv2, p, p); } double System::calculateAngleViaPoint(const Curve& crv1, const Curve& crv2, Point& p1, Point& p2) const { GCS::DeriVector2 n1 = crv1.CalculateNormal(p1); GCS::DeriVector2 n2 = crv2.CalculateNormal(p2); return atan2(-n2.x * n1.y + n2.y * n1.x, n2.x * n1.x + n2.y * n1.y); } double System::calculateAngleViaParams( const Curve& crv1, const Curve& crv2, double* param1, double* param2 ) const { GCS::DeriVector2 n1 = crv1.CalculateNormal(param1); GCS::DeriVector2 n2 = crv2.CalculateNormal(param2); return atan2(-n2.x * n1.y + n2.y * n1.x, n2.x * n1.x + n2.y * n1.y); } void System::calculateNormalAtPoint(const Curve& crv, const Point& p, double& rtnX, double& rtnY) const { GCS::DeriVector2 n1 = crv.CalculateNormal(p); rtnX = n1.x; rtnY = n1.y; } double System::calculateConstraintErrorByTag(int tagId) { int cnt = 0; // how many constraints have been accumulated double sqErr = 0.0; // accumulator of squared errors double err = 0.0; // last computed signed error value for (const auto& constr : clist) { if (constr->getTag() == tagId) { err = constr->error(); sqErr += err * err; cnt++; }; } switch (cnt) { case 0: // constraint not found! return std::numeric_limits::quiet_NaN(); break; case 1: return err; break; default: return sqrt(sqErr / (double)cnt); } } void System::rescaleConstraint(int id, double coeff) { if (id >= static_cast(clist.size()) || id < 0) { return; } if (clist[id]) { clist[id]->rescale(coeff); } } void System::declareUnknowns(VEC_pD& params) { plist = params; pIndex.clear(); for (int i = 0; i < int(plist.size()); ++i) { pIndex[plist[i]] = i; } hasUnknowns = true; } void System::declareDrivenParams(VEC_pD& params) { pdrivenlist = params; } void System::initSolution(Algorithm alg) { // - Stores the current parameters values in the vector "reference" // - identifies any decoupled subsystems and partitions the original // system into corresponding components // - Stores the current parameters in the vector "reference" // - Identifies the equality constraints tagged with ids >= 0 // and prepares a corresponding system reduction // - Organizes the rest of constraints into two subsystems for // tag ids >=0 and < 0 respectively and applies the // system reduction specified in the previous step isInit = false; if (!hasUnknowns) { return; } // storing reference configuration setReference(); // diagnose conflicting or redundant constraints if (!hasDiagnosis) { diagnose(alg); } // if still no diagnosis after explicitly calling `diagnose`, nothing to do here if (!hasDiagnosis) { return; } std::vector clistR; if (!redundant.empty()) { std::ranges::copy_if(clist, std::back_inserter(clistR), [this](auto constr) { return this->redundant.count(constr) == 0; }); } else { clistR = clist; } // partitioning into decoupled components Graph g; for (int i = 0; i < int(plist.size() + clistR.size()); i++) { boost::add_vertex(g); } int cvtid = int(plist.size()); for (const auto constr : clistR) { VEC_pD& cparams = c2p[constr]; for (const auto param : cparams) { MAP_pD_I::const_iterator it = pIndex.find(param); if (it != pIndex.end()) { boost::add_edge(cvtid, it->second, g); } } ++cvtid; } VEC_I components(boost::num_vertices(g)); int componentsSize = 0; if (!components.empty()) { componentsSize = boost::connected_components(g, &components[0]); } // identification of equality constraints and parameter reduction std::set reducedConstrs; // constraints that will be eliminated through reduction reductionmaps.clear(); // destroy any maps reductionmaps.resize(componentsSize); // create empty maps to be filled in { VEC_pD reducedParams = plist; for (const auto& constr : clistR) { if (!(constr->getTag() >= 0 && constr->getTypeId() == Equal)) { continue; } const auto it1 = pIndex.find(constr->params()[0]); const auto it2 = pIndex.find(constr->params()[1]); if (it1 == pIndex.end() || it2 == pIndex.end()) { continue; } reducedConstrs.insert(constr); double* p_kept = reducedParams[it1->second]; double* p_replaced = reducedParams[it2->second]; std::ranges::replace(reducedParams, p_replaced, p_kept); } for (size_t i = 0; i < plist.size(); ++i) { if (plist[i] != reducedParams[i]) { int cid = components[i]; reductionmaps[cid][plist[i]] = reducedParams[i]; } } } // TODO: Why are the later (constraint-related) items added first? // Adding plist-related items first would simplify assignment of `i`, but is not a big expense // overall. Leaving as is to avoid any unintended consequences. clists.clear(); // destroy any lists clists.resize(componentsSize); // create empty lists to be filled in size_t i = plist.size(); for (const auto& constr : clistR) { if (reducedConstrs.count(constr) == 0) { int cid = components[i]; clists[cid].push_back(constr); } ++i; } plists.clear(); // destroy any lists plists.resize(componentsSize); // create empty lists to be filled in for (size_t i = 0; i < plist.size(); ++i) { int cid = components[i]; plists[cid].push_back(plist[i]); } // calculates subSystems and subSystemsAux from clists, plists and reductionmaps clearSubSystems(); subSystems.resize(clists.size(), nullptr); subSystemsAux.resize(clists.size(), nullptr); for (std::size_t cid = 0; cid < clists.size(); ++cid) { std::vector clist0, clist1; std::ranges::partition_copy( clists[cid], std::back_inserter(clist0), std::back_inserter(clist1), [](auto constr) { return constr->getTag() >= 0 && constr->isDriving(); } ); if (!clist0.empty()) { subSystems[cid] = new SubSystem(clist0, plists[cid], reductionmaps[cid]); } if (!clist1.empty()) { subSystemsAux[cid] = new SubSystem(clist1, plists[cid], reductionmaps[cid]); } } isInit = true; } void System::setReference() { reference.clear(); reference.reserve(plist.size()); for (const auto& param : plist) { reference.push_back(*param); } } void System::resetToReference() { if (reference.size() == plist.size()) { VEC_D::const_iterator ref = reference.begin(); VEC_pD::iterator param = plist.begin(); for (; ref != reference.end(); ++ref, ++param) { **param = *ref; } } } int System::solve(VEC_pD& params, bool isFine, Algorithm alg, bool isRedundantsolving) { declareUnknowns(params); initSolution(); return solve(isFine, alg, isRedundantsolving); } int System::solve(bool isFine, Algorithm alg, bool isRedundantsolving) { if (!isInit) { return Failed; } bool isReset = false; // return success by default in order to permit coincidence constraints to be applied // even if no other system has to be solved int res = Success; for (int cid = 0; cid < int(subSystems.size()); cid++) { if ((subSystems[cid] || subSystemsAux[cid]) && !isReset) { resetToReference(); isReset = true; } if (subSystems[cid] && subSystemsAux[cid]) { res = std::max(res, solve(subSystems[cid], subSystemsAux[cid], isFine, isRedundantsolving)); } else if (subSystems[cid]) { res = std::max(res, solve(subSystems[cid], isFine, alg, isRedundantsolving)); } else if (subSystemsAux[cid]) { res = std::max(res, solve(subSystemsAux[cid], isFine, alg, isRedundantsolving)); } } if (res == Success) { for (std::set::const_iterator constr = redundant.begin(); constr != redundant.end(); ++constr) { // DeepSOIC: there used to be a comparison of signed error value to // convergence, which makes no sense. Potentially I fixed bug, and // chances are low I've broken anything. double err = (*constr)->error(); if (err * err > (isRedundantsolving ? convergenceRedundant : convergence)) { res = Converged; return res; } } } return res; } int System::solve(SubSystem* subsys, bool isFine, Algorithm alg, bool isRedundantsolving) { if (alg == BFGS) { return solve_BFGS(subsys, isFine, isRedundantsolving); } else if (alg == LevenbergMarquardt) { return solve_LM(subsys, isRedundantsolving); } else if (alg == DogLeg) { return solve_DL(subsys, isRedundantsolving); } else { return Failed; } } int System::solve_BFGS(SubSystem* subsys, bool /*isFine*/, bool isRedundantsolving) { #ifdef _GCS_EXTRACT_SOLVER_SUBSYSTEM_ extractSubsystem(subsys, isRedundantsolving); #endif int xsize = subsys->pSize(); if (xsize == 0) { return Success; } subsys->redirectParams(); Eigen::MatrixXd D = Eigen::MatrixXd::Identity(xsize, xsize); Eigen::VectorXd x(xsize); Eigen::VectorXd xdir(xsize); Eigen::VectorXd grad(xsize); Eigen::VectorXd h(xsize); Eigen::VectorXd y(xsize); Eigen::VectorXd Dy(xsize); // Initial unknowns vector and initial gradient vector subsys->getParams(x); subsys->calcGrad(grad); // Initial search direction opposed to gradient (steepest-descent) xdir = -grad; lineSearch(subsys, xdir); double err = subsys->error(); h = x; subsys->getParams(x); h = x - h; // = x - xold // double convergence = isFine ? convergence : XconvergenceRough; int maxIterNumber = (sketchSizeMultiplier ? maxIter * xsize : maxIter); double convCriterion = convergence; if (isRedundantsolving) { maxIterNumber = (sketchSizeMultiplierRedundant ? maxIterRedundant * xsize : maxIterRedundant); convCriterion = convergenceRedundant; } if (debugMode == IterationLevel) { std::stringstream stream; stream << "BFGS: convergence: " << convCriterion << ", xsize: " << xsize << ", maxIter: " << maxIterNumber << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } double divergingLim = 1e6 * err + 1e12; double h_norm {}; for (int iter = 1; iter < maxIterNumber; ++iter) { h_norm = h.norm(); if (h_norm <= convCriterion || err <= smallF) { if (debugMode == IterationLevel) { std::stringstream stream; stream << "BFGS Converged!!: " << ", err: " << err << ", h_norm: " << h_norm << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } break; } if (err > divergingLim || err != err) { // check for diverging and NaN if (debugMode == IterationLevel) { std::stringstream stream; stream << "BFGS Failed: Diverging!!: " << ", err: " << err << ", divergingLim: " << divergingLim << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } break; } y = grad; subsys->calcGrad(grad); y = grad - y; // = grad - gradold double hty = h.dot(y); // make sure that hty is never 0 if (hty == 0) { hty = .0000000001; } Dy = D * y; double ytDy = y.dot(Dy); // Now calculate the BFGS update on D D += (1. + ytDy / hty) / hty * h * h.transpose(); D -= 1. / hty * (h * Dy.transpose() + Dy * h.transpose()); xdir = -D * grad; lineSearch(subsys, xdir); err = subsys->error(); h = x; subsys->getParams(x); h = x - h; // = x - xold if (debugMode == IterationLevel) { std::stringstream stream; stream << "BFGS, Iteration: " << iter << ", err: " << err << ", h_norm: " << h_norm << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } } subsys->revertParams(); if (err <= smallF) { return Success; } if (h.norm() <= convCriterion) { return Converged; } return Failed; } int System::solve_LM(SubSystem* subsys, bool isRedundantsolving) { #ifdef _GCS_EXTRACT_SOLVER_SUBSYSTEM_ extractSubsystem(subsys, isRedundantsolving); #endif int xsize = subsys->pSize(); int csize = subsys->cSize(); if (xsize == 0) { return Success; } Eigen::VectorXd e(csize), e_new(csize); // vector of all function errors (every constraint is one function) Eigen::MatrixXd J(csize, xsize); // Jacobi of the subsystem Eigen::MatrixXd A(xsize, xsize); Eigen::VectorXd x(xsize), h(xsize), x_new(xsize), g(xsize), diag_A(xsize); subsys->redirectParams(); subsys->getParams(x); subsys->calcResidual(e); e *= -1; int maxIterNumber = (sketchSizeMultiplier ? maxIter * xsize : maxIter); double divergingLim = 1e6 * e.squaredNorm() + 1e12; double eps = LM_eps; double eps1 = LM_eps1; double tau = LM_tau; if (isRedundantsolving) { maxIterNumber = (sketchSizeMultiplierRedundant ? maxIterRedundant * xsize : maxIterRedundant); eps = LM_epsRedundant; eps1 = LM_eps1Redundant; tau = LM_tauRedundant; } if (debugMode == IterationLevel) { std::stringstream stream; stream << "LM: eps: " << eps << ", eps1: " << eps1 << ", tau: " << tau << ", convergence: " << (isRedundantsolving ? convergenceRedundant : convergence) << ", xsize: " << xsize << ", maxIter: " << maxIterNumber << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } double nu = 2, mu = 0; int iter = 0, stop = 0; for (iter = 0; iter < maxIterNumber && !stop; ++iter) { // check error double err = e.squaredNorm(); if (err <= eps * eps) { // error is small, Success stop = 1; break; } else if (err > divergingLim || err != err) { // check for diverging and NaN stop = 6; break; } // J^T J, J^T e subsys->calcJacobi(J); A = J.transpose() * J; g = J.transpose() * e; // Compute ||J^T e||_inf double g_inf = g.lpNorm(); diag_A = A.diagonal(); // save diagonal entries so that augmentation can be later canceled // check for convergence if (g_inf <= eps1) { stop = 2; break; } // compute initial damping factor if (iter == 0) { mu = tau * diag_A.lpNorm(); } double h_norm {}; // determine increment using adaptive damping int k = 0; while (k < 50) { // augment normal equations A = A+uI for (int i = 0; i < xsize; ++i) { A(i, i) += mu; } // solve augmented functions A*h=-g h = A.fullPivLu().solve(g); double rel_error = (A * h - g).norm() / g.norm(); // check if solving works if (rel_error < 1e-5) { // restrict h according to maxStep double scale = subsys->maxStep(h); if (scale < 1.) { h *= scale; } // compute par's new estimate and ||d_par||^2 x_new = x + h; h_norm = h.squaredNorm(); constexpr double epsilon = std::numeric_limits::epsilon(); if (h_norm <= eps1 * eps1 * x.norm()) { // relative change in p is small, stop stop = 3; break; } else if (h_norm >= (x.norm() + eps1) / (epsilon * epsilon)) { // almost singular stop = 4; break; } subsys->setParams(x_new); subsys->calcResidual(e_new); e_new *= -1; double dF = e.squaredNorm() - e_new.squaredNorm(); double dL = h.dot(mu * h + g); if (dF > 0. && dL > 0.) { // reduction in error, increment is accepted double tmp = 2 * dF / dL - 1.; mu *= std::max(1. / 3., 1. - tmp * tmp * tmp); nu = 2; // update par's estimate x = x_new; e = e_new; break; } } // if this point is reached, either the linear system could not be solved or // the error did not reduce; in any case, the increment must be rejected mu *= nu; nu *= 2.0; for (int i = 0; i < xsize; ++i) { // restore diagonal J^T J entries A(i, i) = diag_A(i); } k++; } if (k > 50) { stop = 7; break; } if (debugMode == IterationLevel) { std::stringstream stream; // Iteration: 1, residual: 1e-3, tolg: 1e-5, tolx: 1e-3 stream << "LM, Iteration: " << iter << ", err(eps): " << err << ", g_inf(eps1): " << g_inf << ", h_norm: " << h_norm << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } } if (iter >= maxIterNumber) { stop = 5; } subsys->revertParams(); return (stop == 1) ? Success : Failed; } int System::solve_DL(SubSystem* subsys, bool isRedundantsolving) { #ifdef _GCS_EXTRACT_SOLVER_SUBSYSTEM_ extractSubsystem(subsys, isRedundantsolving); #endif int xsize = subsys->pSize(); int csize = subsys->cSize(); if (xsize == 0) { return Success; } double tolg = DL_tolg; double tolx = DL_tolx; double tolf = DL_tolf; int maxIterNumber = (sketchSizeMultiplier ? maxIter * xsize : maxIter); if (isRedundantsolving) { tolg = DL_tolgRedundant; tolx = DL_tolxRedundant; tolf = DL_tolfRedundant; maxIterNumber = (sketchSizeMultiplierRedundant ? maxIterRedundant * xsize : maxIterRedundant); } if (debugMode == IterationLevel) { std::stringstream stream; stream << "DL: tolg: " << tolg << ", tolx: " << tolx << ", tolf: " << tolf << ", convergence: " << (isRedundantsolving ? convergenceRedundant : convergence) << ", dogLegGaussStep: " << (dogLegGaussStep == FullPivLU ? "FullPivLU" : (dogLegGaussStep == LeastNormFullPivLU ? "LeastNormFullPivLU" : "LeastNormLdlt")) << ", xsize: " << xsize << ", csize: " << csize << ", maxIter: " << maxIterNumber << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } Eigen::VectorXd x(xsize), x_new(xsize); Eigen::VectorXd fx(csize), fx_new(csize); Eigen::MatrixXd Jx(csize, xsize), Jx_new(csize, xsize); Eigen::VectorXd g(xsize), h_sd(xsize), h_gn(xsize), h_dl(xsize); subsys->redirectParams(); double err; subsys->getParams(x); subsys->calcResidual(fx, err); subsys->calcJacobi(Jx); g = Jx.transpose() * (-fx); // get the infinity norm fx_inf and g_inf double g_inf = g.lpNorm(); double fx_inf = fx.lpNorm(); double divergingLim = 1e6 * err + 1e12; double delta = 0.1; double alpha = 0.; double nu = 2.; int iter = 0, stop = 0, reduce = 0; while (!stop) { // check if finished if (fx_inf <= tolf) { // Success stop = 1; break; } else if (g_inf <= tolg) { stop = 2; break; } else if (delta <= tolx * (tolx + x.norm())) { stop = 2; break; } else if (iter >= maxIterNumber) { stop = 4; break; } else if (err > divergingLim || err != err) { // check for diverging and NaN stop = 6; break; } // get the steepest descent direction alpha = g.squaredNorm() / (Jx * g).squaredNorm(); h_sd = alpha * g; // get the gauss-newton step // https://forum.freecad.org/viewtopic.php?f=10&t=12769&start=50#p106220 // https://forum.kde.org/viewtopic.php?f=74&t=129439#p346104 switch (dogLegGaussStep) { case FullPivLU: h_gn = Jx.fullPivLu().solve(-fx); break; case LeastNormFullPivLU: h_gn = Jx.adjoint() * (Jx * Jx.adjoint()).fullPivLu().solve(-fx); break; case LeastNormLdlt: h_gn = Jx.adjoint() * (Jx * Jx.adjoint()).ldlt().solve(-fx); break; } double rel_error = (Jx * h_gn + fx).norm() / fx.norm(); if (rel_error > 1e15) { break; } // compute the dogleg step if (h_gn.norm() < delta) { h_dl = h_gn; if (h_dl.norm() <= tolx * (tolx + x.norm())) { stop = 5; break; } } else if (alpha * g.norm() >= delta) { h_dl = (delta / (alpha * g.norm())) * h_sd; } else { // compute beta double beta = 0; Eigen::VectorXd b = h_gn - h_sd; double bb = (b.transpose() * b).norm(); double gb = (h_sd.transpose() * b).norm(); double c = (delta + h_sd.norm()) * (delta - h_sd.norm()); if (gb > 0) { beta = c / (gb + sqrt(gb * gb + c * bb)); } else { beta = (sqrt(gb * gb + c * bb) - gb) / bb; } // and update h_dl and dL with beta h_dl = h_sd + beta * b; } // get the new values double err_new; x_new = x + h_dl; subsys->setParams(x_new); subsys->calcResidual(fx_new, err_new); subsys->calcJacobi(Jx_new); // calculate the linear model and the update ratio double dL = err - 0.5 * (fx + Jx * h_dl).squaredNorm(); double dF = err - err_new; double rho = dL / dF; if (dF > 0 && dL > 0) { x = x_new; Jx = Jx_new; fx = fx_new; err = err_new; g = Jx.transpose() * (-fx); // get infinity norms g_inf = g.lpNorm(); fx_inf = fx.lpNorm(); } else { rho = -1; } // update delta if (fabs(rho - 1.) < 0.2 && h_dl.norm() > delta / 3. && reduce <= 0) { delta = 3 * delta; nu = 2; reduce = 0; } else if (rho < 0.25) { delta = delta / nu; nu = 2 * nu; reduce = 2; } else { reduce--; } if (debugMode == IterationLevel) { std::stringstream stream; // Iteration: 1, residual: 1e-3, tolg: 1e-5, tolx: 1e-3 stream << "DL, Iteration: " << iter << ", fx_inf(tolf): " << fx_inf << ", g_inf(tolg): " << g_inf << ", delta(f(tolx)): " << delta << ", err(divergingLim): " << err << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } // count this iteration and start again iter++; } subsys->revertParams(); if (debugMode == IterationLevel) { std::stringstream stream; stream << "DL: stopcode: " << stop << ((stop == 1) ? ", Success" : ", Failed") << "\n"; const std::string tmp = stream.str(); Base::Console().log(tmp.c_str()); } return (stop == 1) ? Success : Failed; } #ifdef _GCS_EXTRACT_SOLVER_SUBSYSTEM_ void System::extractSubsystem(SubSystem* subsys, bool isRedundantsolving) { VEC_pD plistout; // std::vector std::vector clist_; VEC_pD clist_params_; subsys->getParamList(plistout); subsys->getConstraintList(clist_); std::ofstream subsystemfile; subsystemfile.open("subsystemfile.txt", std::ofstream::out | std::ofstream::app); subsystemfile << std::endl; subsystemfile << std::endl; subsystemfile << "Solving: " << (isRedundantsolving ? "Redundant" : "Normal") << " Subsystem Dump starts here................................" << std::endl; int ip = 0; subsystemfile << "GCS::VEC_pD plist_;" << std::endl; // all SYSTEM params subsystemfile << "std::vector clist_;" << std::endl; // SUBSYSTEM constraints subsystemfile << "GCS::VEC_pD plistsub_;" << std::endl; // all SUBSYSTEM params // constraint params not within SYSTEM params subsystemfile << "GCS::VEC_pD clist_params_;" << std::endl; // these are all the parameters, including those not in the subsystem to // which constraints in the subsystem may make reference. for (VEC_pD::iterator it = plist.begin(); it != plist.end(); ++it, ++ip) { subsystemfile << "plist_.push_back(new double(" << *(*it) << ")); // " << ip << " address: " << (void*)(*it) << std::endl; } int ips = 0; for (VEC_pD::iterator it = plistout.begin(); it != plistout.end(); ++it, ++ips) { VEC_pD::iterator p = std::ranges::find(plist, (*it)); size_t p_index = std::distance(plist.begin(), p); if (p_index == plist.size()) { subsystemfile << "// Error: Subsystem parameter not in system params" << "address: " << (void*)(*it) << std::endl; } subsystemfile << "plistsub_.push_back(plist_[" << p_index << "]); // " << ips << std::endl; } int ic = 0; // constraint index int icp = 0; // index of constraint params not within SYSTEM params for (std::vector::iterator it = clist_.begin(); it != clist_.end(); ++it, ++ic) { switch ((*it)->getTypeId()) { case Equal: { // 2 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } subsystemfile << "clist_.push_back(new ConstraintEqual(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]," << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "])); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << std::endl; break; } case Difference: { // 3 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } subsystemfile << "clist_.push_back(new ConstraintDifference(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]," << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]," << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "])); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << std::endl; break; } case P2PDistance: { // 5 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } subsystemfile << "ConstraintP2PDistance * c" << ic << "=new ConstraintP2PDistance();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << std::endl; break; } case P2PAngle: { // 5 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } subsystemfile << "ConstraintP2PAngle * c" << ic << "=new ConstraintP2PAngle();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << std::endl; break; } case P2LDistance: { // 7 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); VEC_pD::iterator p7 = std::ranges::find(plist, (*it)->pvec[6]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); size_t i7 = std::distance(plist.begin(), p7); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } bool npb7 = false; VEC_pD::iterator np7 = std::ranges::find(clist_params_, (*it)->pvec[6]); size_t ni7 = std::distance(clist_params_.begin(), np7); if (i7 == plist.size()) { if (ni7 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[6]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[6]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[6] << std::endl; icp++; } npb7 = true; } subsystemfile << "ConstraintP2LDistance * c" << ic << "=new ConstraintP2LDistance();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb7 ? ("clist_params_[") : ("plist_[")) << (npb7 ? ni7 : i7) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << "," << (*it)->pvec[6] << std::endl; break; } case PointOnLine: { // 6 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } subsystemfile << "ConstraintPointOnLine * c" << ic << "=new ConstraintPointOnLine();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << std::endl; break; } case PointOnPerpBisector: { // 6 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } subsystemfile << "ConstraintPointOnPerpBisector * c" << ic << "=new ConstraintPointOnPerpBisector();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << std::endl; break; } case Parallel: { // 8 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); VEC_pD::iterator p7 = std::ranges::find(plist, (*it)->pvec[6]); VEC_pD::iterator p8 = std::ranges::find(plist, (*it)->pvec[7]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); size_t i7 = std::distance(plist.begin(), p7); size_t i8 = std::distance(plist.begin(), p8); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } bool npb7 = false; VEC_pD::iterator np7 = std::ranges::find(clist_params_, (*it)->pvec[6]); size_t ni7 = std::distance(clist_params_.begin(), np7); if (i7 == plist.size()) { if (ni7 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[6]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[6]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[6] << std::endl; icp++; } npb7 = true; } bool npb8 = false; VEC_pD::iterator np8 = std::ranges::find(clist_params_, (*it)->pvec[7]); size_t ni8 = std::distance(clist_params_.begin(), np8); if (i8 == plist.size()) { if (ni8 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[7]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[7]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[7] << std::endl; icp++; } npb8 = true; } subsystemfile << "ConstraintParallel * c" << ic << "=new ConstraintParallel();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb7 ? ("clist_params_[") : ("plist_[")) << (npb7 ? ni7 : i7) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb8 ? ("clist_params_[") : ("plist_[")) << (npb8 ? ni8 : i8) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << "," << (*it)->pvec[6] << "," << (*it)->pvec[7] << std::endl; break; } case Perpendicular: { // 8 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); VEC_pD::iterator p7 = std::ranges::find(plist, (*it)->pvec[6]); VEC_pD::iterator p8 = std::ranges::find(plist, (*it)->pvec[7]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); size_t i7 = std::distance(plist.begin(), p7); size_t i8 = std::distance(plist.begin(), p8); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } bool npb7 = false; VEC_pD::iterator np7 = std::ranges::find(clist_params_, (*it)->pvec[6]); size_t ni7 = std::distance(clist_params_.begin(), np7); if (i7 == plist.size()) { if (ni7 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[6]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[6]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[6] << std::endl; icp++; } npb7 = true; } bool npb8 = false; VEC_pD::iterator np8 = std::ranges::find(clist_params_, (*it)->pvec[7]); size_t ni8 = std::distance(clist_params_.begin(), np8); if (i8 == plist.size()) { if (ni8 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[7]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[7]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[7] << std::endl; icp++; } npb8 = true; } subsystemfile << "ConstraintPerpendicular * c" << ic << "=new ConstraintPerpendicular();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb7 ? ("clist_params_[") : ("plist_[")) << (npb7 ? ni7 : i7) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb8 ? ("clist_params_[") : ("plist_[")) << (npb8 ? ni8 : i8) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << "," << (*it)->pvec[6] << "," << (*it)->pvec[7] << std::endl; break; } case L2LAngle: { // 9 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); VEC_pD::iterator p7 = std::ranges::find(plist, (*it)->pvec[6]); VEC_pD::iterator p8 = std::ranges::find(plist, (*it)->pvec[7]); VEC_pD::iterator p9 = std::ranges::find(plist, (*it)->pvec[8]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); size_t i7 = std::distance(plist.begin(), p7); size_t i8 = std::distance(plist.begin(), p8); size_t i9 = std::distance(plist.begin(), p9); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } bool npb7 = false; VEC_pD::iterator np7 = std::ranges::find(clist_params_, (*it)->pvec[6]); size_t ni7 = std::distance(clist_params_.begin(), np7); if (i7 == plist.size()) { if (ni7 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[6]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[6]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[6] << std::endl; icp++; } npb7 = true; } bool npb8 = false; VEC_pD::iterator np8 = std::ranges::find(clist_params_, (*it)->pvec[7]); size_t ni8 = std::distance(clist_params_.begin(), np8); if (i8 == plist.size()) { if (ni8 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[7]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[7]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[7] << std::endl; icp++; } npb8 = true; } bool npb9 = false; VEC_pD::iterator np9 = std::ranges::find(clist_params_, (*it)->pvec[8]); size_t ni9 = std::distance(clist_params_.begin(), np9); if (i9 == plist.size()) { if (ni9 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[8]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[8]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[8] << std::endl; icp++; } npb9 = true; } subsystemfile << "ConstraintL2LAngle * c" << ic << "=new ConstraintL2LAngle();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb7 ? ("clist_params_[") : ("plist_[")) << (npb7 ? ni7 : i7) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb8 ? ("clist_params_[") : ("plist_[")) << (npb8 ? ni8 : i8) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb9 ? ("clist_params_[") : ("plist_[")) << (npb9 ? ni9 : i9) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << "," << (*it)->pvec[6] << "," << (*it)->pvec[7] << "," << (*it)->pvec[8] << std::endl; break; } case MidpointOnLine: { // 8 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); VEC_pD::iterator p7 = std::ranges::find(plist, (*it)->pvec[6]); VEC_pD::iterator p8 = std::ranges::find(plist, (*it)->pvec[7]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); size_t i7 = std::distance(plist.begin(), p7); size_t i8 = std::distance(plist.begin(), p8); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } bool npb7 = false; VEC_pD::iterator np7 = std::ranges::find(clist_params_, (*it)->pvec[6]); size_t ni7 = std::distance(clist_params_.begin(), np7); if (i7 == plist.size()) { if (ni7 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[6]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[6]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[6] << std::endl; icp++; } npb7 = true; } bool npb8 = false; VEC_pD::iterator np8 = std::ranges::find(clist_params_, (*it)->pvec[7]); size_t ni8 = std::distance(clist_params_.begin(), np8); if (i8 == plist.size()) { if (ni8 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[7]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[7]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[7] << std::endl; icp++; } npb8 = true; } subsystemfile << "ConstraintMidpointOnLine * c" << ic << "=new ConstraintMidpointOnLine();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb7 ? ("clist_params_[") : ("plist_[")) << (npb7 ? ni7 : i7) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb8 ? ("clist_params_[") : ("plist_[")) << (npb8 ? ni8 : i8) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << "," << (*it)->pvec[6] << "," << (*it)->pvec[7] << std::endl; break; } case TangentCircumf: { // 6 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } subsystemfile << "ConstraintTangentCircumf * c" << ic << "=new ConstraintTangentCircumf(" << (static_cast(*it)->getInternal() ? "true" : "false") << ");" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << std::endl; break; } case PointOnEllipse: { // 7 VEC_pD::iterator p1 = std::ranges::find(plist, (*it)->pvec[0]); VEC_pD::iterator p2 = std::ranges::find(plist, (*it)->pvec[1]); VEC_pD::iterator p3 = std::ranges::find(plist, (*it)->pvec[2]); VEC_pD::iterator p4 = std::ranges::find(plist, (*it)->pvec[3]); VEC_pD::iterator p5 = std::ranges::find(plist, (*it)->pvec[4]); VEC_pD::iterator p6 = std::ranges::find(plist, (*it)->pvec[5]); VEC_pD::iterator p7 = std::ranges::find(plist, (*it)->pvec[6]); size_t i1 = std::distance(plist.begin(), p1); size_t i2 = std::distance(plist.begin(), p2); size_t i3 = std::distance(plist.begin(), p3); size_t i4 = std::distance(plist.begin(), p4); size_t i5 = std::distance(plist.begin(), p5); size_t i6 = std::distance(plist.begin(), p6); size_t i7 = std::distance(plist.begin(), p7); bool npb1 = false; VEC_pD::iterator np1 = std::ranges::find(clist_params_, (*it)->pvec[0]); size_t ni1 = std::distance(clist_params_.begin(), np1); if (i1 == plist.size()) { if (ni1 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[0]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[0]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[0] << std::endl; icp++; } npb1 = true; } bool npb2 = false; VEC_pD::iterator np2 = std::ranges::find(clist_params_, (*it)->pvec[1]); size_t ni2 = std::distance(clist_params_.begin(), np2); if (i2 == plist.size()) { if (ni2 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[1]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[1]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[1] << std::endl; icp++; } npb2 = true; } bool npb3 = false; VEC_pD::iterator np3 = std::ranges::find(clist_params_, (*it)->pvec[2]); size_t ni3 = std::distance(clist_params_.begin(), np3); if (i3 == plist.size()) { if (ni3 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[2]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[2]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[2] << std::endl; icp++; } npb3 = true; } bool npb4 = false; VEC_pD::iterator np4 = std::ranges::find(clist_params_, (*it)->pvec[3]); size_t ni4 = std::distance(clist_params_.begin(), np4); if (i4 == plist.size()) { if (ni4 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[3]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[3]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[3] << std::endl; icp++; } npb4 = true; } bool npb5 = false; VEC_pD::iterator np5 = std::ranges::find(clist_params_, (*it)->pvec[4]); size_t ni5 = std::distance(clist_params_.begin(), np5); if (i5 == plist.size()) { if (ni5 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[4]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[4]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[4] << std::endl; icp++; } npb5 = true; } bool npb6 = false; VEC_pD::iterator np6 = std::ranges::find(clist_params_, (*it)->pvec[5]); size_t ni6 = std::distance(clist_params_.begin(), np6); if (i6 == plist.size()) { if (ni6 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[5]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[5]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[5] << std::endl; icp++; } npb6 = true; } bool npb7 = false; VEC_pD::iterator np7 = std::ranges::find(clist_params_, (*it)->pvec[6]); size_t ni7 = std::distance(clist_params_.begin(), np7); if (i7 == plist.size()) { if (ni7 == clist_params_.size()) { subsystemfile << "// Address not in System params...rebuilding into clist_params_" << std::endl; clist_params_.push_back((*it)->pvec[6]); subsystemfile << "clist_params_.push_back(new double(" << *((*it)->pvec[6]) << ")); // " << icp << " address: " << (void*)(*it)->pvec[6] << std::endl; icp++; } npb7 = true; } subsystemfile << "ConstraintPointOnEllipse * c" << ic << "=new ConstraintPointOnEllipse();" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb1 ? ("clist_params_[") : ("plist_[")) << (npb1 ? ni1 : i1) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb2 ? ("clist_params_[") : ("plist_[")) << (npb2 ? ni2 : i2) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb3 ? ("clist_params_[") : ("plist_[")) << (npb3 ? ni3 : i3) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb4 ? ("clist_params_[") : ("plist_[")) << (npb4 ? ni4 : i4) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb5 ? ("clist_params_[") : ("plist_[")) << (npb5 ? ni5 : i5) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb6 ? ("clist_params_[") : ("plist_[")) << (npb6 ? ni6 : i6) << "]);" << std::endl; subsystemfile << "c" << ic << "->pvec.push_back(" << (npb7 ? ("clist_params_[") : ("plist_[")) << (npb7 ? ni7 : i7) << "]);" << std::endl; subsystemfile << "c" << ic << "->origpvec=c" << ic << "->pvec;" << std::endl; subsystemfile << "c" << ic << "->rescale();" << std::endl; subsystemfile << "clist_.push_back(c" << ic << "); // addresses = " << (*it)->pvec[0] << "," << (*it)->pvec[1] << "," << (*it)->pvec[2] << "," << (*it)->pvec[3] << "," << (*it)->pvec[4] << "," << (*it)->pvec[5] << "," << (*it)->pvec[6] << std::endl; break; } CASE_NOT_IMP(TangentEllipseLine) CASE_NOT_IMP(InternalAlignmentPoint2Ellipse) CASE_NOT_IMP(EqualMajorAxesEllipse) CASE_NOT_IMP(EllipticalArcRangeToEndPoints) CASE_NOT_IMP(AngleViaPoint) CASE_NOT_IMP(Snell) CASE_NOT_IMP(None) } } subsystemfile.close(); } #endif // The following solver variant solves a system compound of two subsystems // treating the first of them as of higher priority than the second int System::solve(SubSystem* subsysA, SubSystem* subsysB, bool /*isFine*/, bool isRedundantsolving) { int xsizeA = subsysA->pSize(); int xsizeB = subsysB->pSize(); int csizeA = subsysA->cSize(); VEC_pD plistAB(xsizeA + xsizeB); { VEC_pD plistA, plistB; subsysA->getParamList(plistA); subsysB->getParamList(plistB); std::sort(plistA.begin(), plistA.end()); std::sort(plistB.begin(), plistB.end()); VEC_pD::const_iterator it; it = std::set_union(plistA.begin(), plistA.end(), plistB.begin(), plistB.end(), plistAB.begin()); plistAB.resize(it - plistAB.begin()); } int xsize = plistAB.size(); Eigen::MatrixXd B = Eigen::MatrixXd::Identity(xsize, xsize); Eigen::MatrixXd JA(csizeA, xsize); Eigen::MatrixXd Y, Z; Eigen::VectorXd resA(csizeA); Eigen::VectorXd lambda(csizeA), lambda0(csizeA), lambdadir(csizeA); Eigen::VectorXd x(xsize), x0(xsize), xdir(xsize), xdir1(xsize); Eigen::VectorXd grad(xsize); Eigen::VectorXd h(xsize); Eigen::VectorXd y(xsize); Eigen::VectorXd Bh(xsize); // We assume that there are no common constraints in subsysA and subsysB subsysA->redirectParams(); subsysB->redirectParams(); subsysB->getParams(plistAB, x); subsysA->getParams(plistAB, x); subsysB->setParams(plistAB, x); // just to ensure that A and B are synchronized subsysB->calcGrad(plistAB, grad); subsysA->calcJacobi(plistAB, JA); subsysA->calcResidual(resA); // double convergence = isFine ? XconvergenceFine : XconvergenceRough; int maxIterNumber = (isRedundantsolving ? (sketchSizeMultiplierRedundant ? maxIterRedundant * xsize : maxIterRedundant) : (sketchSizeMultiplier ? maxIter * xsize : maxIter)); double divergingLim = 1e6 * subsysA->error() + 1e12; double mu = 0; lambda.setZero(); for (int iter = 1; iter < maxIterNumber; iter++) { int status = qp_eq(B, grad, JA, resA, xdir, Y, Z); if (status) { break; } x0 = x; lambda0 = lambda; lambda = Y.transpose() * (B * xdir + grad); lambdadir = lambda - lambda0; // line search { double eta = 0.25; double tau = 0.5; double rho = 0.5; double alpha = 1; alpha = std::min(alpha, subsysA->maxStep(plistAB, xdir)); // Eq. 18.36 mu = std::max( mu, (grad.dot(xdir) + std::max(0., 0.5 * xdir.dot(B * xdir))) / ((1. - rho) * resA.lpNorm<1>()) ); // Eq. 18.27 double f0 = subsysB->error() + mu * resA.lpNorm<1>(); // Eq. 18.29 double deriv = grad.dot(xdir) - mu * resA.lpNorm<1>(); x = x0 + alpha * xdir; subsysA->setParams(plistAB, x); subsysB->setParams(plistAB, x); subsysA->calcResidual(resA); double f = subsysB->error() + mu * resA.lpNorm<1>(); // line search, Eq. 18.28 bool first = true; while (f > f0 + eta * alpha * deriv) { if (first) { xdir1 = -Y * resA; x += xdir1; // = x0 + alpha * xdir + xdir1 subsysA->setParams(plistAB, x); subsysB->setParams(plistAB, x); subsysA->calcResidual(resA); f = subsysB->error() + mu * resA.lpNorm<1>(); if (f < f0 + eta * alpha * deriv) { break; } } alpha = tau * alpha; if (alpha < 1e-8) { // let the linesearch fail alpha = 0.; } x = x0 + alpha * xdir; subsysA->setParams(plistAB, x); subsysB->setParams(plistAB, x); subsysA->calcResidual(resA); f = subsysB->error() + mu * resA.lpNorm<1>(); if (alpha < 1e-8) { // let the linesearch fail break; } } lambda = lambda0 + alpha * lambdadir; } h = x - x0; y = grad - JA.transpose() * lambda; { subsysB->calcGrad(plistAB, grad); subsysA->calcJacobi(plistAB, JA); subsysA->calcResidual(resA); } y = grad - JA.transpose() * lambda - y; // Eq. 18.13 if (iter > 1) { double yTh = y.dot(h); if (yTh != 0) { Bh = B * h; // Now calculate the BFGS update on B B += 1. / yTh * y * y.transpose(); B -= 1. / h.dot(Bh) * (Bh * Bh.transpose()); } } double err = subsysA->error(); if (h.norm() <= (isRedundantsolving ? convergenceRedundant : convergence) && err <= smallF) { break; } if (err > divergingLim || err != err) { // check for diverging and NaN break; } } int ret; if (subsysA->error() <= smallF) { ret = Success; } else if (h.norm() <= (isRedundantsolving ? convergenceRedundant : convergence)) { ret = Converged; } else { ret = Failed; } subsysA->revertParams(); subsysB->revertParams(); return ret; } void System::applySolution() { for (int cid = 0; cid < int(subSystems.size()); cid++) { if (subSystemsAux[cid]) { subSystemsAux[cid]->applySolution(); } if (subSystems[cid]) { subSystems[cid]->applySolution(); } for (MAP_pD_pD::const_iterator it = reductionmaps[cid].begin(); it != reductionmaps[cid].end(); ++it) { *(it->first) = *(it->second); } } } void System::undoSolution() { resetToReference(); } void System::makeReducedJacobian( Eigen::MatrixXd& J, std::map& jacobianconstraintmap, GCS::VEC_pD& pdiagnoselist, std::map& tagmultiplicity ) { // construct specific parameter list for diagonose ignoring driven constraint parameters for (int j = 0; j < int(plist.size()); j++) { auto result1 = std::ranges::find(pdrivenlist, plist[j]); if (result1 == std::end(pdrivenlist)) { pdiagnoselist.push_back(plist[j]); } } J = Eigen::MatrixXd::Zero(clist.size(), pdiagnoselist.size()); int jacobianconstraintcount = 0; int allcount = 0; for (auto& constr : clist) { constr->revertParams(); ++allcount; if (constr->getTag() >= 0 && constr->isDriving()) { jacobianconstraintcount++; for (int j = 0; j < int(pdiagnoselist.size()); j++) { J(jacobianconstraintcount - 1, j) = constr->grad(pdiagnoselist[j]); } // parallel processing: create tag multiplicity map if (tagmultiplicity.find(constr->getTag()) == tagmultiplicity.end()) { tagmultiplicity[constr->getTag()] = 0; } else { tagmultiplicity[constr->getTag()]++; } jacobianconstraintmap[jacobianconstraintcount - 1] = allcount - 1; } } if (jacobianconstraintcount == 0) { // only driven constraints J.resize(0, 0); } } int System::diagnose(Algorithm alg) { // Analyses the constrainess grad of the system and provides feedback // The vector "conflictingTags" will hold a group of conflicting constraints // Hint 1: Only constraints with tag >= 0 are taken into account // Hint 2: Constraints tagged with 0 are treated as high priority // constraints and they are excluded from the returned // list of conflicting constraints. Therefore, this function // will provide no feedback about possible conflicts between // two high priority constraints. For this reason, tagging // constraints with 0 should be used carefully. hasDiagnosis = false; if (!hasUnknowns) { dofs = -1; return dofs; } #ifdef _DEBUG_TO_FILE SolverReportingManager::Manager().LogToFile("GCS::System::diagnose()\n"); #endif // Input parameters' lists: // plist => list of all the parameters of the system, e.g. each coordinate // of a point // pdrivenlist => list of the parameters that are driven by other parameters // (e.g. value of driven constraints) // When adding an external geometry or a constraint on an external geometry the array // 'plist' is empty. // So, we must abort here because otherwise we would create an invalid matrix and make // the application eventually crash. This fixes issues #0002372/#0002373. if (plist.empty() || (plist.size() - pdrivenlist.size()) == 0) { hasDiagnosis = true; emptyDiagnoseMatrix = true; dofs = 0; return dofs; } redundant.clear(); conflictingTags.clear(); redundantTags.clear(); partiallyRedundantTags.clear(); // This QR diagnosis uses a reduced Jacobian matrix to calculate the rank of the system // and identify conflicting and redundant constraints. // // reduced Jacobian matrix // The Jacobian has been reduced to: // 1. only contain driving constraints, but keep a full size (zero padded). // 2. remove the parameters of the values of driven constraints. Eigen::MatrixXd J; // maps the index of the rows of the reduced jacobian matrix (solver constraints) to // the index those constraints would have in a full size Jacobian matrix std::map jacobianconstraintmap; // list of parameters to be diagnosed in this routine (removes value parameters from driven // constraints) GCS::VEC_pD pdiagnoselist; // tag multiplicity gives the number of solver constraints associated with the same tag // A tag generally corresponds to the Sketcher constraint index - There are special tag values, // like 0 and -1. std::map tagmultiplicity; makeReducedJacobian(J, jacobianconstraintmap, pdiagnoselist, tagmultiplicity); // this function will exit with a diagnosis and, unless overridden by functions below, with full // DoFs hasDiagnosis = true; dofs = pdiagnoselist.size(); // Use DenseQR for small to medium systems to avoid SparseQR rank issues. // SparseQR is known to fail rank detection on specific geometric structures (e.g. aligned slots). // 200 parameters roughly corresponds to ~100 points/curves, covering most complex sketches // where stability is preferred over pure O(N) performance. // See: https://github.com/FreeCAD/FreeCAD/issues/10903 if (autoChooseAlgorithm) { qrAlgorithm = dofs < autoQRThreshold ? EigenDenseQR : EigenSparseQR; } // There is a legacy decision to use QR decomposition. I (abdullah) do not know all the // consideration taken in that decisions. I see that: // - QR decomposition is able to provide information about the rank and // redundant/conflicting // constraints // - The QR decomposition of J and the QR decomposition of the transpose of J are unrelated // (for reasons see below): // https://mathoverflow.net/questions/338729/translate-between-qr-decomposition-of-a-and-a-transpose // - QR is cheaper than a SVD decomposition // - QR is more expensive than a rank revealing LU factorization // - QR is less stable than SVD with respect to rank // - It is unclear whether it is possible to obtain information about redundancy with SVD // and LU // Given this legacy decision, the following is observed: // - A = QR decomposition can be used for the diagonise of dependency of the "columns" of A. // the reason is that matrix R is upper triangular with columns of A showing the // dependencies. // - The same does not apply to the "rows". // - For this reason, to enable a full diagnose of constraints, a QR decomposition must be // done // on the transpose of the Jacobian matrix (J), this is JT. // Eigen capabilities: // - If Eigen full pivoting QR decomposition is used, it is possible to track the rows of JT // during the decomposition. This can be leveraged to identify a set of independent rows // of JT (geometry) that form a rank N basis. However, because the R matrix is of the JT // decomposition and not the J decomposition, it is not possible to reduce the system to // identify exactly which rows are dependent. // - The effect is that it provides a set of parameters of geometry that are not constraint, // but it does not identify ALL geometries that are not fixed. // - If SpareQR is used, then it is not possible to track the rows of JT during // decomposition. // I do not know if it is still possible to obtain geometry information at all from // SparseQR. After several years these questions remain open: // https://stackoverflow.com/questions/49009771/getting-rows-transpositions-with-sparse-qr // https://forum.kde.org/viewtopic.php?f=74&t=151239 // // Implementation below: // // Two QR decompositions are used below. One for diagnosis of constraints and a second one // for diagnosis of parameters, i.e. to identify whether the parameter is fully constraint // (independent) or not (i.e. it is dependent). // QR decomposition method selection: SparseQR vs DenseQR #ifndef EIGEN_SPARSEQR_COMPATIBLE if (qrAlgorithm == EigenSparseQR) { Base::Console().warning( "SparseQR not supported by you current version of Eigen. It " "requires Eigen 3.2.2 or higher. Falling back to Dense QR\n" ); qrAlgorithm = EigenDenseQR; } #endif if (J.rows() == 0) { return dofs; } // From here on, presuming `J.rows() > 0`. emptyDiagnoseMatrix = false; if (qrAlgorithm == EigenDenseQR) { #ifdef PROFILE_DIAGNOSE Base::TimeElapsed DenseQR_start_time; #endif int rank = 0; // rank is not cheap to retrieve from qrJT in DenseQR Eigen::MatrixXd R; Eigen::FullPivHouseholderQR qrJT; // Here we give the system the possibility to run the two QR decompositions in parallel, // depending on the load of the system so we are using the default std::launch::async | // std::launch::deferred policy, as nobody better than the system nows if it can run the // task in parallel or is oversubscribed and should deferred it. Care to wait() for the // future before any prospective detection of conflicting/redundant, because the // redundant solve modifies pdiagnoselist and it would NOT be thread-safe. Care to call // the thread with silent=true, unless the present thread does not use Base::Console, or // the launch policy is set to std::launch::deferred policy, as it is not thread-safe to // use them in both at the same time. // // identifyDependentParametersDenseQR(J, jacobianconstraintmap, pdiagnoselist, true) // auto fut = std::async( &System::identifyDependentParametersDenseQR, this, J, jacobianconstraintmap, pdiagnoselist, true ); makeDenseQRDecomposition(J, jacobianconstraintmap, qrJT, rank, R); int paramsNum = qrJT.rows(); int constrNum = qrJT.cols(); // This function is legacy code that was used to obtain partial geometry dependency // information from a SINGLE Dense QR decomposition. I am reluctant to remove it from // here until everything new is well tested. // identifyDependentGeometryParametersInTransposedJacobianDenseQRDecomposition( qrJT, // pdiagnoselist, paramsNum, rank); fut.wait(); // wait for the execution of identifyDependentParametersSparseQR to finish dofs = paramsNum - rank; // unless overconstraint, which will be overridden below // Detecting conflicting or redundant constraints if (constrNum > rank) { // conflicting or redundant constraints int nonredundantconstrNum; identifyConflictingRedundantConstraints( alg, qrJT, jacobianconstraintmap, tagmultiplicity, pdiagnoselist, R, constrNum, rank, nonredundantconstrNum ); if (paramsNum == rank && nonredundantconstrNum > rank) { // over-constrained dofs = paramsNum - nonredundantconstrNum; } } #ifdef PROFILE_DIAGNOSE Base::TimeElapsed DenseQR_end_time; auto SolveTime = Base::TimeElapsed::diffTimeF(DenseQR_start_time, DenseQR_end_time); Base::Console().log("\nDenseQR - Lapsed Time: %f seconds\n", SolveTime); #endif } #ifdef EIGEN_SPARSEQR_COMPATIBLE else if (qrAlgorithm == EigenSparseQR) { # ifdef PROFILE_DIAGNOSE Base::TimeElapsed SparseQR_start_time; # endif int rank = 0; Eigen::MatrixXd R; Eigen::SparseQR, Eigen::COLAMDOrdering> SqrJT; // Here we give the system the possibility to run the two QR decompositions in parallel, // depending on the load of the system so we are using the default std::launch::async | // std::launch::deferred policy, as nobody better than the system nows if it can run the // task in parallel or is oversubscribed and should deferred it. Care to wait() for the // future before any prospective detection of conflicting/redundant, because the // redundant solve modifies pdiagnoselist and it would NOT be thread-safe. Care to call // the thread with silent=true, unless the present thread does not use Base::Console, or // the launch policy is set to std::launch::deferred policy, as it is not thread-safe to // use them in both at the same time. // // identifyDependentParametersSparseQR(J, jacobianconstraintmap, pdiagnoselist, true) // // Debug: // auto fut = // std::async(std::launch::deferred,&System::identifyDependentParametersSparseQR, this, // J, jacobianconstraintmap, pdiagnoselist, false); auto fut = std::async( &System::identifyDependentParametersSparseQR, this, J, jacobianconstraintmap, pdiagnoselist, /*silent=*/true ); makeSparseQRDecomposition( J, jacobianconstraintmap, SqrJT, rank, R, /*transposed=*/true, /*silent=*/false ); int paramsNum = SqrJT.rows(); int constrNum = SqrJT.cols(); fut.wait(); // wait for the execution of identifyDependentParametersSparseQR to finish dofs = paramsNum - rank; // unless overconstraint, which will be overridden below // Detecting conflicting or redundant constraints if (constrNum > rank) { int nonredundantconstrNum; identifyConflictingRedundantConstraints( alg, SqrJT, jacobianconstraintmap, tagmultiplicity, pdiagnoselist, R, constrNum, rank, nonredundantconstrNum ); if (paramsNum == rank && nonredundantconstrNum > rank) { // over-constrained dofs = paramsNum - nonredundantconstrNum; } } # ifdef PROFILE_DIAGNOSE Base::TimeElapsed SparseQR_end_time; auto SolveTime = Base::TimeElapsed::diffTimeF(SparseQR_start_time, SparseQR_end_time); Base::Console().log("\nSparseQR - Lapsed Time: %f seconds\n", SolveTime); # endif } #endif return dofs; } void System::makeDenseQRDecomposition( const Eigen::MatrixXd& J, const std::map& jacobianconstraintmap, Eigen::FullPivHouseholderQR& qrJT, int& rank, Eigen::MatrixXd& R, bool transposeJ, bool silent ) { #ifdef _GCS_DEBUG if (!silent) { SolverReportingManager::Manager().LogMatrix("J", J); } #endif #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX Eigen::MatrixXd Q; // Obtaining the Q matrix with Sparse QR is buggy, see comments below Eigen::MatrixXd R2; // Intended for a trapezoidal matrix, where R is the top triangular matrix // of the R2 trapezoidal matrix #endif // For a transposed J SJG rows are paramsNum and cols are constrNum // For a non-transposed J SJG rows are constrNum and cols are paramsNum int rowsNum = 0; int colsNum = 0; if (J.rows() > 0) { Eigen::MatrixXd JG; if (transposeJ) { JG = J.topRows(jacobianconstraintmap.size()).transpose(); } else { JG = J.topRows(jacobianconstraintmap.size()); } if (JG.rows() > 0 && JG.cols() > 0) { qrJT.compute(JG); rowsNum = qrJT.rows(); colsNum = qrJT.cols(); qrJT.setThreshold(qrpivotThreshold); rank = qrJT.rank(); if (colsNum >= rowsNum) { R = qrJT.matrixQR().triangularView(); } else { R = qrJT.matrixQR().topRows(colsNum).triangularView(); } } else { rowsNum = JG.rows(); colsNum = JG.cols(); } #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX R2 = qrJT.matrixQR(); Q = qrJT.matrixQ(); #endif } if (debugMode == IterationLevel && !silent) { SolverReportingManager::Manager().LogQRSystemInformation(*this, rowsNum, colsNum, rank); } #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX if (J.rows() > 0 && !silent) { SolverReportingManager::Manager().LogMatrix("R", R); SolverReportingManager::Manager().LogMatrix("R2", R2); SolverReportingManager::Manager().LogMatrix("Q", Q); SolverReportingManager::Manager().LogMatrix("RowTransp", qrJT.rowsTranspositions()); } #endif } #ifdef EIGEN_SPARSEQR_COMPATIBLE void System::makeSparseQRDecomposition( const Eigen::MatrixXd& J, const std::map& jacobianconstraintmap, Eigen::SparseQR, Eigen::COLAMDOrdering>& SqrJT, int& rank, Eigen::MatrixXd& R, bool transposeJ, bool silent ) { Eigen::SparseMatrix SJ; // this creation is not optimized (done using triplets) // however the time this takes is negligible compared to the // time the QR decomposition itself takes SJ = J.sparseView(); SJ.makeCompressed(); # ifdef _GCS_DEBUG if (!silent) { SolverReportingManager::Manager().LogMatrix("J", J); } # endif # ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX Eigen::MatrixXd Q; // Obtaining the Q matrix with Sparse QR is buggy, see comments below Eigen::MatrixXd R2; // Intended for a trapezoidal matrix, where R is the top triangular matrix // of the R2 trapezoidal matrix # endif // For a transposed J SJG rows are paramsNum and cols are constrNum // For a non-transposed J SJG rows are constrNum and cols are paramsNum int rowsNum = 0; int colsNum = 0; if (SJ.rows() > 0) { Eigen::SparseMatrix SJG; if (transposeJ) { SJG = SJ.topRows(jacobianconstraintmap.size()).transpose(); } else { SJG = SJ.topRows(jacobianconstraintmap.size()); } if (SJG.rows() > 0 && SJG.cols() > 0) { SqrJT.compute(SJG); // Do not ask for Q Matrix!! // At Eigen 3.2 still has a bug that this only works for square matrices // if enabled it will crash # ifdef SPARSE_Q_MATRIX Q = SqrJT.matrixQ(); // Q = QS; # endif rowsNum = SqrJT.rows(); colsNum = SqrJT.cols(); SqrJT.setPivotThreshold(qrpivotThreshold); rank = SqrJT.rank(); if (colsNum >= rowsNum) { R = SqrJT.matrixR().triangularView(); } else { R = SqrJT.matrixR().topRows(colsNum).triangularView(); } # ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX R2 = SqrJT.matrixR(); # endif } else { rowsNum = SJG.rows(); colsNum = SJG.cols(); } } if (debugMode == IterationLevel && !silent) { SolverReportingManager::Manager().LogQRSystemInformation(*this, rowsNum, colsNum, rank); } # ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX if (J.rows() > 0 && !silent) { SolverReportingManager::Manager().LogMatrix("R", R); SolverReportingManager::Manager().LogMatrix("R2", R2); # ifdef SPARSE_Q_MATRIX SolverReportingManager::Manager().LogMatrix("Q", Q); # endif } # endif //_GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX } #endif // EIGEN_SPARSEQR_COMPATIBLE void System::identifyDependentParametersDenseQR( const Eigen::MatrixXd& J, const std::map& jacobianconstraintmap, const GCS::VEC_pD& pdiagnoselist, bool silent ) { Eigen::FullPivHouseholderQR qrJ; Eigen::MatrixXd Rparams; int rank; makeDenseQRDecomposition(J, jacobianconstraintmap, qrJ, rank, Rparams, false, true); identifyDependentParameters(qrJ, Rparams, rank, pdiagnoselist, silent); } #ifdef EIGEN_SPARSEQR_COMPATIBLE void System::identifyDependentParametersSparseQR( const Eigen::MatrixXd& J, const std::map& jacobianconstraintmap, const GCS::VEC_pD& pdiagnoselist, bool silent ) { Eigen::SparseQR, Eigen::COLAMDOrdering> SqrJ; Eigen::MatrixXd Rparams; int nontransprank; makeSparseQRDecomposition( J, jacobianconstraintmap, SqrJ, nontransprank, Rparams, false, true ); // do not transpose allow one to diagnose parameters identifyDependentParameters(SqrJ, Rparams, nontransprank, pdiagnoselist, silent); } #endif template void System::identifyDependentParameters( T& qrJ, Eigen::MatrixXd& Rparams, int rank, const GCS::VEC_pD& pdiagnoselist, bool silent ) { (void)silent; // silent is only used in debug code, but it is important as Base::Console is not // thread-safe. Removes warning in non Debug mode. // int constrNum = SqrJ.rows(); // this is the other way around than for the transposed J // int paramsNum = SqrJ.cols(); eliminateNonZerosOverPivotInUpperTriangularMatrix(Rparams, rank); #ifdef _GCS_DEBUG if (!silent) { SolverReportingManager::Manager().LogMatrix("Rparams_nonzeros_over_pilot", Rparams); } #endif pDependentParametersGroups.resize(qrJ.cols() - rank); for (int j = rank; j < qrJ.cols(); j++) { for (int row = 0; row < rank; row++) { if (fabs(Rparams(row, j)) > 1e-10) { int origCol = qrJ.colsPermutation().indices()[row]; pDependentParametersGroups[j - rank].push_back(pdiagnoselist[origCol]); pDependentParameters.push_back(pdiagnoselist[origCol]); } } int origCol = qrJ.colsPermutation().indices()[j]; pDependentParametersGroups[j - rank].push_back(pdiagnoselist[origCol]); pDependentParameters.push_back(pdiagnoselist[origCol]); } #ifdef _GCS_DEBUG if (!silent) { SolverReportingManager::Manager().LogMatrix( "PermMatrix", (Eigen::MatrixXd)qrJ.colsPermutation() ); SolverReportingManager::Manager().LogGroupOfParameters( "ParameterGroups", pDependentParametersGroups ); } #endif } void System::identifyDependentGeometryParametersInTransposedJacobianDenseQRDecomposition( const Eigen::FullPivHouseholderQR& qrJT, const GCS::VEC_pD& pdiagnoselist, int paramsNum, int rank ) { // DETECTING CONSTRAINT SOLVER PARAMETERS // // NOTE: This is only true for dense QR with full pivoting, because solve parameters get // reordered. I am unable to adapt it to Sparse QR. (abdullah). See: // // https://stackoverflow.com/questions/49009771/getting-rows-transpositions-with-sparse-qr // https://forum.kde.org/viewtopic.php?f=74&t=151239 // // R (original version, not R here which is trimmed to not have empty rows) // has paramsNum rows, the first "rank" rows correspond to parameters that are constraint // Calculate the Permutation matrix from the Transposition matrix Eigen::PermutationMatrix rowPermutations; rowPermutations.setIdentity(paramsNum); // P.J.P' = Q.R see https://eigen.tuxfamily.org/dox/classEigen_1_1FullPivHouseholderQR.html const MatrixIndexType rowTranspositions = qrJT.rowsTranspositions(); for (int k = 0; k < rank; ++k) { rowPermutations.applyTranspositionOnTheRight(k, rowTranspositions.coeff(k)); } #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX std::stringstream stream; #endif // params (in the order of J) shown as independent from QR std::set indepParamCols; std::set depParamCols; for (int j = 0; j < rank; j++) { int origRow = rowPermutations.indices()[j]; indepParamCols.insert(origRow); // NOTE: Q*R = transpose(J), so the row of R corresponds to the col of J (the rows of // transpose(J)). The cols of J are the parameters, the rows are the constraints. #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX stream << "R row " << j << " = J col " << origRow << std::endl; #endif } #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX std::string tmp = stream.str(); SolverReportingManager::Manager().LogString(tmp); #endif // If not independent, must be dependent for (int j = 0; j < paramsNum; j++) { auto result = indepParamCols.find(j); if (result == indepParamCols.end()) { depParamCols.insert(j); } } #ifdef _GCS_DEBUG_SOLVER_JACOBIAN_QR_DECOMPOSITION_TRIANGULAR_MATRIX stream.flush(); stream << "Indep params: ["; for (auto indep : indepParamCols) { stream << indep; } stream << "]" << std::endl; stream << "Dep params: ["; for (auto dep : depParamCols) { stream << dep; } stream << "]" << std::endl; tmp = stream.str(); SolverReportingManager::Manager().LogString(tmp); #endif for (auto param : depParamCols) { pDependentParameters.push_back(pdiagnoselist[param]); } } void System::eliminateNonZerosOverPivotInUpperTriangularMatrix(Eigen::MatrixXd& R, int rank) { for (int i = 1; i < rank; i++) { // eliminate non zeros above pivot assert(R(i, i) != 0); for (int row = 0; row < i; row++) { if (R(row, i) != 0) { double coef = R(row, i) / R(i, i); R.block(row, i + 1, 1, R.cols() - i - 1) -= coef * R.block(i, i + 1, 1, R.cols() - i - 1); R(row, i) = 0; } } } } template void System::identifyConflictingRedundantConstraints( Algorithm alg, const T& qrJT, const std::map& jacobianconstraintmap, const std::map& tagmultiplicity, GCS::VEC_pD& pdiagnoselist, Eigen::MatrixXd& R, int constrNum, int rank, int& nonredundantconstrNum ) { eliminateNonZerosOverPivotInUpperTriangularMatrix(R, rank); std::vector> conflictGroups(constrNum - rank); for (int j = rank; j < constrNum; j++) { for (int row = 0; row < rank; row++) { if (fabs(R(row, j)) > 1e-10) { int origCol = qrJT.colsPermutation().indices()[row]; conflictGroups[j - rank].push_back(clist[jacobianconstraintmap.at(origCol)]); } } int origCol = qrJT.colsPermutation().indices()[j]; conflictGroups[j - rank].push_back(clist[jacobianconstraintmap.at(origCol)]); } // Augment the information regarding the group of constraints that are conflicting or redundant. if (debugMode == IterationLevel) { SolverReportingManager::Manager().LogGroupOfConstraints( "Analysing groups of constraints of special interest", conflictGroups ); } // try to remove the conflicting constraints and solve the // system in order to check if the removed constraints were // just redundant but not really conflicting std::set skipped; SET_I satisfiedGroups; while (1) { // conflictingMap contains all the eligible constraints of conflict groups not yet // satisfied. As groups get satisfied, the map created on every iteration is smaller, until // such time it is empty and the infinite loop is exited. The guarantee that the loop will // be exited originates from the fact that in each iteration the algorithm will select one // constraint from the conflict groups, which will satisfy at least one group. std::map conflictingMap; for (std::size_t i = 0; i < conflictGroups.size(); i++) { if (satisfiedGroups.count(i) != 0) { continue; } for (const auto& constr : conflictGroups[i]) { bool isinternalalignment = (constr->isInternalAlignment() == Constraint::Alignment::InternalAlignment); bool priorityconstraint = (constr->getTag() == 0); if (!priorityconstraint && !isinternalalignment) { // exclude constraints tagged with zero and internal alignment conflictingMap[constr].insert(i); } } } if (conflictingMap.empty()) { break; } /* This is a heuristic algorithm to propose the user which constraints from a * redundant/conflicting set should be removed. It is based on the following principles: * 1. if the current constraint is more popular than previous ones (appears in more * sets), take it. This prioritises removal of constraints that cause several * independent groups of constraints to be conflicting/redundant. It is based on the * observation that the redundancy/conflict is caused by the lesser amount of * constraints. * 2. if there is already a constraint ranking in the contest, and the current one is as * popular, prefer the constraint that removes a lesser amount of DoFs. This prioritises * removal of sketcher constraints (not solver constraints) that generates a higher * amount of solver constraints. It is based on the observation that constraints taking * a higher amount of DoFs (such as symmetry) are preferred by the user, who may not see * the redundancy of simpler ones. * 3. if there is already a constraint ranking in the context, the current one is as * popular, and they remove the same amount of DoFs, prefer removal of the latest * introduced. */ auto iterMostPopular = std::max_element( conflictingMap.begin(), conflictingMap.end(), [&tagmultiplicity](const auto& pair1, const auto& pair2) { size_t sizeOfSet1 = pair1.second.size(); size_t sizeOfSet2 = pair2.second.size(); auto tag1 = pair1.first->getTag(); auto tag2 = pair2.first->getTag(); return ( sizeOfSet2 > sizeOfSet1 // (1) || (sizeOfSet2 == sizeOfSet1 && tagmultiplicity.at(tag2) < tagmultiplicity.at(tag1)) // (2) || (sizeOfSet2 == sizeOfSet1 && tagmultiplicity.at(tag2) == tagmultiplicity.at(tag1) && tag2 > tag1) ); // (3) } ); Constraint* mostPopular = iterMostPopular->first; int maxPopularity = iterMostPopular->second.size(); if (!(maxPopularity > 0)) { continue; } // adding for skipping not only the mostPopular, but also any other constraint in the // conflicting map associated with the same tag (namely any other solver // constraint associated with the same sketcher constraint that is also conflicting) auto maxPopularityTag = mostPopular->getTag(); for (const auto& [constr, conflSet] : conflictingMap) { if (!(constr->getTag() == maxPopularityTag)) { continue; } skipped.insert(constr); std::ranges::copy(conflSet, std::inserter(satisfiedGroups, satisfiedGroups.begin())); } } // Augment information regarding the choice made by popularity contest if (debugMode == IterationLevel) { SolverReportingManager::Manager().LogSetOfConstraints("Chosen redundants", skipped); } std::vector clistTmp; clistTmp.reserve(clist.size()); std::ranges::copy_if(clist, std::back_inserter(clistTmp), [&skipped](const auto& constr) { return (constr->isDriving() && skipped.count(constr) == 0); }); SubSystem* subSysTmp = new SubSystem(clistTmp, pdiagnoselist); int res = solve(subSysTmp, true, alg, true); if (debugMode == Minimal || debugMode == IterationLevel) { std::string solvername; switch (alg) { case 0: solvername = "BFGS"; break; case 1: // solving with the LevenbergMarquardt solver solvername = "LevenbergMarquardt"; break; case 2: // solving with the BFGS solver solvername = "DogLeg"; break; } Base::Console().log("Sketcher::RedundantSolving-%s-\n", solvername.c_str()); } if (res == Success) { subSysTmp->applySolution(); std::ranges::copy_if( skipped, std::inserter(redundant, redundant.begin()), [this](const auto& constr) { double err = constr->error(); return (err * err < this->convergenceRedundant); } ); resetToReference(); if (debugMode == Minimal || debugMode == IterationLevel) { Base::Console().log("Sketcher Redundant solving: %d redundants\n", redundant.size()); } // TODO: Figure out why we need to iterate in reverse order and add explanation here. std::vector> conflictGroupsOrig = conflictGroups; conflictGroups.clear(); for (int i = conflictGroupsOrig.size() - 1; i >= 0; i--) { auto iterRedundantEntry = std::ranges::find_if( conflictGroupsOrig[i], [this](const auto item) { return (this->redundant.count(item) > 0); } ); bool hasRedundant = (iterRedundantEntry != conflictGroupsOrig[i].end()); if (!hasRedundant) { conflictGroups.push_back(conflictGroupsOrig[i]); continue; } if (debugMode == IterationLevel) { Base::Console().log( "(Partially) Redundant, Group %d, index %d, Tag: %d\n", i, iterRedundantEntry - conflictGroupsOrig[i].begin(), (*iterRedundantEntry)->getTag() ); } constrNum--; } } delete subSysTmp; // simplified output of conflicting tags SET_I conflictingTagsSet; for (const auto& cGroup : conflictGroups) { // exclude internal alignment std::ranges::transform( cGroup, std::inserter(conflictingTagsSet, conflictingTagsSet.begin()), [](const auto& constr) { bool isinternalalignment = (constr->isInternalAlignment() == Constraint::Alignment::InternalAlignment); return (isinternalalignment ? 0 : constr->getTag()); } ); } // exclude constraints tagged with zero conflictingTagsSet.erase(0); conflictingTags.resize(conflictingTagsSet.size()); std::ranges::copy(conflictingTagsSet, conflictingTags.begin()); // output of redundant tags SET_I redundantTagsSet, partiallyRedundantTagsSet; for (const auto& constr : redundant) { redundantTagsSet.insert(constr->getTag()); partiallyRedundantTagsSet.insert(constr->getTag()); } // remove tags represented at least in one non-redundant constraint for (const auto& constr : clist) { if (redundant.count(constr) == 0) { redundantTagsSet.erase(constr->getTag()); } } redundantTags.resize(redundantTagsSet.size()); std::ranges::copy(redundantTagsSet, redundantTags.begin()); for (auto r : redundantTagsSet) { partiallyRedundantTagsSet.erase(r); } partiallyRedundantTags.resize(partiallyRedundantTagsSet.size()); std::ranges::copy(partiallyRedundantTagsSet, partiallyRedundantTags.begin()); nonredundantconstrNum = constrNum; } void System::clearSubSystems() { isInit = false; deleteAllContent(subSystems); deleteAllContent(subSystemsAux); subSystems.clear(); subSystemsAux.clear(); } double lineSearch(SubSystem* subsys, Eigen::VectorXd& xdir) { double f1, f2, f3, alpha1, alpha2, alpha3, alphaStar; double alphaMax = subsys->maxStep(xdir); Eigen::VectorXd x0, x; // Save initial values subsys->getParams(x0); // Start at the initial position alpha1 = 0 alpha1 = 0.; f1 = subsys->error(); // Take a step of alpha2 = 1 alpha2 = 1.; x = x0 + alpha2 * xdir; subsys->setParams(x); f2 = subsys->error(); // Take a step of alpha3 = 2*alpha2 alpha3 = alpha2 * 2; x = x0 + alpha3 * xdir; subsys->setParams(x); f3 = subsys->error(); // Now reduce or lengthen alpha2 and alpha3 until the minimum is // Bracketed by the triplet f1>f2 f1 || f2 > f3) { if (f2 > f1) { // If f2 is greater than f1 then we shorten alpha2 and alpha3 closer to f1 // Effectively both are shortened by a factor of two. alpha3 = alpha2; f3 = f2; alpha2 = alpha2 / 2; x = x0 + alpha2 * xdir; subsys->setParams(x); f2 = subsys->error(); } else if (f2 > f3) { if (alpha3 >= alphaMax) { break; } // If f2 is greater than f3 then we increase alpha2 and alpha3 away from f1 // Effectively both are lengthened by a factor of two. alpha2 = alpha3; f2 = f3; alpha3 = alpha3 * 2; x = x0 + alpha3 * xdir; subsys->setParams(x); f3 = subsys->error(); } } // Get the alpha for the minimum f of the quadratic approximation alphaStar = alpha2 + ((alpha2 - alpha1) * (f1 - f3)) / (3 * (f1 - 2 * f2 + f3)); // Guarantee that the new alphaStar is within the bracket if (alphaStar >= alpha3 || alphaStar <= alpha1) { alphaStar = alpha2; } if (alphaStar > alphaMax) { alphaStar = alphaMax; } if (alphaStar != alphaStar) { alphaStar = 0.; } // Take a final step to alphaStar x = x0 + alphaStar * xdir; subsys->setParams(x); return alphaStar; } void deleteAllContent(VEC_pD& doublevec) { for (auto& doubleptr : doublevec) { delete doubleptr; } doublevec.clear(); } void deleteAllContent(std::vector& constrvec) { for (auto& constr : constrvec) { delete constr; } constrvec.clear(); } void deleteAllContent(std::vector& subsysvec) { for (auto& subsys : subsysvec) { delete subsys; } } } // namespace GCS