#include <cstdlib>
#include <iostream>
#include <string>
#include <math.h>
#include "utils.h"
#include <float.h>
#include "onboostnn.h"

//using namespace std;
/* Constructor function. This sets the training example file and number
of base models. It also sets to NULL other variables to be used later. */
onboostnn::onboostnn(datafile* examples_to_train, long new_num_base_models,
				int new_num_epochs, int new_num_hidden, float newlearningrate,
				float newmomentumterm)
{
	if (new_num_epochs > 0)
	{
		num_epochs = new_num_epochs;
	}
	else
	{
		num_epochs = 100;
	}
	
	if (new_num_hidden > 0)
	{
		num_hidden = new_num_hidden;
	}

	if ((examples_to_train != NULL) && (new_num_base_models > 0))
	{
		setup(examples_to_train, new_num_base_models, new_num_epochs,
			new_num_hidden, newlearningrate, newmomentumterm);
	}
	else
	{
		training_examples = NULL;
		basemodels = NULL;
		lambda_correct = NULL;
		lambda_wrong = NULL;
		epsilons = NULL;
		betas = NULL;
		init_num_base_models = num_base_models = 0;
		classifications = NULL;
		votes = NULL;
	}
}


/* Destructor function. */
onboostnn::~onboostnn()
{
	delete [] basemodels;
	basemodels = NULL;
	delete [] lambda_correct;
	lambda_correct = NULL;
	delete [] lambda_wrong;
	lambda_wrong = NULL;
	delete [] epsilons;
	epsilons = NULL;
	delete [] betas;
	betas = NULL;
	delete [] classifications;
	classifications = NULL;
	delete [] votes;
	votes = NULL;
}


/* set_training_example_file:
Input: datafile containing the training examples.
Output: None.
Sets the training example file data structure to point to examples_to_train.
*/
void onboostnn::set_training_example_file(datafile* examples_to_train)
{
	training_examples = examples_to_train;
	
	if (votes != NULL)
	{
		delete [] votes;
	}
	votes = new double[training_examples->NumClasses()];
}


/* setup:
Inputs: datafile data structure, number of base models to be trained.
Output: none
This function sets the training example datafile data structure to point to
examples_to_train and sets the number of base models to be trained to 
new_num_base_models. 
*/
void onboostnn::setup(datafile* examples_to_train, long new_num_base_models,
			int new_num_epochs, int new_num_hidden,
			float newlearningrate, float newmomentumterm)
{
	training_examples = examples_to_train;
		
	if (new_num_base_models > 0)
	{
		init_num_base_models = num_base_models = new_num_base_models;
		
		// For the code below, see if you can just set up 'basemodels'
		// as a *nnbp and then do basemodels[i] = new nnbp(blah, blah)
		// for each i. This way, you can call the explicit-value
		// constructor for all the base models rather than all these
		// setter functions.
		basemodels = new nnbp[init_num_base_models];
		
		for (int i = 0; i < init_num_base_models; i++)
		{
			if (new_num_hidden > 0)
			{
				basemodels[i].set_num_hidden(new_num_hidden);
			}
			
			basemodels[i].set_training_file(training_examples);
			
			if (new_num_epochs > 0)
			{
				basemodels[i].set_num_epochs(new_num_epochs);
			}
			
			basemodels[i].set_learning_rate(newlearningrate);
			basemodels[i].set_momentum_term(newmomentumterm);
		}
		
		lambda_correct = new double[init_num_base_models];
		lambda_wrong = new double[init_num_base_models];
		epsilons = new double[init_num_base_models];
		betas = new double[init_num_base_models];
		
		for (int t = 0; t < init_num_base_models; t++)
		{
			lambda_correct[t] = 0.0;
			lambda_wrong[t] = 0.0;
			epsilons[t] = 0.0;
			betas[t] = 0.0;
		}
		
		classifications = new ClassNo[init_num_base_models];
		votes = new double[training_examples->NumClasses()];


	}
	else
	{
		std::cout << "ILLEGAL NUMBER OF BASE MODELS: "
				<< new_num_base_models << "\n";
		exit(1);
	}
}


