/* FFNet_Activation_Categories.c
 *
 * Copyright (C) 1997-2002 David Weenink
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 djmw 19960322
 djmw 20020712 GPL header
*/

#include "FFNet_Activation_Categories.h"

static long winnerTakesAll (I, const float activation[])
{
	iam (FFNet); long i, pos = 1; float max = activation[1];
	for (i=2; i <= my nOutputs; i++)
	{
		if (activation[i] > max)
		{
			max = activation[i]; pos = i;
		}
	}
	return pos;
}

static long stochastic (I, const float activation[])
{
	iam (FFNet); long i; double number;
	float range = 0, lower = 0;
	for (i=1; i <= my nOutputs; i++) range += activation[i];
	number = NUMrandomUniform (0,1)*range;
	for (i=1; i <= my nOutputs; i++) if (number < (lower += activation[i])) break;
	return i;
}

Categories FFNet_Activation_to_Categories (FFNet me, Activation activation, int labeling)
{
	Categories thee = NULL, categories = my outputCategories;
	long i, (*labelingFunction) (I, const float act[]);
	if (! my outputCategories) return Melder_errorp ("FFNet_Activation_to_Categories:"
		"neural net has no categories.");
	if (my nOutputs != activation->nx) return Melder_errorp ("FFNet_Activation_to_Categories:"
		"#columns in activation must equal #outputs of net.");
	if (! (thee = Categories_create ())) return NULL;
	labelingFunction = labeling == 2 ? stochastic : winnerTakesAll;
	for (i=1; i <= activation->ny; i++)
	{
		long index = labelingFunction (me, activation->z[i]);
		Data item = Data_copy (categories->item[index]);
		if (! item || ! Collection_addItem (thee, item)) 
		{
			forget (thee);
			return Melder_errorp ("FFNet_Activation_to_Categories: error creating label %ld.", i);
		}
	}
	return thee;
}

Activation FFNet_Categories_to_Activation (FFNet me, Categories thee)
{
	Activation him = NULL; Categories uCategories;
	long i, nl, cSize = thy size, hasCategories = my outputCategories ? 1 : 0;
	if (! (uCategories = Categories_selectUniqueItems (thee, 1))) 
	{
		 (void) Melder_error ("FFNet_Categories_to_Activation: no memory."); goto error;
	}
	if (! my outputCategories) my outputCategories = uCategories;
	else if (! ( (nl = OrderedOfString_isSubsetOf (uCategories, my outputCategories, NULL)) &&
		nl == uCategories->size && my nOutputs >= uCategories->size))
	{
		 (void) Melder_error ("FFNet_Categories_to_Activation: Categories do not match Categories of neural net.");
		goto error;
	}
	 
	if (! (him = Activation_create (cSize, my nOutputs))) goto error;
	for (i=1; i <= cSize; i++)
	{
		long pos =  OrderedOfString_indexOfItem_c (my outputCategories, OrderedOfString_itemAtIndex_c (thee, i));
		if (pos < 1)
		{
			 (void) Melder_error ("FFNet_Categories_to_Activation: label \"%s\" in Categories does not match"
				" Categories of neural net.", OrderedOfString_itemAtIndex_c (thee, i)); goto error;
		}
		his z[i][pos] = 1.0;
	}
	if (hasCategories) forget (uCategories); 
	return him;
error:
	forget (him); forget (uCategories);
	return Melder_errorp ("FFNet_Categories_to_Activation: not performed.");
}

/* End of file FFNet_Activation_Categories.c */
