// SPDX-License-Identifier: LGPL-2.1-or-later // Wild Magic Source Code // David Eberly // http://www.geometrictools.com // Copyright (c) 1998-2007 // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. The license is available for reading at // either of the locations: // http://www.gnu.org/copyleft/lgpl.html // http://www.geometrictools.com/License/WildMagicLicense.pdf // The license applies to versions 0 through 4 of Wild Magic. // // Version: 4.0.0 (2006/06/28) #include "Wm4FoundationPCH.h" #include "Wm4PolynomialRoots.h" namespace Wm4 { //---------------------------------------------------------------------------- template PolynomialRoots::PolynomialRoots (Real fEpsilon) { assert(fEpsilon >= (Real)0.0); m_fEpsilon = fEpsilon; m_iMaxIterations = 128; m_iCount = 0; m_iMaxRoot = 4; // default support for degree <= 4 m_afRoot = WM4_NEW Real[m_iMaxRoot]; } //---------------------------------------------------------------------------- template PolynomialRoots::~PolynomialRoots () { WM4_DELETE[] m_afRoot; } //---------------------------------------------------------------------------- template int PolynomialRoots::GetCount () const { return m_iCount; } //---------------------------------------------------------------------------- template const Real* PolynomialRoots::GetRoots () const { return m_afRoot; } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetRoot (int i) const { assert(0 <= i && i < m_iCount); if (0 <= i && i < m_iCount) { return m_afRoot[i]; } return Math::MAX_REAL; } //---------------------------------------------------------------------------- template Real& PolynomialRoots::Epsilon () { return m_fEpsilon; } //---------------------------------------------------------------------------- template Real PolynomialRoots::Epsilon () const { return m_fEpsilon; } //---------------------------------------------------------------------------- template int& PolynomialRoots::MaxIterations () { return m_iMaxIterations; } //---------------------------------------------------------------------------- template int PolynomialRoots::MaxIterations () const { return m_iMaxIterations; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // degree 1 //---------------------------------------------------------------------------- template bool PolynomialRoots::FindA (Real fC0, Real fC1) { if (Math::FAbs(fC1) >= m_fEpsilon) { m_afRoot[0] = -fC0/fC1; m_iCount = 1; return true; } m_iCount = 0; return false; } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetBound (Real fC0, Real fC1) { if (Math::FAbs(fC1) <= m_fEpsilon) { // polynomial is constant, return invalid bound return -(Real)1.0; } Real fInvC1 = ((Real)1.0)/fC1; Real fMax = Math::FAbs(fC0)*fInvC1; return (Real)1.0 + fMax; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // degree 2 //---------------------------------------------------------------------------- template bool PolynomialRoots::FindA (Real fC0, Real fC1, Real fC2) { if (Math::FAbs(fC2) <= m_fEpsilon) { // polynomial is linear return FindA(fC0,fC1); } Real fDiscr = fC1*fC1 - 4.0f*fC0*fC2; if (Math::FAbs(fDiscr) <= m_fEpsilon) { fDiscr = (Real)0.0; } if (fDiscr < (Real)0.0) { m_iCount = 0; return false; } Real fTmp = ((Real)0.5)/fC2; if (fDiscr > (Real)0.0) { fDiscr = Math::Sqrt(fDiscr); m_afRoot[0] = fTmp*(-fC1 - fDiscr); m_afRoot[1] = fTmp*(-fC1 + fDiscr); m_iCount = 2; } else { m_afRoot[0] = -fTmp*fC1; m_iCount = 1; } return true; } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetBound (Real fC0, Real fC1, Real fC2) { if (Math::FAbs(fC2) <= m_fEpsilon) { // polynomial is linear return (FindA(fC0,fC1) ? m_afRoot[0] : Math::MAX_REAL); } Real fInvC2 = ((Real)1.0)/fC2; Real fTmp0 = Math::FAbs(fC0)*fInvC2; Real fTmp1 = Math::FAbs(fC1)*fInvC2; Real fMax = ( fTmp0 >= fTmp1 ? fTmp0 : fTmp1 ); return (Real)1.0 + fMax; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // degree 3 //---------------------------------------------------------------------------- template bool PolynomialRoots::FindA (Real fC0, Real fC1, Real fC2, Real fC3) { if (Math::FAbs(fC3) <= m_fEpsilon) { // polynomial is quadratic return FindA(fC0,fC1,fC2); } // make polynomial monic, x^3+c2*x^2+c1*x+c0 Real fInvC3 = ((Real)1.0)/fC3; fC0 *= fInvC3; fC1 *= fInvC3; fC2 *= fInvC3; // convert to y^3+a*y+b = 0 by x = y-c2/3 const Real fThird = (Real)1.0/(Real)3.0; const Real fTwentySeventh = (Real)1.0/(Real)27.0; Real fOffset = fThird*fC2; Real fA = fC1 - fC2*fOffset; Real fB = fC0+fC2*(((Real)2.0)*fC2*fC2-((Real)9.0)*fC1)*fTwentySeventh; Real fHalfB = ((Real)0.5)*fB; Real fDiscr = fHalfB*fHalfB + fA*fA*fA*fTwentySeventh; if (Math::FAbs(fDiscr) <= m_fEpsilon) { fDiscr = (Real)0.0; } if (fDiscr > (Real)0.0) // 1 real, 2 complex roots { fDiscr = Math::Sqrt(fDiscr); Real fTemp = -fHalfB + fDiscr; if (fTemp >= (Real)0.0) { m_afRoot[0] = Math::Pow(fTemp,fThird); } else { m_afRoot[0] = -Math::Pow(-fTemp,fThird); } fTemp = -fHalfB - fDiscr; if ( fTemp >= (Real)0.0 ) { m_afRoot[0] += Math::Pow(fTemp,fThird); } else { m_afRoot[0] -= Math::Pow(-fTemp,fThird); } m_afRoot[0] -= fOffset; m_iCount = 1; } else if (fDiscr < (Real)0.0) { const Real fSqrt3 = Math::Sqrt((Real)3.0); Real fDist = Math::Sqrt(-fThird*fA); Real fAngle = fThird*Math::ATan2(Math::Sqrt(-fDiscr), -fHalfB); Real fCos = Math::Cos(fAngle); Real fSin = Math::Sin(fAngle); m_afRoot[0] = ((Real)2.0)*fDist*fCos-fOffset; m_afRoot[1] = -fDist*(fCos+fSqrt3*fSin)-fOffset; m_afRoot[2] = -fDist*(fCos-fSqrt3*fSin)-fOffset; m_iCount = 3; } else { Real fTemp; if (fHalfB >= (Real)0.0) { fTemp = -Math::Pow(fHalfB,fThird); } else { fTemp = Math::Pow(-fHalfB,fThird); } m_afRoot[0] = ((Real)2.0)*fTemp-fOffset; m_afRoot[1] = -fTemp-fOffset; m_afRoot[2] = m_afRoot[1]; m_iCount = 3; } return true; } //---------------------------------------------------------------------------- template bool PolynomialRoots::FindE (Real fC0, Real fC1, Real fC2, Real fC3, bool bDoBalancing) { if (Math::FAbs(fC3) <= m_fEpsilon) { // polynomial is quadratic return FindA(fC0,fC1,fC2); } // make polynomial monic, x^3+c2*x^2+c1*x+c0 Real fInvC3 = ((Real)1.0)/fC3; fC0 *= fInvC3; fC1 *= fInvC3; fC2 *= fInvC3; // construct the 3-by-3 companion matrix GMatrix kMat(3,3); // initialized to zero kMat[1][0] = (Real)1.0; kMat[2][1] = (Real)1.0; kMat[0][2] = -fC0; kMat[1][2] = -fC1; kMat[2][2] = -fC2; if (bDoBalancing) { BalanceCompanion3(kMat); } return QRIteration3(kMat); } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetBound (Real fC0, Real fC1, Real fC2, Real fC3) { if (Math::FAbs(fC3) <= m_fEpsilon) { // polynomial is quadratic return GetBound(fC0,fC1,fC2); } Real fInvC3 = ((Real)1.0)/fC3; Real fMax = Math::FAbs(fC0)*fInvC3; Real fTmp = Math::FAbs(fC1)*fInvC3; if (fTmp > fMax) { fMax = fTmp; } fTmp = Math::FAbs(fC2)*fInvC3; if (fTmp > fMax) { fMax = fTmp; } return (Real)1.0 + fMax; } //---------------------------------------------------------------------------- template Real PolynomialRoots::SpecialCubic (Real fA, Real fB, Real fC) { // Solve A*r^3 + B*r = C where A > 0 and B > 0. // // Let r = D*sinh(u) where D = sqrt(4*B/(3*A)). Then // sinh(3*u) = 4*[sinh(u)]^3+3*sinh(u) = E where E = 4*C/(A*D^3). // sinh(3*u) = E has solution u = (1/3)*log(E+sqrt(E^2+1)). This // leads to sinh(u) = ((E+sqrt(E^2+1))^{1/3}-(E+sqrt(E^2+1))^{-1/3})/2. // Therefore, r = D*((E+sqrt(E^2+1))^{1/3}-(E+sqrt(E^2+1))^{-1/3})/2. const Real fThird = (Real)1.0/(Real)3.0; Real fD = Math::Sqrt(((Real)4.0)*fThird*fB/fA); Real fE = ((Real)4.0)*fC/(fA*fD*fD*fD); Real fF = Math::Pow(fE+Math::Sqrt(fE*fE+(Real)1.0),fThird); Real fRoot = ((Real)0.5)*fD*(fF-((Real)1.0)/fF); return fRoot; } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetRowNorm (int iRow, GMatrix& rkMat) { Real fNorm = Math::FAbs(rkMat[iRow][0]); for (int iCol = 1; iCol < rkMat.GetColumns(); iCol++) { Real fAbs = Math::FAbs(rkMat[iRow][iCol]); if (fAbs > fNorm) { fNorm = fAbs; } } return fNorm; } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetColNorm (int iCol, GMatrix& rkMat) { Real fNorm = Math::FAbs(rkMat[0][iCol]); for (int iRow = 1; iRow < rkMat.GetRows(); iRow++) { Real fAbs = Math::FAbs(rkMat[iRow][iCol]); if (fAbs > fNorm) { fNorm = fAbs; } } return fNorm; } //---------------------------------------------------------------------------- template void PolynomialRoots::ScaleRow (int iRow, Real fScale, GMatrix& rkMat) { for (int iCol = 0; iCol < rkMat.GetColumns(); iCol++) { rkMat[iRow][iCol] *= fScale; } } //---------------------------------------------------------------------------- template void PolynomialRoots::ScaleCol (int iCol, Real fScale, GMatrix& rkMat) { for (int iRow = 0; iRow < rkMat.GetRows(); iRow++) { rkMat[iRow][iCol] *= fScale; } } //---------------------------------------------------------------------------- template bool PolynomialRoots::IsBalanced3 (GMatrix& rkMat) { const Real fTolerance = (Real)0.001; for (int i = 0; i < 3; i++) { Real fRowNorm = GetRowNorm(i,rkMat); Real fColNorm = GetColNorm(i,rkMat); Real fTest = Math::FAbs((Real)1.0 - fColNorm/fRowNorm); if (fTest > fTolerance) { return false; } } return true; } //---------------------------------------------------------------------------- template void PolynomialRoots::Balance3 (GMatrix& rkMat) { const int iMax = 16; int i; for (i = 0; i < iMax; i++) { for (int j = 0; j < 3; j++) { Real fRowNorm = GetRowNorm(j,rkMat); Real fColNorm = GetColNorm(j,rkMat); Real fScale = Math::Sqrt(fColNorm/fRowNorm); Real fInvScale = ((Real)1.0)/fScale; ScaleRow(j,fScale,rkMat); ScaleCol(j,fInvScale,rkMat); } if (IsBalanced3(rkMat)) { break; } } assert(i < iMax); } //---------------------------------------------------------------------------- template bool PolynomialRoots::IsBalancedCompanion3 (Real fA10, Real fA21, Real fA02, Real fA12, Real fA22) { const Real fTolerance = (Real)0.001; // row/col 0 Real fRowNorm = fA02; Real fColNorm = fA10; Real fTest = Math::FAbs((Real)1.0 - fColNorm/fRowNorm); if (fTest > fTolerance) { return false; } // row/col 1 fRowNorm = (fA10 >= fA12 ? fA10 : fA12); fColNorm = fA21; fTest = Math::FAbs((Real)1.0 - fColNorm/fRowNorm); if (fTest > fTolerance) { return false; } // row/col 2 fRowNorm = (fA21 >= fA22 ? fA21 : fA22); fColNorm = (fA02 >= fA12 ? fA02 : fA12); if (fA22 > fColNorm) { fColNorm = fA22; } fTest = Math::FAbs((Real)1.0 - fColNorm/fRowNorm); return fTest <= fTolerance; } //---------------------------------------------------------------------------- template void PolynomialRoots::BalanceCompanion3 (GMatrix& rkMat) { Real fA10 = Math::FAbs(rkMat[1][0]); Real fA21 = Math::FAbs(rkMat[2][1]); Real fA02 = Math::FAbs(rkMat[0][2]); Real fA12 = Math::FAbs(rkMat[1][2]); Real fA22 = Math::FAbs(rkMat[2][2]); Real fRowNorm, fColNorm, fScale, fInvScale; const int iMax = 16; int i; for (i = 0; i < iMax; i++) { // balance row/col 0 fRowNorm = fA02; fColNorm = fA10; fScale = Math::Sqrt(fColNorm/fRowNorm); fA02 *= fScale; fA10 = fA02; // balance row/col 1 fRowNorm = (fA10 >= fA12 ? fA10 : fA12); fColNorm = fA21; fScale = Math::Sqrt(fColNorm/fRowNorm); fInvScale = ((Real)1.0)/fScale; fA10 *= fScale; fA12 *= fScale; fA21 *= fInvScale; // balance row/col 2 fRowNorm = (fA21 >= fA22 ? fA21 : fA22); fColNorm = (fA02 >= fA12 ? fA02 : fA12); if (fA22 > fColNorm) { fColNorm = fA22; } fScale = Math::Sqrt(fColNorm/fRowNorm); fInvScale = ((Real)1.0)/fScale; fA21 *= fScale; fA02 *= fInvScale; fA12 *= fInvScale; if (IsBalancedCompanion3(fA10,fA21,fA02,fA12,fA22)) { break; } } assert(i < iMax); rkMat[1][0] = (rkMat[1][0] >= (Real)0.0 ? fA10 : -fA10); rkMat[2][1] = (rkMat[2][1] >= (Real)0.0 ? fA21 : -fA21); rkMat[0][2] = (rkMat[0][2] >= (Real)0.0 ? fA02 : -fA02); rkMat[1][2] = (rkMat[1][2] >= (Real)0.0 ? fA12 : -fA12); rkMat[2][2] = (rkMat[2][2] >= (Real)0.0 ? fA22 : -fA22); } //---------------------------------------------------------------------------- template bool PolynomialRoots::QRIteration3 (GMatrix& rkMat) { GVector kW(3); Real fRHS, fTrace, fDet; for (int i = 0; i < m_iMaxIterations; i++) { fRHS = m_fEpsilon*(Math::FAbs(rkMat[0][0]) + Math::FAbs(rkMat[1][1])); if (Math::FAbs(rkMat[1][0]) <= fRHS) { // mat[0][0] is a root, solve the quadratic for the submatrix fTrace = rkMat[1][1] + rkMat[2][2]; fDet = rkMat[1][1]*rkMat[2][2] - rkMat[1][2]*rkMat[2][1]; FindA(fDet,-fTrace,(Real)1.0); m_afRoot[m_iCount++] = rkMat[0][0]; return true; } fRHS = m_fEpsilon*(Math::FAbs(rkMat[1][1]) + Math::FAbs(rkMat[2][2])); if (Math::FAbs(rkMat[2][1]) <= fRHS) { // mat[2][2] is a root, solve the quadratic for the submatrix fTrace = rkMat[0][0] + rkMat[1][1]; fDet = rkMat[0][0]*rkMat[1][1] - rkMat[0][1]*rkMat[1][0]; FindA(fDet,-fTrace,(Real)1.0); m_afRoot[m_iCount++] = rkMat[2][2]; return true; } FrancisQRStep(rkMat,kW); } // TO DO: In theory, cubic polynomials always have one real-valued root, // but if the maximum iterations were exceeded, what to do? Some // experiments show that when the polynomial nearly has a double root, // the convergence of the algorithm is slow. Maybe a random perturbation // to "kick" the system a bit might work? // // If you want to trap exceeding the maximum iterations, uncomment the // 'assert' line of code. // // assert( false ); // For now, zero out the smallest subdiagonal entry to decouple the // matrix. if (Math::FAbs(rkMat[1][0]) <= Math::FAbs(rkMat[2][1])) { // mat[0][0] is a root, solve the quadratic for the submatrix fTrace = rkMat[1][1] + rkMat[2][2]; fDet = rkMat[1][1]*rkMat[2][2] - rkMat[1][2]*rkMat[2][1]; FindA(fDet,-fTrace,(Real)1.0); m_afRoot[m_iCount++] = rkMat[0][0]; } else { // mat[2][2] is a root, solve the quadratic for the submatrix fTrace = rkMat[0][0] + rkMat[1][1]; fDet = rkMat[0][0]*rkMat[1][1] - rkMat[0][1]*rkMat[1][0]; FindA(fDet,-fTrace,(Real)1.0); m_afRoot[m_iCount++] = rkMat[2][2]; } return true; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // degree 4 //---------------------------------------------------------------------------- template bool PolynomialRoots::FindA (Real fC0, Real fC1, Real fC2, Real fC3, Real fC4) { if (Math::FAbs(fC4) <= m_fEpsilon) { // polynomial is cubic return FindA(fC0,fC1,fC2,fC3); } // make polynomial monic, x^4+c3*x^3+c2*x^2+c1*x+c0 Real fInvC4 = ((Real)1.0)/fC4; fC0 *= fInvC4; fC1 *= fInvC4; fC2 *= fInvC4; fC3 *= fInvC4; // reduction to resolvent cubic polynomial y^3+r2*y^2+r1*y+r0 = 0 Real fR0 = -fC3*fC3*fC0 + ((Real)4.0)*fC2*fC0 - fC1*fC1; Real fR1 = fC3*fC1 - ((Real)4.0)*fC0; Real fR2 = -fC2; FindA(fR0,fR1,fR2,(Real)1.0); // always produces at least one root Real fY = m_afRoot[0]; m_iCount = 0; Real fDiscr = ((Real)0.25)*fC3*fC3 - fC2 + fY; if (Math::FAbs(fDiscr) <= m_fEpsilon) { fDiscr = (Real)0.0; } if (fDiscr > (Real)0.0) { Real fR = Math::Sqrt(fDiscr); Real fT1 = ((Real)0.75)*fC3*fC3 - fR*fR - ((Real)2.0)*fC2; Real fT2 = (((Real)4.0)*fC3*fC2 - ((Real)8.0)*fC1 - fC3*fC3*fC3) / (((Real)4.0)*fR); Real fTplus = fT1+fT2; Real fTminus = fT1-fT2; if (Math::FAbs(fTplus) <= m_fEpsilon) { fTplus = (Real)0.0; } if (Math::FAbs(fTminus) <= m_fEpsilon) { fTminus = (Real)0.0; } if (fTplus >= (Real)0.0) { Real fD = Math::Sqrt(fTplus); m_afRoot[0] = -((Real)0.25)*fC3+((Real)0.5)*(fR+fD); m_afRoot[1] = -((Real)0.25)*fC3+((Real)0.5)*(fR-fD); m_iCount += 2; } if (fTminus >= (Real)0.0) { Real fE = Math::Sqrt(fTminus); m_afRoot[m_iCount++] = -((Real)0.25)*fC3+((Real)0.5)*(fE-fR); m_afRoot[m_iCount++] = -((Real)0.25)*fC3-((Real)0.5)*(fE+fR); } } else if (fDiscr < (Real)0.0) { m_iCount = 0; } else { Real fT2 = fY*fY-((Real)4.0)*fC0; if (fT2 >= -m_fEpsilon) { if (fT2 < (Real)0.0) // round to zero { fT2 = (Real)0.0; } fT2 = ((Real)2.0)*Math::Sqrt(fT2); Real fT1 = ((Real)0.75)*fC3*fC3 - ((Real)2.0)*fC2; if (fT1+fT2 >= m_fEpsilon) { Real fD = Math::Sqrt(fT1+fT2); m_afRoot[0] = -((Real)0.25)*fC3+((Real)0.5)*fD; m_afRoot[1] = -((Real)0.25)*fC3-((Real)0.5)*fD; m_iCount += 2; } if (fT1-fT2 >= m_fEpsilon) { Real fE = Math::Sqrt(fT1-fT2); m_afRoot[m_iCount++] = -((Real)0.25)*fC3+((Real)0.5)*fE; m_afRoot[m_iCount++] = -((Real)0.25)*fC3-((Real)0.5)*fE; } } } return m_iCount > 0; } //---------------------------------------------------------------------------- template bool PolynomialRoots::FindE (Real fC0, Real fC1, Real fC2, Real fC3, Real fC4, bool bDoBalancing) { if (Math::FAbs(fC4) <= m_fEpsilon) { // polynomial is cubic return FindA(fC0,fC1,fC2,fC3); } // make polynomial monic, x^4+c3*x^3+c2*x^2+c1*x+c0 Real fInvC4 = ((Real)1.0)/fC4; fC0 *= fInvC4; fC1 *= fInvC4; fC2 *= fInvC4; fC3 *= fInvC4; // construct the 4-by-4 companion matrix GMatrix kMat(4,4); // initialized to zero kMat[1][0] = (Real)1.0; kMat[2][1] = (Real)1.0; kMat[3][2] = (Real)1.0; kMat[0][3] = -fC0; kMat[1][3] = -fC1; kMat[2][3] = -fC2; kMat[3][3] = -fC3; if (bDoBalancing) { BalanceCompanion4(kMat); } return QRIteration4(kMat); } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetBound (Real fC0, Real fC1, Real fC2, Real fC3, Real fC4) { if (Math::FAbs(fC4) <= m_fEpsilon) { // polynomial is cubic return GetBound(fC0,fC1,fC2,fC3); } Real fInvC4 = ((Real)1.0)/fC4; Real fMax = Math::FAbs(fC0)*fInvC4; Real fTmp = Math::FAbs(fC1)*fInvC4; if (fTmp > fMax) { fMax = fTmp; } fTmp = Math::FAbs(fC2)*fInvC4; if (fTmp > fMax) { fMax = fTmp; } fTmp = Math::FAbs(fC3)*fInvC4; if (fTmp > fMax) { fMax = fTmp; } return (Real)1.0 + fMax; } //---------------------------------------------------------------------------- template bool PolynomialRoots::IsBalancedCompanion4 (Real fA10, Real fA21, Real fA32, Real fA03, Real fA13, Real fA23, Real fA33) { const Real fTolerance = (Real)0.001; // row/col 0 Real fRowNorm = fA03; Real fColNorm = fA10; Real fTest = Math::FAbs(1.0f - fColNorm/fRowNorm); if (fTest > fTolerance) { return false; } // row/col 1 fRowNorm = (fA10 >= fA13 ? fA10 : fA13); fColNorm = fA21; fTest = Math::FAbs(1.0f - fColNorm/fRowNorm); if (fTest > fTolerance) { return false; } // row/col 2 fRowNorm = (fA21 >= fA23 ? fA21 : fA23); fColNorm = fA32; fTest = Math::FAbs(1.0f - fColNorm/fRowNorm); if (fTest > fTolerance) { return false; } // row/col 3 fRowNorm = (fA32 >= fA33 ? fA32 : fA33); fColNorm = (fA03 >= fA13 ? fA03 : fA13); if (fA23 > fColNorm) { fColNorm = fA23; } if (fA33 > fColNorm) { fColNorm = fA33; } fTest = Math::FAbs(1.0f - fColNorm/fRowNorm); return fTest <= fTolerance; } //---------------------------------------------------------------------------- template void PolynomialRoots::BalanceCompanion4 (GMatrix& rkMat) { Real fA10 = Math::FAbs(rkMat[1][0]); Real fA21 = Math::FAbs(rkMat[2][1]); Real fA32 = Math::FAbs(rkMat[3][2]); Real fA03 = Math::FAbs(rkMat[0][3]); Real fA13 = Math::FAbs(rkMat[1][3]); Real fA23 = Math::FAbs(rkMat[2][3]); Real fA33 = Math::FAbs(rkMat[3][3]); Real fRowNorm, fColNorm, fScale, fInvScale; const int iMax = 16; int i; for (i = 0; i < iMax; i++) { // balance row/col 0 fRowNorm = fA03; fColNorm = fA10; fScale = Math::Sqrt(fColNorm/fRowNorm); fA03 *= fScale; fA10 = fA03; // balance row/col 1 fRowNorm = (fA10 >= fA13 ? fA10 : fA13); fColNorm = fA21; fScale = Math::Sqrt(fColNorm/fRowNorm); fInvScale = ((Real)1.0)/fScale; fA10 *= fScale; fA13 *= fScale; fA21 *= fInvScale; // balance row/col 2 fRowNorm = (fA21 >= fA23 ? fA21 : fA23); fColNorm = fA32; fScale = Math::Sqrt(fColNorm/fRowNorm); fInvScale = ((Real)1.0)/fScale; fA21 *= fScale; fA23 *= fScale; fA32 *= fInvScale; // balance row/col 3 fRowNorm = (fA32 >= fA33 ? fA32 : fA33); fColNorm = (fA03 >= fA13 ? fA03 : fA13); if (fA23 > fColNorm) { fColNorm = fA23; } if (fA33 > fColNorm) { fColNorm = fA33; } fScale = Math::Sqrt(fColNorm/fRowNorm); fInvScale = ((Real)1.0)/fScale; fA32 *= fScale; fA03 *= fInvScale; fA13 *= fInvScale; fA23 *= fInvScale; if (IsBalancedCompanion4(fA10,fA21,fA32,fA03,fA13,fA23,fA33)) { break; } } assert(i < iMax); rkMat[1][0] = (rkMat[1][0] >= (Real)0.0 ? fA10 : -fA10); rkMat[2][1] = (rkMat[2][1] >= (Real)0.0 ? fA21 : -fA21); rkMat[3][2] = (rkMat[3][2] >= (Real)0.0 ? fA32 : -fA32); rkMat[0][3] = (rkMat[0][3] >= (Real)0.0 ? fA03 : -fA03); rkMat[1][3] = (rkMat[1][3] >= (Real)0.0 ? fA13 : -fA13); rkMat[2][3] = (rkMat[2][3] >= (Real)0.0 ? fA23 : -fA23); rkMat[3][3] = (rkMat[3][3] >= (Real)0.0 ? fA33 : -fA33); } //---------------------------------------------------------------------------- template bool PolynomialRoots::QRIteration4 (GMatrix& rkMat) { GVector kW(4); GMatrix kMS(3,3); Real fRHS, fTrace, fDet, afSaveRoot[2]; int i, j, iSaveCount; for (i = 0; i < m_iMaxIterations; i++) { fRHS = m_fEpsilon*(Math::FAbs(rkMat[0][0]) + Math::FAbs(rkMat[1][1])); if (Math::FAbs(rkMat[1][0]) <= fRHS) { // mat[0][0] is a root, reduce the 3-by-3 submatrix // TO DO: Avoid the copy and pass row/column offsets to the // FrancisQR method. kMS[0][0] = rkMat[1][1]; kMS[0][1] = rkMat[1][2]; kMS[0][2] = rkMat[1][3]; kMS[1][0] = rkMat[2][1]; kMS[1][1] = rkMat[2][2]; kMS[1][2] = rkMat[2][3]; kMS[2][0] = rkMat[3][1]; kMS[2][1] = rkMat[3][2]; kMS[2][2] = rkMat[3][3]; QRIteration3(kMS); m_afRoot[m_iCount++] = rkMat[0][0]; return true; } fRHS = m_fEpsilon*(Math::FAbs(rkMat[1][1]) + Math::FAbs(rkMat[2][2])); if (Math::FAbs(rkMat[2][1]) <= fRHS) { // The matrix is decoupled into two 2-by-2 blocks. Solve the // quadratics for the blocks. fTrace = rkMat[0][0] + rkMat[1][1]; fDet = rkMat[0][0]*rkMat[1][1] - rkMat[0][1]*rkMat[1][0]; FindA(fDet,-fTrace,(Real)1.0); iSaveCount = m_iCount; for (j = 0; j < iSaveCount; j++) { afSaveRoot[j] = m_afRoot[j]; } fTrace = rkMat[2][2] + rkMat[3][3]; fDet = rkMat[2][2]*rkMat[3][3] - rkMat[2][3]*rkMat[3][2]; FindA(fDet,-fTrace,(Real)1.0); for (j = 0; j < iSaveCount; j++) { m_afRoot[m_iCount++] = afSaveRoot[j]; } return m_iCount > 0; } fRHS = m_fEpsilon*(Math::FAbs(rkMat[2][2]) + Math::FAbs(rkMat[3][3])); if (Math::FAbs(rkMat[3][2]) <= fRHS) { // mat[3][3] is a root, reduce the 3-by-3 submatrix // TO DO: Avoid the copy and pass row/column offsets to the // FrancisQR method. kMS[0][0] = rkMat[0][0]; kMS[0][1] = rkMat[0][1]; kMS[0][2] = rkMat[0][2]; kMS[1][0] = rkMat[1][0]; kMS[1][1] = rkMat[1][1]; kMS[1][2] = rkMat[1][2]; kMS[2][0] = rkMat[2][0]; kMS[2][1] = rkMat[2][1]; kMS[2][2] = rkMat[2][2]; QRIteration3(kMS); m_afRoot[m_iCount++] = rkMat[3][3]; return true; } FrancisQRStep(rkMat,kW); } // TO DO: What to do if the maximum iterations were exceeded? Maybe a // random perturbation to "kick" the system a bit might work? // // If you want to trap exceeding the maximum iterations, uncomment the // 'assert' line of code. // // assert( false ); // For now, decouple the matrix using the smallest subdiagonal entry. i = 0; Real fMin = Math::FAbs(rkMat[1][0]); Real fAbs = Math::FAbs(rkMat[2][1]); if (fAbs < fMin) { fMin = fAbs; i = 1; } fAbs = Math::FAbs(rkMat[3][2]); if (fAbs < fMin) { fMin = fAbs; i = 2; } if (i == 0) { // mat[0][0] is a root, reduce the 3-by-3 submatrix // TO DO: Avoid the copy and pass row/column offsets to the // FrancisQR method. kMS[0][0] = rkMat[1][1]; kMS[0][1] = rkMat[1][2]; kMS[0][2] = rkMat[1][3]; kMS[1][0] = rkMat[2][1]; kMS[1][1] = rkMat[2][2]; kMS[1][2] = rkMat[2][3]; kMS[2][0] = rkMat[3][1]; kMS[2][1] = rkMat[3][2]; kMS[2][2] = rkMat[3][3]; QRIteration3(kMS); m_afRoot[m_iCount++] = rkMat[0][0]; } else if (i == 1) { // The matrix is decoupled into two 2-by-2 blocks. Solve the // quadratics for the blocks. fTrace = rkMat[0][0] + rkMat[1][1]; fDet = rkMat[0][0]*rkMat[1][1] - rkMat[0][1]*rkMat[1][0]; FindA(fDet,-fTrace,(Real)1.0); iSaveCount = m_iCount; for (j = 0; j < iSaveCount; j++) { afSaveRoot[j] = m_afRoot[j]; } fTrace = rkMat[2][2] + rkMat[3][3]; fDet = rkMat[2][2]*rkMat[3][3] - rkMat[2][3]*rkMat[3][2]; FindA(fDet,-fTrace,(Real)1.0); for (j = 0; j < iSaveCount; j++) { m_afRoot[m_iCount++] = afSaveRoot[j]; } } else // i == 2 { // mat[3][3] is a root, reduce the 3-by-3 submatrix // TO DO: Avoid the copy and pass row/column offsets to the // FrancisQR method. kMS[0][0] = rkMat[0][0]; kMS[0][1] = rkMat[0][1]; kMS[0][2] = rkMat[0][2]; kMS[1][0] = rkMat[1][0]; kMS[1][1] = rkMat[1][1]; kMS[1][2] = rkMat[1][2]; kMS[2][0] = rkMat[2][0]; kMS[2][1] = rkMat[2][1]; kMS[2][2] = rkMat[2][2]; QRIteration3(kMS); m_afRoot[m_iCount++] = rkMat[3][3]; } return m_iCount > 0; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // degree N //---------------------------------------------------------------------------- template bool PolynomialRoots::FindB (const Polynomial1& rkPoly, int iDigits) { Real fBound = GetBound(rkPoly); return FindB(rkPoly,-fBound,fBound,iDigits); } //---------------------------------------------------------------------------- template bool PolynomialRoots::FindN (const Polynomial1&, int) { // TO DO: Implement this. assert(false); return false; } //---------------------------------------------------------------------------- template bool PolynomialRoots::FindE (const Polynomial1&, bool) { // TO DO: Implement this. assert(false); return false; } //---------------------------------------------------------------------------- template Real PolynomialRoots::GetBound (const Polynomial1& rkPoly) { Polynomial1 kCPoly = rkPoly; kCPoly.Compress(m_fEpsilon); int iDegree = kCPoly.GetDegree(); if (iDegree < 1) { // polynomial is constant, return invalid bound return -(Real)1.0; } Real fInvCDeg = ((Real)1.0)/kCPoly[iDegree]; Real fMax = (Real)0.0; for (int i = 0; i < iDegree; i++) { Real fTmp = Math::FAbs(kCPoly[i])*fInvCDeg; if (fTmp > fMax) { fMax = fTmp; } } return (Real)1.0 + fMax; } //---------------------------------------------------------------------------- template bool PolynomialRoots::FindB (const Polynomial1& rkPoly, Real fXMin, Real fXMax, int iDigits) { // reallocate root array if necessary if (rkPoly.GetDegree() > m_iMaxRoot) { m_iMaxRoot = rkPoly.GetDegree(); WM4_DELETE[] m_afRoot; m_afRoot = WM4_NEW Real[m_iMaxRoot]; } Real fRoot; if (rkPoly.GetDegree() == 1) { if (Bisection(rkPoly,fXMin,fXMax,iDigits,fRoot)) { m_iCount = 1; m_afRoot[0] = fRoot; return true; } m_iCount = 0; return false; } // get roots of derivative polynomial Polynomial1 kDeriv = rkPoly.GetDerivative(); FindB(kDeriv,fXMin,fXMax,iDigits); int i, iNewCount = 0; Real* afNewRoot = WM4_NEW Real[m_iCount+1]; if ( m_iCount > 0 ) { // find root on [xmin,root[0]] if (Bisection(rkPoly,fXMin,m_afRoot[0],iDigits,fRoot)) { afNewRoot[iNewCount++] = fRoot; } // find root on [root[i],root[i+1]] for 0 <= i <= count-2 for (i = 0; i <= m_iCount-2; i++) { if (Bisection(rkPoly,m_afRoot[i],m_afRoot[i+1],iDigits,fRoot)) { afNewRoot[iNewCount++] = fRoot; } } // find root on [root[count-1],xmax] if (Bisection(rkPoly,m_afRoot[m_iCount-1],fXMax,iDigits,fRoot)) { afNewRoot[iNewCount++] = fRoot; } } else { // polynomial is monotone on [xmin,xmax], has at most one root if (Bisection(rkPoly,fXMin,fXMax,iDigits,fRoot)) { afNewRoot[iNewCount++] = fRoot; } } // copy to old buffer if (iNewCount > 0) { m_iCount = 1; m_afRoot[0] = afNewRoot[0]; for (i = 1; i < iNewCount; i++) { Real fRootDiff = afNewRoot[i]-afNewRoot[i-1]; if (Math::FAbs(fRootDiff) > m_fEpsilon) { m_afRoot[m_iCount++] = afNewRoot[i]; } } } else { m_iCount = 0; } WM4_DELETE[] afNewRoot; return m_iCount > 0; } //---------------------------------------------------------------------------- template bool PolynomialRoots::FindN (const Polynomial1&, Real, Real, int) { // TO DO: Implement this. assert(false); return false; } //---------------------------------------------------------------------------- template bool PolynomialRoots::AllRealPartsNegative ( const Polynomial1& rkPoly) { // make a copy of coefficients, later calls will change the copy int iDegree = rkPoly.GetDegree(); const Real* afPolyCoeff = (const Real*)rkPoly; Real* afCoeff = WM4_NEW Real[iDegree+1]; size_t uiSize = (iDegree+1)*sizeof(Real); System::Memcpy(afCoeff,uiSize,afPolyCoeff,uiSize); // make polynomial monic if (afCoeff[iDegree] != (Real)1.0) { Real fInv = ((Real)1.0)/afCoeff[iDegree]; for (int i = 0; i < iDegree; i++) { afCoeff[i] *= fInv; } afCoeff[iDegree] = (Real)1.0; } return AllRealPartsNegative(iDegree,afCoeff); } //---------------------------------------------------------------------------- template bool PolynomialRoots::AllRealPartsPositive ( const Polynomial1& rkPoly) { // make a copy of coefficients, later calls will change the copy int iDegree = rkPoly.GetDegree(); const Real* afPolyCoeff = (const Real*)rkPoly; Real* afCoeff = WM4_NEW Real[iDegree+1]; size_t uiSize = (iDegree+1)*sizeof(Real); System::Memcpy(afCoeff,uiSize,afPolyCoeff,uiSize); // make polynomial monic int i; if (afCoeff[iDegree] != (Real)1.0) { Real fInv = ((Real)1.0)/afCoeff[iDegree]; for (i = 0; i < iDegree; i++) { afCoeff[i] *= fInv; } afCoeff[iDegree] = (Real)1.0; } // reflect z -> -z int iSign = -1; for (i = iDegree-1; i >= 0; i--, iSign = -iSign) { afCoeff[i] *= iSign; } return AllRealPartsNegative(iDegree,afCoeff); } //---------------------------------------------------------------------------- template bool PolynomialRoots::AllRealPartsNegative (int iDegree, Real* afCoeff) { // assert: afCoeff[iDegree] = 1 if (afCoeff[iDegree-1] <= (Real)0.0) { return false; } if (iDegree == 1) { return true; } Real* afTmpCoeff = WM4_NEW Real[iDegree]; afTmpCoeff[0] = ((Real)2.0)*afCoeff[0]*afCoeff[iDegree-1]; int i; for (i = 1; i <= iDegree-2; i++) { afTmpCoeff[i] = afCoeff[iDegree-1]*afCoeff[i]; if (((iDegree-i) % 2) == 0) { afTmpCoeff[i] -= afCoeff[i-1]; } afTmpCoeff[i] *= (Real)2.0; } afTmpCoeff[iDegree-1] = ((Real)2.0)*afCoeff[iDegree-1]*afCoeff[iDegree-1]; int iNextDegree; for (iNextDegree = iDegree-1; iNextDegree >= 0; iNextDegree--) { if (afTmpCoeff[iNextDegree] != (Real)0.0) { break; } } for (i = 0; i <= iNextDegree-1; i++) { afCoeff[i] = afTmpCoeff[i]/afTmpCoeff[iNextDegree]; } WM4_DELETE[] afTmpCoeff; return AllRealPartsNegative(iNextDegree,afCoeff); } //---------------------------------------------------------------------------- template int PolynomialRoots::GetRootCount (const Polynomial1& rkPoly, Real fT0, Real fT1) { int iDegree = rkPoly.GetDegree(); const Real* afCoeff = (const Real*)rkPoly; if (iDegree == 0) { // polynomial is constant on the interval if (afCoeff[0] != (Real)0.0) { return 0; } else { return -1; // to indicate "infinitely many" } } // generate the Sturm sequence std::vector*> kSturm; Polynomial1* pkF0 = WM4_NEW Polynomial1(rkPoly); Polynomial1* pkF1 = WM4_NEW Polynomial1( pkF0->GetDerivative()); kSturm.push_back(pkF0); kSturm.push_back(pkF1); while (pkF1->GetDegree() > 0) { Polynomial1* pkF2 = WM4_NEW Polynomial1(-1); Polynomial1 kQuot; pkF0->Divide(*pkF1,kQuot,*pkF2,Math::ZERO_TOLERANCE); *pkF2 = -(*pkF2); kSturm.push_back(pkF2); pkF0 = pkF1; pkF1 = pkF2; } int i; Real fValue0, fValue1; // count the sign changes at t0 int iSignChanges0 = 0; if (fT0 == -Math::MAX_REAL) { pkF0 = kSturm[0]; if (pkF0->GetDegree() & 1) { fValue0 = -(*pkF0)[pkF0->GetDegree()]; } else { fValue0 = (*pkF0)[pkF0->GetDegree()]; } if (Math::FAbs(fValue0) < m_fEpsilon) { fValue0 = (Real)0.0; } for (i = 1; i < (int)kSturm.size(); i++) { pkF1 = kSturm[i]; if (pkF1->GetDegree() & 1) { fValue1 = -(*pkF1)[pkF1->GetDegree()]; } else { fValue1 = (*pkF1)[pkF1->GetDegree()]; } if (Math::FAbs(fValue1) < m_fEpsilon) { fValue1 = (Real)0.0; } if (fValue0*fValue1 < (Real)0.0 || fValue0 == (Real)0.0) { iSignChanges0++; } fValue0 = fValue1; pkF0 = pkF1; } } else { pkF0 = kSturm[0]; fValue0 = (*pkF0)(fT0); if (Math::FAbs(fValue0) < m_fEpsilon) { fValue0 = (Real)0.0; } for (i = 1; i < (int)kSturm.size(); i++) { pkF1 = kSturm[i]; fValue1 = (*pkF1)(fT0); if (Math::FAbs(fValue1) < m_fEpsilon) { fValue1 = (Real)0.0; } if (fValue0*fValue1 < (Real)0.0 || fValue0 == (Real)0.0) { iSignChanges0++; } fValue0 = fValue1; pkF0 = pkF1; } } // count the sign changes at t1 int iSignChanges1 = 0; if (fT1 == Math::MAX_REAL) { pkF0 = kSturm[0]; fValue0 = (*pkF0)[pkF0->GetDegree()]; if (Math::FAbs(fValue0) < m_fEpsilon) { fValue0 = (Real)0.0; } for (i = 1; i < (int)kSturm.size(); i++) { pkF1 = kSturm[i]; fValue1 = (*pkF1)[pkF1->GetDegree()]; if (Math::FAbs(fValue1) < m_fEpsilon) { fValue1 = (Real)0.0; } if (fValue0*fValue1 < (Real)0.0 || fValue0 == (Real)0.0) { iSignChanges1++; } fValue0 = fValue1; pkF0 = pkF1; } } else { pkF0 = kSturm[0]; fValue0 = (*pkF0)(fT1); if (Math::FAbs(fValue0) < m_fEpsilon) { fValue0 = (Real)0.0; } for (i = 1; i < (int)kSturm.size(); i++) { pkF1 = kSturm[i]; fValue1 = (*pkF1)(fT1); if (Math::FAbs(fValue1) < m_fEpsilon) { fValue1 = (Real)0.0; } if (fValue0*fValue1 < (Real)0.0 || fValue0 == (Real)0.0 ) { iSignChanges1++; } fValue0 = fValue1; pkF0 = pkF1; } } // clean up for (i = 0; i < (int)kSturm.size(); i++) { WM4_DELETE kSturm[i]; } if (iSignChanges0 >= iSignChanges1) { return iSignChanges0 - iSignChanges1; } // theoretically we should not get here assert(false); return 0; } //---------------------------------------------------------------------------- template bool PolynomialRoots::Bisection (const Polynomial1& rkPoly, Real fXMin, Real fXMax, int iDigits, Real& rfRoot) { Real fP0 = rkPoly(fXMin); if (Math::FAbs(fP0) <= Math::ZERO_TOLERANCE) { rfRoot = fXMin; return true; } Real fP1 = rkPoly(fXMax); if (Math::FAbs(fP1) <= Math::ZERO_TOLERANCE) { rfRoot = fXMax; return true; } if (fP0*fP1 > (Real)0.0) return false; // determine number of iterations to get 'digits' accuracy. Real fTmp0 = Math::Log(fXMax-fXMin); Real fTmp1 = ((Real)iDigits)*Math::Log((Real)10.0); Real fArg = (fTmp0 + fTmp1)/Math::Log((Real)2.0); int iMaxIter = (int)(fArg + (Real)0.5); for (int i = 0; i < iMaxIter; i++) { rfRoot = ((Real)0.5)*(fXMin + fXMax); Real fP = rkPoly(rfRoot); Real fProduct = fP*fP0; if (fProduct < (Real)0.0) { fXMax = rfRoot; fP1 = fP; } else if (fProduct > (Real)0.0) { fXMin = rfRoot; fP0 = fP; } else { break; } } return true; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // FindE support //---------------------------------------------------------------------------- template void PolynomialRoots::GetHouseholderVector (int iSize, const Vector3& rkU, Vector3& rkV) { // Householder vector V: // Given a vector U, compute a vector V such that V[0] = 1 and // (I-2*V*V^T/|V|^2)*U is zero in all but the first component. The // matrix P = I-2*V*V^T/|V|^2 is a Householder transformation, a // reflection matrix. Real fLength = rkU[0]*rkU[0]; int i; for (i = 1; i < iSize; i++) { fLength += rkU[i]*rkU[i]; } fLength = Math::Sqrt(fLength); if (fLength > m_fEpsilon) { Real fInv = ((Real)1.0)/(rkU[0]+Math::Sign(rkU[0])*fLength); rkV[0] = (Real)1.0; for (i = 1; i < iSize; i++) { rkV[i] = fInv*rkU[i]; } } else { // U is the zero vector, any vector will do rkV[0] = (Real)1.0; for (i = 1; i < iSize; i++) { rkV[i] = (Real)0.0; } } } //---------------------------------------------------------------------------- template void PolynomialRoots::PremultiplyHouseholder (GMatrix& rkMat, GVector& rkW, int iRMin, int iRMax, int iCMin, int iCMax, int iVSize, const Vector3& rkV) { // Householder premultiplication: // Given a matrix A and an m-by-1 vector V with V[0] = 1, let S be the // submatrix of A with m rows rmin <= r <= m+rmin-1 and columns // cmin <= c <= cmax. Overwrite S with P*S where P = I-2*V*V^T/|V|^2. int iSubRows = iRMax - iRMin + 1, iSubCols = iCMax - iCMin + 1; int iRow, iCol; Real fSqrLen = rkV[0]*rkV[0]; for (int i = 1; i < iVSize; i++) { fSqrLen += rkV[i]*rkV[i]; } Real fBeta = -((Real)2.0)/fSqrLen; for (iCol = 0; iCol < iSubCols; iCol++) { rkW[iCol] = (Real)0.0; for (iRow = 0; iRow < iSubRows; iRow++) { rkW[iCol] += rkV[iRow]*rkMat[iRMin+iRow][iCMin+iCol]; } rkW[iCol] *= fBeta; } for (iRow = 0; iRow < iSubRows; iRow++) { for (iCol = 0; iCol < iSubCols; iCol++) { rkMat[iRMin+iRow][iCMin+iCol] += rkV[iRow]*rkW[iCol]; } } } //---------------------------------------------------------------------------- template void PolynomialRoots::PostmultiplyHouseholder (GMatrix& rkMat, GVector& rkW, int iRMin, int iRMax, int iCMin, int iCMax, int iVSize, const Vector3& rkV) { // Householder postmultiplication: // Given a matrix A and an n-by-1 vector V with V[0] = 1, let S be the // submatrix of A with n columns cmin <= c <= n+cmin-1 and rows // rmin <= r <= rmax. Overwrite S with S*P where P = I-2*V*V^T/|V|^2. int iSubRows = iRMax - iRMin + 1, iSubCols = iCMax - iCMin + 1; int iRow, iCol; Real fSqrLen = rkV[0]*rkV[0]; for (int i = 1; i < iVSize; i++) { fSqrLen += rkV[i]*rkV[i]; } Real fBeta = -((Real)2.0)/fSqrLen; for (iRow = 0; iRow < iSubRows; iRow++) { rkW[iRow] = (Real)0.0; for (iCol = 0; iCol < iSubCols; iCol++) { rkW[iRow] += rkMat[iRMin+iRow][iCMin+iCol]*rkV[iCol]; } rkW[iRow] *= fBeta; } for (iRow = 0; iRow < iSubRows; iRow++) { for (iCol = 0; iCol < iSubCols; iCol++) { rkMat[iRMin+iRow][iCMin+iCol] += rkW[iRow]*rkV[iCol]; } } } //---------------------------------------------------------------------------- template void PolynomialRoots::FrancisQRStep (GMatrix& rkH, GVector& rkW) { // Given an n-by-n unreduced upper Hessenberg matrix H whose trailing // 2-by-2 principal submatrix has eigenvalues a1 and a2, overwrite H // with Z^T*H*Z where Z = P(1)*...*P(n-2) is a product of Householder // matrices and Z^T*(H-a1*I)*(H-a2*I) is upper triangular. // assert: H is unreduced upper Hessenberg and n >= 3 // compute first column of (H-a1*I)*(H-a2*I) int iN = rkH.GetRows(); Real fTrace = rkH[iN-2][iN-2] + rkH[iN-1][iN-1]; Real fDet = rkH[iN-2][iN-2]*rkH[iN-1][iN-1] - rkH[iN-2][iN-1]*rkH[iN-1][iN-2]; Vector3 kU; kU[0] = rkH[0][0]*rkH[1][1]+rkH[0][1]*rkH[1][0]-fTrace*rkH[0][0]+fDet; kU[1] = rkH[1][0]*(rkH[0][0]+rkH[1][1]-fTrace); kU[2] = rkH[1][0]*rkH[2][1]; // overwrite H with P(0)*H*P(0)^T Vector3 kV; GetHouseholderVector(3,kU,kV); PremultiplyHouseholder(rkH,rkW,0,2,0,iN-1,3,kV); PostmultiplyHouseholder(rkH,rkW,0,iN-1,0,2,3,kV); for (int i = 1; i <= iN-3; i++) { // overwrite H with P(i)*H*P(i)^T kU[0] = rkH[i ][i-1]; kU[1] = rkH[i+1][i-1]; kU[2] = rkH[i+2][i-1]; GetHouseholderVector(3,kU,kV); // The column range does not need to be 0 to iN-1 because of the // pattern of zeros that occur in matrix H. PremultiplyHouseholder(rkH,rkW,i,i+2,i-1,iN-1,3,kV); // The row range does not need to be 0 to iN-1 because of the pattern // of zeros that occur in matrix H. int iRMax = i+3; if (iRMax >= iN) { iRMax = iN-1; } PostmultiplyHouseholder(rkH,rkW,0,iRMax,i,i+2,3,kV); } // overwrite H with P(n-2)*H*P(n-2)^T kU[0] = rkH[iN-2][iN-3]; kU[1] = rkH[iN-1][iN-3]; GetHouseholderVector(2,kU,kV); // The column range does not need to be 0 to iN-1 because of the pattern // of zeros that occur in matrix H. PremultiplyHouseholder(rkH,rkW,iN-2,iN-1,iN-3,iN-1,2,kV); PostmultiplyHouseholder(rkH,rkW,0,iN-1,iN-2,iN-1,2,kV); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // explicit instantiation //---------------------------------------------------------------------------- template WM4_FOUNDATION_ITEM class PolynomialRoots; template WM4_FOUNDATION_ITEM class PolynomialRoots; //---------------------------------------------------------------------------- }