/* set_num_epochs: This function sets the number of epochs in the base models.
*/
void onboostnn::set_num_epochs(int new_num_epochs)
{
	for (int t = 0; t < num_base_models; t++)
	{
		basemodels[t].set_num_epochs(new_num_epochs);
	}
}


/* reset:
This function resets the data structures to make it look as though no
training was done. This means resetting the base models contained within
this ensemble. */
void onboostnn::reset()
{
	for (int t = 0; t < init_num_base_models; t++)
	{
		basemodels[t].reset();
		lambda_correct[t] = 0.0;
		lambda_wrong[t] = 0.0;
		epsilons[t] = 0.0;
		betas[t] = 0.0;
	}
	num_base_models = init_num_base_models; // Forget the fact that we
			// might have chosen to use fewer than the full set of base
			// models.
}
		


// Do batch boosting with examples in datafile
// training_examples.
void onboostnn::trainbatch()
{
	trainbatch(0, training_examples->NumItems());
}

// Do batch training with examples 'first' up to 'last' (these
// are indices into the trainingfile).
void onboostnn::trainbatch(long first, long last)
{
	long numexamples = last-first;
	long* sample = new long[numexamples];
	Description* items = new Description[numexamples];
	Boolean* correctp = new Boolean[numexamples];
	double* weights = new double[numexamples];
	
	// Initialize weights.
	for (int j = 0; j < numexamples; j++)
	{
		weights[j] = 1.0;
	}
	
	// For each base model,...
	for (int i = 0; i < init_num_base_models; i++)
	{
		/* Create training sample according to the weights. The array
		'sample' contains indices into the training examples data
		structure. */
		sample = create_sample(sample, weights, first, last);
		
		for (int j = 0; j < numexamples; j++)
		{
			// Put the jth randomly chosen sample into the items
			// array.
			items[j] = (*training_examples)[sample[j]];
		}
		
		// Now that the sample with replacement is ready, learn
		// the next base model.
		basemodels[i].trainbatch(items, numexamples);
		
		// Now we need to adjust the weights.
		// First determine which examples are being misclassified by
		// the current base model and calculate lambda_wrong[i] (the sum
		// of the weights of the incorrectly classified examples) and
		// lambda_correct[i] (the sum of the weights of the correctly
		// classified examples).
		
		for (int j = first; j < last; j++)
		{
			// If the example is being classified correctly, then...
			if (basemodels[i].test((*training_examples)[j]) ==
				training_examples->TrueClass(j))
			{
				// it is classified correctly,...
				correctp[j-first] = 1;
			}
			else
			{
				// else it is misclassified and its weight needs to be
				// added to lambda_wrong.
				correctp[j-first] = 0;
				lambda_wrong[i] += weights[j-first];
			}
		}
		
		// lambda_correct + lambda_wrong = numexamples. This is equivalent
		// to epsilon + (1 - epsilon) = 1 from AdaBoost.
		lambda_correct[i] = numexamples - lambda_wrong[i];
		
		
		// The following condition is equivalent to epsilon > 1/2 in
		// AdaBoost, i.e., under this condition we stop learning more
		// base models.
		if (lambda_wrong[i] >= numexamples / 2.0)
		{
			num_base_models = i; // Note that init_num_base_models (the
					// number of base models allocated) is still correct.
			return;
		}
		
		if (lambda_wrong[i] == 0.0)
		{
			// If this zero-error base model is the only one we have,
			// then we should use it. Otherwise, we should not use it
			// because it will get all the weight when classifying new
			// examples.
			if (i == 0)
			{
				num_base_models = 1;
				return;
			}
			else
			{
				num_base_models = i;
			}
		}
		
		// Now scale up the weights of misclassified examples and scale
		// down the weights of correctly-classified examples. The method
		// being used is equivalent to AdaBoost's method except that it
		// avoids floating-point problems and does not require a separate
		// normalization step, so it is more efficient.
		for (int j = 0; j < numexamples; j++)
		{
			if (correctp[j])
			{	
				weights[j] *= numexamples / (2.0 * lambda_correct[i]);
			}
			else
			{
				weights[j] *= numexamples / (2.0 * lambda_wrong[i]);
			}
		}
		
		// This is the new weight distribution to be tried.
	}
	
	// We don't need the following data structures specific to the
	// learning algorithm.
	delete [] sample;
	sample = NULL;
	delete [] items;
	items = NULL;
	delete [] correctp;
	correctp = NULL;
	delete [] weights;
	weights = NULL;
}


