~swaits/differential-evolution

aac0b98ba908bf3618842b66d973db7693700e13 — swaits 16 years ago 6f03c0b
renamed DE::Engine::CalculateError to DE::Engine::TestFitness
added ErrorAccumulator to aid in calculating RMSE
converted piecewiseapprox.cpp and polyapprox.cpp to use new pure virtual method name and ErrorAccumulator
4 files changed, 91 insertions(+), 34 deletions(-)

M de.h
M de.inl
M piecewiseapprox.cpp
M polyapprox.cpp
M de.h => de.h +24 -1
@@ 23,12 23,35 @@ namespace DE
		void SetRange(unsigned int i, double minimum, double maximum);
		void SetRange(const Vector<DIM>& minimum, const Vector<DIM>& maximum);
		
		virtual double CalculateError(const double testcase[DIM], bool& stop) = 0;
		virtual double TestFitness(const double testcase[DIM], bool& stop) = 0;

		void Reset();
		bool Solve(unsigned int maxgenerations = 1000000000);
		bool RunOneGeneration();

	public:

		// helper class for accumulating error and calculating
		// root mean square error
		class ErrorAccumulator
		{
		public:
			ErrorAccumulator();
			virtual ~ErrorAccumulator();

			void Clear();
			void AddTestCase(double expected, double actual);
			double GetRMSE();

		private:

			void CalculateError();

			unsigned int num_tests;         // # of test cases
			double       sum_error_squared; // sum of error^2
			double       error_rmse;        // root mean square error (-1 == "dirty" flag)
		};

	private:

		// randomly select unique individuals

M de.inl => de.inl +51 -1
@@ 207,7 207,7 @@ bool DE::Engine<DIM,POPSIZE>::RunOneGeneration()
		MakeTrial_randtobest1exp(candidate,trial);

		// determine fitness of trial individual
		double trialfitness = CalculateError(trial,success);
		double trialfitness = TestFitness(trial,success);

		// see if the trial improved current candidate in population
		if ( trialfitness < fitness[candidate] )


@@ 448,5 448,55 @@ inline void DE::Engine<DIM,POPSIZE>::MakeTrial_rand2bin(unsigned int candidate, 
	return;
}




template <unsigned int DIM, unsigned int POPSIZE>
inline DE::Engine<DIM,POPSIZE>::ErrorAccumulator::ErrorAccumulator()
{
	Clear();
}

template <unsigned int DIM, unsigned int POPSIZE>
inline DE::Engine<DIM,POPSIZE>::ErrorAccumulator::~ErrorAccumulator()
{
	// empty
}

template <unsigned int DIM, unsigned int POPSIZE>
inline void DE::Engine<DIM,POPSIZE>::ErrorAccumulator::Clear()
{
	num_tests         = 0;
	sum_error_squared = 0.0;
	error_rmse        = 0.0; // initial error == zero
}

template <unsigned int DIM, unsigned int POPSIZE>
inline void DE::Engine<DIM,POPSIZE>::ErrorAccumulator::AddTestCase(double expected, double actual)
{
	double e           = expected - actual; // compute error
	sum_error_squared += e * e;             // add to sum of error^2
	num_tests         += 1;                 // increment total test count
	error_rmse         = -1.0;              // mark as dirty
}

template <unsigned int DIM, unsigned int POPSIZE>
inline double DE::Engine<DIM,POPSIZE>::ErrorAccumulator::GetRMSE()
{
	if ( error_rmse < 0.0 ) // recalculate if set to "dirty"
		CalculateError();

	return error_rmse;
}

template <unsigned int DIM, unsigned int POPSIZE>
inline void DE::Engine<DIM,POPSIZE>::ErrorAccumulator::CalculateError()
{
	// convert squared error to root mean squared error
	error_rmse = sqrt(sum_error_squared / (double)num_tests);
}



#endif // __de_inl__


M piecewiseapprox.cpp => piecewiseapprox.cpp +8 -16
@@ 39,37 39,29 @@ class PolySearch: public DE::Engine<NUM_COEFFICIENTS>
{
	public:
		// this method is called by the DE::Engine::Solve() to test a single DE individual
		double CalculateError(const double testcase[NUM_COEFFICIENTS], bool& stop)
		double TestFitness(const double testcase[NUM_COEFFICIENTS], bool& stop)
		{
			double       error      = 0.0;
			unsigned int test_count = 0;
			ErrorAccumulator e;

			// accumulate squared error
			for ( double i=-2.5;i<=2.5;i+=0.01 )
			{
				// test
				double test   = CalculatePiecewise(i,NUM_COEFFICIENTS,testcase);
				double actual = TargetFunction(i);
				double actual   = CalculatePiecewise(i,NUM_COEFFICIENTS,testcase);
				double expected = TargetFunction(i);

				// accumulate error^2
				double e = test - actual;
				error += e * e;

				// inc test count (divisor to get from SE to MSE)
				test_count++;
				// accumulate error
				e.AddTestCase(expected,actual);
			}

			// convert SE to RMSE
			error = sqrt(error / (double)test_count);

			// set stop flag if the error is really low
			if ( error < 0.0000001 )
			if ( e.GetRMSE() < 0.0000001 )
			{
				stop = true;
			}

			// done
			return error;
			return e.GetRMSE();
		}
};


M polyapprox.cpp => polyapprox.cpp +8 -16
@@ 39,37 39,29 @@ class PolySearch: public DE::Engine<NUM_COEFFICIENTS>
{
	public:
		// this method is called by the DE::Engine::Solve() to test a single DE individual
		double CalculateError(const double testcase[NUM_COEFFICIENTS], bool& stop)
		double TestFitness(const double testcase[NUM_COEFFICIENTS], bool& stop)
		{
			double       error      = 0.0;
			unsigned int test_count = 0;
			ErrorAccumulator e;

			// accumulate squared error
			for ( double i=-1.25;i<=1.25;i+=0.001 )
			{
				// test
				double test   = CalculatePolynomial(i,NUM_COEFFICIENTS,testcase);
				double actual = TargetFunction(i);
				double actual   = CalculatePolynomial(i,NUM_COEFFICIENTS,testcase);
				double expected = TargetFunction(i);

				// accumulate error^2
				double e = test - actual;
				error += e * e;

				// inc test count (divisor to get from SE to MSE)
				test_count++;
				// accumulate error
				e.AddTestCase(expected, actual);
			}

			// convert SE to RMSE
			error = sqrt(error / (double)test_count);

			// set stop flag if the error is really low
			if ( error < 0.0000001 )
			if ( e.GetRMSE() < 0.0000001 )
			{
				stop = true;
			}

			// done
			return error;
			return e.GetRMSE();
		}
};