Foam::scalar Foam::highestMomentReconstruction::minimizeTargetFunction ( scalar sigmaLow, scalar sigmaHigh, const univariateMomentSet& moments, univariateMomentSet& momentsStar ) { const scalar goldenRatio = (sqrt(5.0) - 1.0)/2.0; scalar a = sigmaLow; scalar b = sigmaHigh; scalar x = b - goldenRatio*(b - a); scalar y = a + goldenRatio*(b - a); label iter = 0; while (mag (x - y) > sigmaTol_ && iter < maxSigmaIter_) { // Square the target function to find closest value to zero, // independently from the sign of the function scalar fx = sqr(targetFunction(x, moments, momentsStar)); scalar fy = sqr(targetFunction(y, moments, momentsStar)); if (fx < fy) { b = y; y = x; x = b - goldenRatio*(b - a); } else { a = x; x = y; y = a + goldenRatio*(b - a); } iter++; } if (iter > maxSigmaIter_) { FatalErrorInFunction << "Number of iterations exceeded." << nl << " Max allowed iterations = " << maxSigmaIter_ << abort(FatalError); } return (a + b)/2.0; }
TEST(Sample, hookStaticFunction) { globalCounter = 999; ASSERT_EQ(999, globalCounter); ASSERT_TRUE(HookEngine::instance()->installHook( reinterpret_cast<uint32_t>(targetFunction), reinterpret_cast<uint32_t>(hook), &info)); ASSERT_TRUE(info != NULL); ASSERT_EQ(2, targetFunction(1, -4, 4)); ASSERT_EQ(1, globalCounter); }
Foam::scalar Foam::highestMomentReconstruction::normalizedMomentError ( scalar sigma, const univariateMomentSet& moments, univariateMomentSet& momentsStar ) { scalar norm = 0.0; targetFunction(sigma, moments, momentsStar); univariateMomentSet approximatedMoments(moments.size(), moments.support()); kernel_().momentsStarToMoments(sigma, approximatedMoments, momentsStar); for (label momenti = 0; momenti < moments.size(); momenti++) { norm += mag(1.0 - approximatedMoments[momenti]/moments[momenti]); } return sqrt(norm); }
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::extendedMomentInversion::invert(const univariateMomentSet& moments) { univariateMomentSet m(moments); reset(); // Terminate execution if negative number density is encountered if (m[0] < 0.0) { FatalErrorIn ( "Foam::extendedMomentInversion::invert\n" "(\n" " const univariateMomentSet& moments\n" ")" ) << "The zero-order moment is negative." << abort(FatalError); } // Exclude cases where the zero-order moment is very small to avoid // problems in the inversion due to round-off error if (m[0] < SMALL) { sigma_ = 0.0; nullSigma_ = true; return; } label nRealizableMoments = m.nRealizableMoments(); if (nRealizableMoments % 2 == 0) { // If the number of realizable moments is even, we apply the standard // QMOM directly to maximize the number of preserved moments. // Info << "Even number of realizable moments: using QMOM" << endl; // Info << "Moments: " << m << endl; // Info << "Invertible: " << m.nInvertibleMoments() << endl; // Info << "Realizable: " << m.nRealizableMoments() << endl; m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); } else { // Resizing the moment set to avoid copying again m.resize(nRealizableMoments); // Local set of starred moments univariateMomentSet mStar(nRealizableMoments, 0); // Compute target function for sigma = 0 scalar sigmaLow = 0.0; scalar fLow = targetFunction(sigmaLow, m, mStar); sigma_ = sigmaLow; // Check if sigma = 0 is root if (mag(fLow) <= targetFunctionTol_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } // Compute target function for sigma = sigmaMax scalar sigMax = sigmaMax(m); scalar sigmaHigh = sigMax; scalar fHigh = targetFunction(sigmaHigh, m, mStar); if (fLow*fHigh > 0) { // Root not found. Minimize target function in [0, sigma_] sigma_ = minimizeTargetFunction(0, sigmaHigh, m, mStar); // Check if sigma is small and use QMOM if (mag(sigma_) < sigmaTol_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } targetFunction(sigma_, m, mStar); secondaryQuadrature(mStar); return; } // Apply Ridder's algorithm to find sigma for (label iter = 0; iter < maxSigmaIter_; iter++) { scalar fMid, sigmaMid; sigmaMid = (sigmaLow + sigmaHigh)/2.0; fMid = targetFunction(sigmaMid, m, mStar); scalar s = sqrt(sqr(fMid) - fLow*fHigh); if (s == 0.0) { FatalErrorIn ( "Foam::extendedMomentInversion::invert\n" "(\n" " const univariateMomentSet& moments\n" ")" )<< "Singular value encountered while attempting to find root." << "Moment set = " << m << endl << "sigma = " << sigma_ << endl << "fLow = " << fLow << endl << "fMid = " << fMid << endl << "fHigh = " << fHigh << abort(FatalError); } sigma_ = sigmaMid + (sigmaMid - sigmaLow)*sign(fLow - fHigh)*fMid/s; momentsToMomentsStar(sigma_, m, mStar); scalar fNew = targetFunction(sigma_, m, mStar); scalar dSigma = (sigmaHigh - sigmaLow)/2.0; // Check for convergence if (mag(fNew) <= targetFunctionTol_ || mag(dSigma) <= sigmaTol_) { if (mag(sigma_) < sigmaTol_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } scalar momentError = normalizedMomentError(sigma_, m, mStar); if ( momentError < momentsTol_ ) { // Found a value of sigma that preserves all the moments secondaryQuadrature(mStar); return; } else { // Root not found. Minimize target function in [0, sigma_] sigma_ = minimizeTargetFunction(0, sigma_, m, mStar); if (mag(sigma_) < sigmaTol_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } targetFunction(sigma_, m, mStar); secondaryQuadrature(mStar); return; } } else { if (fNew*fMid < 0 && sigma_ < sigmaMid) { sigmaLow = sigma_; fLow = fNew; sigmaHigh = sigmaMid; fHigh = fMid; } else if (fNew*fMid < 0 && sigma_ > sigmaMid) { sigmaLow = sigmaMid; fLow = fMid; sigmaHigh = sigma_; fHigh = fNew; } else if (fNew*fLow < 0) { sigmaHigh = sigma_; fHigh = fNew; } else if (fNew*fHigh < 0) { sigmaLow = sigma_; fLow = fNew; } } } FatalErrorIn ( "Foam::extendedMomentInversion::invert\n" "(\n" " const univariateMomentSet& moments\n" ")" ) << "Number of iterations exceeded." << abort(FatalError); } }
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::highestMomentReconstruction::invert(const univariateMomentSet& moments) { univariateMomentSet m(moments); reset(); invertSingular(m); if (nullSigma_) return; label nRealizableMoments = m.nRealizableMoments(); // Resizing the moment set to avoid copying again m.resize(nRealizableMoments); // Local set of starred moments univariateMomentSet mStar(nRealizableMoments, m.support()); // Compute target function for sigma = 0 scalar sigmaLow = 0.0; scalar fLow = targetFunction(sigmaLow, m, mStar); sigma_ = sigmaLow; // Check if sigma = 0 is root if (mag(fLow) <= targetFunctionTol_) { sigma_ = 0.0; nullSigma_ = true; momentInverter_().invert(m); secondaryQuadrature ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } // Compute target function for sigma = sigmaMax scalar sigMax = kernel_().sigmaMax(m); scalar sigmaHigh = sigMax; scalar fHigh = targetFunction(sigmaHigh, m, mStar); // This should not happen with the new algorithm if (fLow*fHigh > 0) { // Root not found. Minimize target function in [0, sigma_] sigma_ = minimizeTargetFunction(0, sigmaHigh, m, mStar); // If sigma_ is small, use QMOM if (mag(sigma_) < sigmaMin_) { sigma_ = 0.0; nullSigma_ = true; momentInverter_().invert(m); secondaryQuadrature ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } targetFunction(sigma_, m, mStar); secondaryQuadrature // secondary quadrature from mStar ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } // Apply Ridder's algorithm to find sigma for (label iter = 0; iter < maxSigmaIter_; iter++) { scalar fMid, sigmaMid; sigmaMid = (sigmaLow + sigmaHigh)/2.0; fMid = targetFunction(sigmaMid, m, mStar); scalar s = sqrt(sqr(fMid) - fLow*fHigh); if (s == 0.0) { FatalErrorInFunction << "Singular value encountered searching for root." << nl << " Moment set = " << m << nl << " sigma = " << sigma_ << nl << " fLow = " << fLow << nl << " fMid = " << fMid << nl << " fHigh = " << fHigh << abort(FatalError); } sigma_ = sigmaMid + (sigmaMid - sigmaLow)*sign(fLow - fHigh)*fMid/s; kernel_().momentsToMomentsStar(sigma_, m, mStar); scalar fNew = targetFunction(sigma_, m, mStar); scalar dSigma = (sigmaHigh - sigmaLow)/2.0; // Check for convergence if (mag(fNew) <= targetFunctionTol_ || mag(dSigma) <= sigmaTol_) { // Root finding converged // If sigma_ is small, use QMOM if (mag(sigma_) < sigmaMin_) { sigma_ = 0.0; nullSigma_ = true; momentInverter_().invert(m); secondaryQuadrature ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } scalar momentError = normalizedMomentError(sigma_, m, mStar); if ( momentError < momentsTol_ ) { // Found a value of sigma that preserves all the moments secondaryQuadrature // Secondary quadrature from mStar ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } else { // Root not found. Minimize target function in [0, sigma_] sigma_ = minimizeTargetFunction(0, sigma_, m, mStar); // If sigma_ is small, use QMOM if (mag(sigma_) < sigmaMin_) { sigma_ = 0.0; nullSigma_ = true; momentInverter_().invert(m); secondaryQuadrature ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } targetFunction(sigma_, m, mStar); secondaryQuadrature // Secondary quadrature from mStar ( momentInverter_().weights(), momentInverter_().abscissae() ); return; } } else { // Root finding did not converge. Refine search. if (fNew*fMid < 0 && sigma_ < sigmaMid) { sigmaLow = sigma_; fLow = fNew; sigmaHigh = sigmaMid; fHigh = fMid; } else if (fNew*fMid < 0 && sigma_ > sigmaMid) { sigmaLow = sigmaMid; fLow = fMid; sigmaHigh = sigma_; fHigh = fNew; } else if (fNew*fLow < 0) { sigmaHigh = sigma_; fHigh = fNew; } else if (fNew*fHigh < 0) { sigmaLow = sigma_; fLow = fNew; } } } FatalErrorInFunction << "Number of iterations exceeded." << nl << " Max allowed iterations = " << maxSigmaIter_ << abort(FatalError); }
TEST(Sample, simpleCallTargetFunction) { ASSERT_EQ(2, targetFunction(1, -4, 4)); }
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::extendedMomentInversion::invert(const univariateMomentSet& moments) { univariateMomentSet m(moments); reset(); // Terminate execution if negative number density is encountered if (m[0] < 0.0) { FatalErrorInFunction << "The zero-order moment is negative." << abort(FatalError); } // Exclude cases where the zero-order moment is very small to avoid // problems in the inversion due to round-off error if (m[0] < SMALL) { sigma_ = 0.0; nullSigma_ = true; return; } label nRealizableMoments = m.nRealizableMoments(); // If the moment set is on the boundary of the moment space, the // distribution will be reconstructed by a summation of Dirac delta, // and no attempt to use the extended quadrature method of moments is made. if (m.isOnMomentSpaceBoundary()) { sigma_ = 0.0; nullSigma_ = true; m.invert(); secondaryQuadrature(m); return; } if (nRealizableMoments % 2 == 0) { // If the number of realizable moments is even, we apply the standard // QMOM directly to maximize the number of preserved moments. m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); } else { // Do not attempt the EQMOM reconstruction if mean or variance of the // moment set are small to avoid numerical problems. These problems are // particularly acute in the calculation of the recurrence relationship // of the Jacobi orthogonal polynomials used for the beta kernel density // function. if (m[1]/m[0] < minMean_ || (m[2]/m[0] - sqr(m[1]/m[0])) < minVariance_) { sigma_ = 0.0; nullSigma_ = true; m.invert(); secondaryQuadrature(m); return; } // Resizing the moment set to avoid copying again m.resize(nRealizableMoments); // Local set of starred moments univariateMomentSet mStar(nRealizableMoments, 0, m.support()); // Compute target function for sigma = 0 scalar sigmaLow = 0.0; scalar fLow = targetFunction(sigmaLow, m, mStar); sigma_ = sigmaLow; // Check if sigma = 0 is root if (mag(fLow) <= targetFunctionTol_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } // Compute target function for sigma = sigmaMax scalar sigMax = sigmaMax(m); scalar sigmaHigh = sigMax; scalar fHigh = targetFunction(sigmaHigh, m, mStar); // This should not happen with the new algorithm if (fLow*fHigh > 0) { // Root not found. Minimize target function in [0, sigma_] sigma_ = minimizeTargetFunction(0, sigmaHigh, m, mStar); // If sigma_ is small, use QMOM if (mag(sigma_) < sigmaMin_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } targetFunction(sigma_, m, mStar); secondaryQuadrature(mStar); return; } // Apply Ridder's algorithm to find sigma for (label iter = 0; iter < maxSigmaIter_; iter++) { scalar fMid, sigmaMid; sigmaMid = (sigmaLow + sigmaHigh)/2.0; fMid = targetFunction(sigmaMid, m, mStar); scalar s = sqrt(sqr(fMid) - fLow*fHigh); if (s == 0.0) { FatalErrorInFunction << "Singular value encountered searching for root.\n" << "Moment set = " << m << endl << "sigma = " << sigma_ << endl << "fLow = " << fLow << endl << "fMid = " << fMid << endl << "fHigh = " << fHigh << abort(FatalError); } sigma_ = sigmaMid + (sigmaMid - sigmaLow)*sign(fLow - fHigh)*fMid/s; momentsToMomentsStar(sigma_, m, mStar); scalar fNew = targetFunction(sigma_, m, mStar); scalar dSigma = (sigmaHigh - sigmaLow)/2.0; // Check for convergence if (mag(fNew) <= targetFunctionTol_ || mag(dSigma) <= sigmaTol_) { // Root finding converged // If sigma_ is small, use QMOM if (mag(sigma_) < sigmaMin_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } scalar momentError = normalizedMomentError(sigma_, m, mStar); if ( momentError < momentsTol_ ) { // Found a value of sigma that preserves all the moments secondaryQuadrature(mStar); return; } else { // Root not found. Minimize target function in [0, sigma_] sigma_ = minimizeTargetFunction(0, sigma_, m, mStar); // If sigma_ is small, use QMOM if (mag(sigma_) < sigmaMin_) { m.invert(); sigma_ = 0.0; nullSigma_ = true; secondaryQuadrature(m); return; } targetFunction(sigma_, m, mStar); secondaryQuadrature(mStar); return; } } else { // Root finding did not converge. Refine search. if (fNew*fMid < 0 && sigma_ < sigmaMid) { sigmaLow = sigma_; fLow = fNew; sigmaHigh = sigmaMid; fHigh = fMid; } else if (fNew*fMid < 0 && sigma_ > sigmaMid) { sigmaLow = sigmaMid; fLow = fMid; sigmaHigh = sigma_; fHigh = fNew; } else if (fNew*fLow < 0) { sigmaHigh = sigma_; fHigh = fNew; } else if (fNew*fHigh < 0) { sigmaLow = sigma_; fLow = fNew; } } } FatalErrorInFunction << "Number of iterations exceeded.\n" << "Max allowed iterations = " << maxSigmaIter_ << abort(FatalError); } }