// Do batch training with examples 'first' up to 'last' (these
// are indices into the trainingfile)--with training file in online mode.
void onboostnn::trainonlinebatch(long first, long last)
{
	long numexamples = last-first;
	long* sampleindices = new long[numexamples]; // Indices into the original sample. We
								// use these to create the actual training sample.
	Description* origsample = new Description[numexamples]; // Original training set
	Description* items = new Description[numexamples]; // Sample for current base model.
	Boolean* correctp = new Boolean[numexamples];
	double* weights = new double[numexamples];
	
	// First create the original sample. We have to create it one-at-a-time because
	// the training set's datafile is in online mode.
	
	for (int j = 0; j < numexamples; j++)
	{
		origsample[j] = training_examples->next_example();
	}
	
	// Initialize weights.
	for (int j = 0; j < numexamples; j++)
	{
		weights[j] = 1.0;
	}
	
	// For each base model,...
	for (int i = 0; i < init_num_base_models; i++)
	{
		/* Create training sample according to the weights. The array
		'sampleindices' contains indices into the training set given in
		origsample. */
		sampleindices = create_sample(sampleindices, weights, first, last);
		
		for (int j = 0; j < numexamples; j++)
		{
			// Put the jth randomly chosen sample into the items
			// array.
			items[j] = origsample[sampleindices[j]];
		}
		
		// Now that the sample with replacement is ready, learn
		// the next base model.
		basemodels[i].trainbatch(items, numexamples);
		
		// Now we need to adjust the weights.
		// First determine which examples are being misclassified by
		// the current base model and calculate lambda_wrong[i] (the sum
		// of the weights of the incorrectly classified examples) and
		// lambda_correct[i] (the sum of the weights of the correctly
		// classified examples).
		
		for (int j = first; j < last; j++)
		{
			// If the example is being classified correctly, then...
			short numattributes = training_examples->return_num_attributes();
			DiscrValue exampleclass = origsample[j][numattributes]._discr_val;
			
			if (basemodels[i].test(origsample[j]) == exampleclass)
			{
				// it is classified correctly,...
				correctp[j-first] = 1;
			}
			else
			{
				// else it is misclassified and its weight needs to be
				// added to lambda_wrong.
				correctp[j-first] = 0;
				lambda_wrong[i] += weights[j-first];
			}
		}
		
		// lambda_correct + lambda_wrong = numexamples. This is equivalent
		// to epsilon + (1 - epsilon) = 1 from AdaBoost.
		lambda_correct[i] = numexamples - lambda_wrong[i];
		
// The following ifdef-endif combination was set up by Nikunj Oza
// 8-2-01 to test when the first base model's performance is < 0.5.		
// #ifdef NO			
		// The following condition is equivalent to epsilon > 1/2 in
		// AdaBoost, i.e., under this condition we stop learning more
		// base models.
		if (lambda_wrong[i] >= numexamples / 2.0)
		{
			if (i == 0)
			{
				num_base_models = 1;
				return;
			}
			else
			{
				num_base_models = i; // Note that init_num_base_models (the
						// number of base models allocated) is still correct.
				return;
			}
		}
// #endif
		
		if (lambda_wrong[i] == 0.0)
		{
			// If this zero-error base model is the only one we have,
			// then we should use it. Otherwise, we should not use it
			// because it will get all the weight when classifying new
			// examples.
			if (i == 0)
			{
				num_base_models = 1;
				return;
			}
			else
			{
				num_base_models = i;
			}
		}
		
		// Now scale up the weights of misclassified examples and scale
		// down the weights of correctly-classified examples. The method
		// being used is equivalent to AdaBoost's method except that it
		// avoids floating-point problems and does not require a separate
		// normalization step, so it is more efficient.
		for (int j = 0; j < numexamples; j++)
		{
			if (correctp[j])
			{	
				weights[j] *= numexamples / (2.0 * lambda_correct[i]);
			}
			else
			{
				weights[j] *= numexamples / (2.0 * lambda_wrong[i]);
			}
		}
		
		// This is the new weight distribution to be tried.
	}
	
	// We don't need the following data structures specific to the
	// learning algorithm.
	delete [] origsample;
	origsample = NULL;
	delete [] sampleindices;
	sampleindices = NULL;
	delete [] items;
	items = NULL;
	delete [] correctp;
	correctp = NULL;
	delete [] weights;
	weights = NULL;
}


