Listing 1: The basic simplex template.
#if !defined ( SIMPLEX_H_INCLUDED ) #define SIMPLEX_H_INCLUDED #include <cmath> #include <cfloat> #include <valarray> #include <utility> using namespace std; typedef valarray<double> vd; template < typename T > class simplex { public: //--------------------------------------------------------------- simplex ( const T t ) : m_nVar( t( ).size( ) ) // constructor { m_FunctionObject = t; // copy the function object in // for partial safety m_vInitialVector.resize( m_nVar ); m_vInitialVector = t( ); m_vDeltaVector.resize ( m_nVar ); m_vFinalVector.resize ( m_nVar ); m_bDeltaVectorIsValid = false; m_dFinalFOM = DBL_MAX; // make huge starting FOM m_lMaximumIterations = 5000; m_dSmallestAllowedShift = 1.0E-7; //if the change falls below this value, minimization stops }; //--------------------------------------------------------------- std::pair<double, vd> fnMinimize( void ) { // this does all the work for simplex if ( !m_bDeltaVectorIsValid ) m_fnComputeDeltaVector( ); m_fnSetupSimplex( ); valarray<double> vShift(m_nVar); valarray<double> vPreviousResponseVector( m_nVar+1 ); vPreviousResponseVector = DBL_MAX; m_lIterationsPerformed = 0; for ( size_t iterations=0; iterations<m_lMaximumIterations; iterations++ ) { double dResponse1, dResponse2; valarray<double> vCentroid( m_nVar ); valarray<double> vTest1( m_nVar ); vCentroid = 0.0; vTest1 = 0; fnFindMinMaxResponse( ); vCentroid = fnCalculateSimplexCentroid( ); vShift = vCentroid - m_vSimplexVertex[ m_lWorstFitVertex ]; vTest1 = vCentroid + vShift; dResponse1 = m_FunctionObject( vTest1 ); // if better than the best then try a bigger step if (dResponse1 < m_vVectorOfFOM[ m_lBestFitVertex ] ) { valarray<double> vTest2( m_nVar ); vTest2 = vTest1 + vShift; dResponse2 = m_FunctionObject ( vTest2 ); // if better than the best, use this one if ( dResponse2 < m_vVectorOfFOM[m_lBestFitVertex] ) { // doubled reflection shift m_vSimplexVertex[ m_lWorstFitVertex ] = vTest2; m_vVectorOfFOM[ m_lWorstFitVertex ] = dResponse2; } else { // reflected the worst vertex m_vSimplexVertex[ m_lWorstFitVertex ] = vTest1; m_vVectorOfFOM[ m_lWorstFitVertex ] = dResponse1; } } // if better than the worst, then just replace the worst else if ( dResponse1 < m_vVectorOfFOM[ m_lWorstFitVertex ] ) { // simple replacement m_vSimplexVertex[ m_lWorstFitVertex ] = vTest1; m_vVectorOfFOM[ m_lWorstFitVertex ] = dResponse1; } // otherwise, it was worse than the worst we know else { //try the point midway between worst and the centroid vTest1 = vCentroid - 0.5 * vShift; dResponse1 = m_FunctionObject ( vTest1 ); // if better than the worst we know, then just use it if (dResponse1 < m_vVectorOfFOM[m_lWorstFitVertex] ) { // replace with halfway between worst and centroid m_vSimplexVertex[ m_lWorstFitVertex ] = vTest1; m_vVectorOfFOM[ m_lWorstFitVertex ] = dResponse1; } else // nothing worked; contract toward the best point { // just replace each point (except best) with the // point halfway between best and itself size_t i; for ( i=0; i<m_nVar; i++ ) { if ( i != m_lBestFitVertex ) { m_vSimplexVertex[ i ] = ( m_vSimplexVertex[ i ] + m_vSimplexVertex[m_lBestFitVertex] ) /2.0; m_vVectorOfFOM[ i ] = m_FunctionObject( m_vSimplexVertex[ i ] ); } } } } if ( iterations > 1 ) { valarray<double> vTest = vPreviousResponseVector - m_vVectorOfFOM; if ( sqrt( vTest.sum( ) ) <= m_dSmallestAllowedShift ) break; } vPreviousResponseVector = m_vVectorOfFOM; fnFindMinMaxResponse( ); } // iterations m_lIterationsPerformed = iterations; fnFindMinMaxResponse( ); m_vFinalVector = m_vSimplexVertex[ m_lBestFitVertex ]; m_dFinalFOM = m_vVectorOfFOM[ m_lBestFitVertex ]; return ( make_pair( // return best FOM and best vertex m_dFinalFOM, m_vSimplexVertex[ m_lBestFitVertex ] ) ); }; //--------------------------------------------------------------- private: const size_t m_nVar; size_t m_lMaximumIterations; double m_dSmallestAllowedShift; valarray<double> m_vInitialVector; valarray<double> m_vFinalVector; valarray<double> m_vDeltaVector; valarray<double> m_vVectorOfFOM; valarray<vd> m_vSimplexVertex; // vd is just a typedef // for the Microsoft compiler bool m_bDeltaVectorIsValid; double m_dFinalFOM; size_t m_lBestFitVertex; size_t m_lWorstFitVertex; double m_dBestFitValue; double m_dWorstFitValue; size_t m_lIterationsPerformed; // copy the function object in for safety T m_FunctionObject; //--------------------------------------------------------------- void m_fnComputeDeltaVector ( void ) { size_t i; for ( i=0; i<m_vDeltaVector.size( ); ++i ) { if ( fabs( m_vInitialVector[i] ) > 100.0 * DBL_MIN ) { m_vDeltaVector[i] = 0.01 * m_vInitialVector[i]; } else { m_vDeltaVector = 0.1; } } m_bDeltaVectorIsValid = true; }; //--------------------------------------------------------------- void m_fnSetupSimplex ( void ) { size_t i; m_vVectorOfFOM.resize( m_nVar + 1 ); m_vSimplexVertex.resize( m_nVar + 1 ); valarray<double> tempVertex( m_nVar ); for ( i=0; i<m_nVar+1; ++i ) { tempVertex = m_vInitialVector; if ( i < m_nVar ) tempVertex[i] += m_vDeltaVector[i]; m_vVectorOfFOM[i] = m_FunctionObject( tempVertex ); m_vSimplexVertex[ i ] = tempVertex; } fnFindMinMaxResponse ( ); }; //--------------------------------------------------------------- void fnFindMinMaxResponse( void ) { m_dBestFitValue = m_vVectorOfFOM.min( ); m_dWorstFitValue = m_vVectorOfFOM.max( ); size_t i; m_lBestFitVertex = -1; for ( i=0; i<m_nVar+1; ++i ) { if ( m_vVectorOfFOM[i] == m_dBestFitValue ) { m_lBestFitVertex = i; break; } } for ( i=0; i<m_nVar+1; ++i ) { if ( m_vVectorOfFOM[i] == m_dWorstFitValue ) { m_lWorstFitVertex = i; break; } } }; //--------------------------------------------------------------- valarray<double> fnCalculateSimplexCentroid( void ) const { return ( ( m_vSimplexVertex.sum( ) - m_vSimplexVertex[ m_lWorstFitVertex ] ) / double( m_nVar ) ); } }; // class simplex #endif // if !defined ( SIMPLEX_H_INCLUDED )