// Do online boosting learning with the supplied example
void onboostnn::train(Description example)
{
	double lambda = 1.0;
		
	for (int t = 0; t < num_base_models; t++)
	{
//			cout << "     Updating base model: " << t << endl;
			// Number of times example is to be included.
		long k = poisson(lambda);

		for (int n = 0; n < k; n++)
		{
		// The next line is used for weighted training of the base
		// models.
		//	basemodels[t].train((*training_examples)[i], lambda);
			basemodels[t].train(example);
		}
			
		// Check if example is classified correctly.
		Boolean correctp = (basemodels[t].test(example) == 
						training_examples->TrueClass(example));
			
		// Now update the lambda_correct and lambda_wrong. Also update
		// the weight of the current example.
		if (correctp == 1)
		{
			lambda_correct[t] += lambda;
				
		//	double factor = ((i + 1.0) / (2.0 * lambda_correct[t]));
			double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_correct[t]);
			
			// Since the example has been correctly classified, its
			// weight should be decreased, i.e., its multiplier should
				// be less than 1.0
				
//			lambda *= factor;
// The following ifdef-endif combination was set up by Nikunj Oza
// 8-2-01 to test when the first base model's performance is < 0.5.		
// #ifdef NO	
			if (factor < 1.0)
			{
				lambda *= factor;
			}
			else
			{
				lambda *= 1.0 / (2.0 * (1.0 - exp(-1.0)));
			}
// #endif
		}
		else
		{
			lambda_wrong[t] += lambda;
				
		//	double factor = ((i + 1.0) / (2.0 * lambda_wrong[t]));
			double factor = (lambda_correct[t] + lambda_wrong[t]) /
							(2.0 * lambda_wrong[t]);
				
			// Since the example has been misclassified, its weight
			// should be increased, i.e., its multiplier should be
			// greater than 1.0.
			
//			lambda *= factor;
// The following ifdef-endif combination was set up by Nikunj Oza
// 8-2-01 to test when the first base model's performance is < 0.5.		
// #ifdef NO	
			if (factor > 1.0)
			{
				lambda *= factor;
			}
			else
			{
				 lambda *= 1.0 / (2.0 * exp(-1.0));
			}
// #endif
		}
	}
}

// Do online boosting on the training set in the datafile 
//'training_examples.'
void onboostnn::train()
{
	train(0, training_examples->NumItems());
}

// Do online boosting on the training set in the datafile 
// 'training_examples.' (from 'first' up to 'last,' but not including 
// 'last.')
void onboostnn::train(long first, long last)
{
	for (int i = first; i < last; i++)
	{
//		cout << "Learning example: " << i << endl;
		// Set initial weight.
		double lambda = 1.0;
		
		for (int t = 0; t < num_base_models; t++)
		{
//			cout << "     Updating base model: " << t << endl;
			// Number of times example is to be included.
			long k = poisson(lambda);

			for (int n = 0; n < k; n++)
			{
			// The next line is used for weighted training of the base
			// models.
			//	basemodels[t].train((*training_examples)[i], lambda);
				basemodels[t].train((*training_examples)[i]);
			}
			
			// Check if example is classified correctly.
			Boolean correctp = (basemodels[t].test((*training_examples)[i]) ==
								training_examples->TrueClass(i));
			
			// Now update the lambda_correct and lambda_wrong. Also update
			// the weight of the current example.
			if (correctp == 1)
			{
				lambda_correct[t] += lambda;
				
			//	double factor = ((i + 1.0) / (2.0 * lambda_correct[t]));
				double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_correct[t]);
			
				// Since the example has been correctly classified, its
				// weight should be decreased, i.e., its multiplier should
				// be less than 1.0	
				if (factor < 1.0)
				{
					lambda *= factor;
				}
				else
				{
					 lambda *= 1.0 / (2.0 * (1.0 - exp(-1.0)));
				}
			}
			else
			{
				lambda_wrong[t] += lambda;
				
			//	double factor = ((i + 1.0) / (2.0 * lambda_wrong[t]));
				double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_wrong[t]);
				
				// Since the example has been misclassified, its weight
				// should be increased, i.e., its multiplier should be
				// greater than 1.0.
				if (factor > 1.0)
				{
					lambda *= factor;
				}
				else
				{
					 lambda *= 1.0 / (2.0 * exp(-1.0));
				}
			}
		}
	}
}
			

/* This version of train calculates the errors of each online-updated base model on
all the training examples seen earlier and returns these in one file per base model. This
is to see how "stable" the base models are.
*/
void onboostnn::traintest(long first, long last)
{
	std::vector< std::vector<double> > bmc(num_base_models, std::vector<double>(last-first, 0.0));
	std::vector< std::vector<double> > oberror(num_base_models, std::vector<double>(last-first, 0.0));
	std::vector< std::vector<double> > onlineerror(num_base_models, std::vector<double>(last-first, 0.0));
	
	for (int i = first; i < last; i++)
	{
//		cout << "Learning example: " << i << endl;
		// Set initial weight.
		double lambda = 1.0;
		
		for (int t = 0; t < num_base_models; t++)
		{
//			cout << "     Updating base model: " << t << endl;
			// Number of times example is to be included.
			long k = poisson(lambda);

			for (int n = 0; n < k; n++)
			{
			// The next line is used for weighted training of the base
			// models.
			//	basemodels[t].train((*training_examples)[i], lambda);
				basemodels[t].train((*training_examples)[i]);
			}
			
			// Check if example is classified correctly.
			Boolean correctp = (basemodels[t].test((*training_examples)[i]) ==
								training_examples->TrueClass(i));
			
			// Now update the lambda_correct and lambda_wrong. Also update
			// the weight of the current example.
			if (correctp == 1)
			{
				lambda_correct[t] += lambda;
				
			//	double factor = ((i + 1.0) / (2.0 * lambda_correct[t]));
				double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_correct[t]);
			
				// Since the example has been correctly classified, its
				// weight should be decreased, i.e., its multiplier should
				// be less than 1.0	
				if (factor < 1.0)
				{
					lambda *= factor;
				}
				else
				{
					 lambda *= 1.0 / (2.0 * (1.0 - exp(-1.0)));
				}
			}
			else
			{
				lambda_wrong[t] += lambda;
				
			//	double factor = ((i + 1.0) / (2.0 * lambda_wrong[t]));
				double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_wrong[t]);
				
				// Since the example has been misclassified, its weight
				// should be increased, i.e., its multiplier should be
				// greater than 1.0.
				if (factor > 1.0)
				{
					lambda *= factor;
				}
				else
				{
					 lambda *= 1.0 / (2.0 * exp(-1.0));
				}
			}
			
			
			// We will calculate the "online-batch" error (the error on all past training
			// examples of the latest online base model.
			
			// We will also update the bmc (base model classification error) here.
			
			// Now, go through the previously-seen examples and test the base model
			// on each of them.
			for (int j = first; j <= i; j++)
			{
				Boolean correctp = (basemodels[t].test((*training_examples)[j]) ==
								training_examples->TrueClass(j));
				
				if (correctp == 0)
				{
					oberror[t][i] += 1.0;
				}
				else
				{
					bmc[t][j] += 1.0;
				}
			}
			
			// Now divide by the number of examples seen earlier.
			oberror[t][i] /= (i+1)-first;
						
			// Calculate the online error.
			onlineerror[t][i] = lambda_wrong[t] / (lambda_correct[t] + lambda_wrong[t]);
		}
	}
	
	// Normalize bmc by dividing by the number of base models that have been evaluated
	// on example j.
	for (int t = 0; t < num_base_models; t++)
	{
		for (int j = first; j < last; j++)
		{
			bmc[t][j] /= (last-j);
		}
	}
	
	// Open the file where the classification results are to be placed.
	char basemodelnum[100];
	
	for (int t = 0; t < num_base_models; t++)
	{
		sprintf(basemodelnum, "%d\0", t+1);
		std::string baseclassesfilename = "classifications";
		baseclassesfilename = baseclassesfilename + basemodelnum;
			
		// Stupid additional code required for this to work under GCC.
		// GCC has an obviously incorrect implementation of the string library.
		// I.e., the function string::data() is supposed to return a char* version of 
		// the string, but it instead nondeterministically returns some corrupted version
		// of the string. For this reason, we have to create the char* versions manually.
		char* bcfchars = new char[baseclassesfilename.size()+1];
		
		for (int m = 0; m < baseclassesfilename.size(); m++)
		{
			bcfchars[m] = baseclassesfilename[m];
		}
		bcfchars[baseclassesfilename.size()] = '\0';
		// End of code needed to spoonfeed GNU C++.
			
		std::ofstream baseclasses(bcfchars, ios::app);
		//baseclasses.seekp(0,ios::end);
		delete [] bcfchars;
	
		for (int j = first; j < last; j++)
		{
			baseclasses << bmc[t][j] << " " << oberror[t][j] << " " << onlineerror[t][j] << "\n";
		}
		
		baseclasses.close();
	}
}
			
			

// Do online boosting on the training set in the datafile 
// 'training_examples.' drawn one-at-a-time (from 'first' up to 'last,' 
// but not including 'last.')
void onboostnn::train2(long first, long last)
{
	for (int i = first; i < last; i++)
	{
		Description Item = training_examples->next_example();
//		cout << "Learning example: " << i << endl;
		// Set initial weight.
		double lambda = 1.0;
		
		for (int t = 0; t < num_base_models; t++)
		{
//			cout << "     Updating base model: " << t << endl;
			// Number of times example is to be included.
			long k = poisson(lambda);

			for (int n = 0; n < k; n++)
			{
			// The next line is used for weighted training of the base
			// models.
			//	basemodels[t].train((*training_examples)[i], lambda);
				basemodels[t].train(Item);
			}

			// Check if example is classified correctly.
			Boolean correctp = (basemodels[t].test(Item) ==
								training_examples->TrueClass(Item));
			
			// Now update the lambda_correct and lambda_wrong. Also update
			// the weight of the current example.
			if (correctp == 1)
			{
				lambda_correct[t] += lambda;
				
			//	double factor = ((i + 1.0) / (2.0 * lambda_correct[t]));
				double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_correct[t]);

				// Since the example has been correctly classified, its
				// weight should be decreased, i.e., its multiplier should
				// be less than 1.0					
				if (factor < 1.0)
				{
					lambda *= factor;
				}
				else
				{
					 lambda *= 1.0 / (2.0 * (1.0 - exp(-1.0)));
				}
			}
			else
			{
				lambda_wrong[t] += lambda;
				
			//	double factor = ((i + 1.0) / (2.0 * lambda_wrong[t]));
				double factor = (lambda_correct[t] + lambda_wrong[t]) /
								(2.0 * lambda_wrong[t]);
				
				// Since the example has been misclassified, its weight
				// should be increased, i.e., its multiplier should be
				// greater than 1.0.	
				if (factor > 1.0)
				{
					lambda *= factor;
				}
				else
				{
					 lambda *= 1.0 / (2.0 * exp(-1.0));
				}
			}
		}
	}
}


// Test the example 'instance' using the learned boosted ensemble.
ClassNo onboostnn::test(Description instance)
{
  return test(instance, num_base_models);
}


// Test the example 'instance' using the first 'use_base_models' number
// of base models from the learned boosted ensemble.
ClassNo onboostnn::test(Description instance, long use_base_models)
{
	// Classify the instance using each base model.
	// Recall that 'classifications' contains each base model's
	// classification of the instance. 
	for (int t = 0; t < use_base_models; t++)
	{
		classifications[t] = basemodels[t].test(instance);
	}
	
	// Initialize the weighted votes for each class.
	// votes contains, for each class, the sum of log(1/beta) values
	// by the base models that voted for that class.
	for (int i = 0; i < training_examples->NumClasses(); i++)
	{
		votes[i] = 0.0;
	}

	// Calculate the epsilons and then beta values needed to weight the
	// votes of each base model.
	
	for (int t = 0; t < use_base_models; t++)
	{
		epsilons[t] = lambda_wrong[t] / (lambda_wrong[t] + lambda_correct[t]);
		
		if (epsilons[t] == 1.0)
		{
			betas[t] = FLT_MAX; // If error is maximized, then maximize beta,
						// thereby giving this base model very low weight.
		}
		else
		{
			betas[t] = epsilons[t] / (1.0 - epsilons[t]);
		}
	}
	
	// The trigger is set up so that if the mth base model has an error of
	// more than 0.5, we don't use it or any subsequent base models to
	// classify new examples.
	int trigger = 0;
	// Add up the log(1/beta) values for each class.
	for (int t = 0; t < use_base_models; t++)
	{
//		if (!((betas[t] >= 1.0) || (betas[t] == 0.0)))
//		{
//			votes[classifications[t]] += log(1.0 / betas[t]);
//		}
// The following ifdef-endif combination was set up by Nikunj Oza
// 8-2-01 to test when the first base model's performance is < 0.5.		
// #ifdef NO	
		if ( !((betas[t] >= 1.0) || (betas[t] == 0.0) || (trigger == 1)))
		{
			votes[classifications[t]] += log(1.0 / betas[t]);
		}
		
		if (betas[t] >= 1.0)
		{
			trigger = 1;
		}
// #endif
	}
	
	int answer = argmax(votes, training_examples->NumClasses());	
	return answer;
}


/* create_sample:
Inputs: Array of indices (to be overwritten) into the training set, the
weights of each example (THE WEIGHTS ARE POISSON PARAMETERS, i.e., a
'sampleweight' of 1.0 corresponds to an AdaBoost weight of 1.0/n where 'n'
is the number of training examples), and the indices of the first and last
examples to be considered for sampling.
Output: Array of indices into the training set. These indices correspond to
example sampled with replacement according to the supplied weights.
*/
long* onboostnn::create_sample(long* sample, double* sampleweights,
								long first, long last)
{	
	// First we find the sum of the weights---we want to choose a random value
	// from 0 to this sum and choose a training example accordingly.
	double weightssum = sampleweights[0];
	for (int j = 1; j < last-first; j++)
	{
		weightssum += sampleweights[j];
	}
	
	// For each training example, choose a random example out of the
	// entire set of examples.
	for (int i = 0; i < last-first; i++)
	{
		double randdouble = double(rand()) / RAND_MAX * weightssum;
			
		int j = first; // Index to example.
		double sumsofar = sampleweights[0];
		while ((randdouble > sumsofar) && (j < last))
		{
			j++;
			sumsofar += sampleweights[j-first];
		}
		
		// In case, due to rounding error, the weights do not quite
		// add up to 1 and the random number chosen happens to be
		// greater than the weight sum...
		if (j >= last)
		{
			j = last - 1;
		}
		
		// Example j is the one to be included.
		
		sample[i] = j;
	}

	return sample;
}


// The following function returns the beta value for a given base model.
double onboostnn::return_beta(int modelnum)
{
	if (modelnum <= num_base_models)
	{
		return betas[modelnum-1];
	}
	else
	{
		std::cout << "Error in onboostnn::return_beta: The model number entered ("
			<< modelnum << ") is out of range. The range is 1 to " << num_base_models
			<< "\n";
		exit(1);
	}
}


long onboostnn::return_num_base_models()
{
	return num_base_models;
}

Recommended Answers

All 2 Replies

Damn, forum rules much...did you read?

I'd suggest that you don't post that many lines of code. You are refering to line numbers, yet you didnt show line numbers....[code=language] [/code] tags would have done the job, I suggest you shrink it down to a small example that has the same problem.

Chris

1. I agree with Freaky_Chris remarks 100%...
2. Qualify ios class name with std:: namespace prefix.
I don't know why but as usually programmers don't believe that compiler diagnostic is the best source of the right solution ;)...

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.