/*
 * MiningMart Version 1.0
 * 
 * Copyright (C) 2006 Martin Scholz, Timm Euler, 
 *                    Daniel Hakenjos, Katharina Morik
 *
 * Contact: miningmart@ls8.cs.uni-dortmund.de
 *
 * A list of contributing developers (other than the copyright 
 * holders) can be found at
 * http://mmart.cs.uni-dortmund.de/downloads/download.html
 * 
 * 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, see the file MM_HOME/LICENSE; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */
package edu.udo.cs.miningmart.operator;

import java.sql.SQLException;

import edu.udo.cs.miningmart.exception.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.Columnset;
import edu.udo.cs.miningmart.m4.Value;
import edu.udo.cs.miningmart.m4.utils.Print;

/**
 * This class computes an error for the SupportVectorMachine. It is based
 * on the application of the operator <code>SupportVectorMachineForRegression</code>
 * previously in the operator chain.
 * 
 * @see edu.udo.cs.miningmart.operator.SupportVectorMachineForRegression
 * 
 * @author Timm Euler
 * @version $Id: ComputeSVMError.java,v 1.6 2006/04/11 14:10:10 euler Exp $
 */ 
public class ComputeSVMError extends EvaluateResults
{
	/**
	 * @see edu.udo.cs.miningmart.operator.EvaluateResults#computeResult(double[], double[])
	 */
    public double computeResult(double actualvalue[],double predictedvalue[])
    	throws  M4CompilerError
    {
    	Value lossP = this.getLossFunctionPos();
    	Value lossN = this.getLossFunctionNeg();
    	double lossFunctionPosl, lossFunctionNegl;
    	
    	boolean regression = ((lossP != null) && (lossN != null));
    	
    	if (regression)
    	{
    		lossFunctionPosl=Double.parseDouble(lossP.getValue());
	    	lossFunctionNegl=Double.parseDouble(lossN.getValue());
    	}
    	else
    	{   lossFunctionPosl = lossFunctionNegl = 1.0;  } // equal weight to both
	    
    	double epsilonl=Double.parseDouble(this.getEpsilon().getValue());

		if (regression)
		{    	
			// compute loss:
		    double avgloss = 0;
    		double loss = 0;

		    for(int j=0; j < actualvalue.length; j++)
			{
				// see Rueping, Master Thesis, page 31
				// (linear asymmetric cost function):
		    	if (java.lang.Math.abs(actualvalue[j]-predictedvalue[j]) <= epsilonl)
		    	{
		    		loss=0;
		    	}
			    else if (predictedvalue[j] < (actualvalue[j] - epsilonl))
					 {	
					 	 loss=lossFunctionNegl*(actualvalue[j]-predictedvalue[j]-epsilonl); 
					 }
		 	    	 else if(predictedvalue[j]> (actualvalue[j] + epsilonl) )
					 	  {  
					 		 loss=lossFunctionPosl*(predictedvalue[j]-actualvalue[j]-epsilonl);  
					 	  }
			    avgloss += loss;
			}
		    avgloss/=actualvalue.length;

		    return avgloss;
		}
		else
		{ 	
			// compute accuracy:
			int acc = 0;
			int pos = 0; int neg = 0;
		    for(int j=0; j < actualvalue.length; j++)
			{
				if (actualvalue[j] == predictedvalue[j])
				{   acc++;  }	
				
				// debug/statistics:
				if (predictedvalue[j] == 1.0)  {  pos++;  }
				if (predictedvalue[j] == -1.0) {  neg++;  }
			}
			this.doPrint(Print.OPERATOR, "SVM predicted " + pos + " positives and " + neg + " negatives.");
			return ((double) acc) / actualvalue.length;
		}
    }

	/**
	 * @see edu.udo.cs.miningmart.operator.Operator#createStatement
	 */
    public void createStatement(boolean lazy) throws SQLException, M4CompilerError
    {
    	Value lossP = this.getLossFunctionPos();
    	Value lossN = this.getLossFunctionNeg();
    	
    	boolean regression = ((lossP != null) && (lossN != null));
    	
    	if (regression)
    	{
    		super.createStatement(lazy);
    	}
    	else
    	{    
    		// compute accuracy:
    		Columnset theColumnSet;
    		String inputColumnSetName, actualColumnName, predictedColumnName;
    		try {
	        	theColumnSet = this.getInputConcept().getCurrentColumnSet();
		 		inputColumnSetName = theColumnSet.getSchema()+"."+theColumnSet.getName();
	        	actualColumnName = this.getActualValueAttribute().getCurrentColumn().getSQLDefinition();
		 		predictedColumnName = this.getPredictedValueAttribute().getCurrentColumn().getSQLDefinition();
    		}
    		catch (M4Exception m4e)
    		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  }
    		
			String query = "SELECT COUNT(*) FROM " + inputColumnSetName;
			Long countL = this.getM4Db().executeBusinessSingleValueSqlReadL(query);
			if (countL == null || countL.longValue() == 0) {
				throw new M4CompilerError("ComputeSVMError: tried to compute accuracy; found 0 rows in TheInputConcept!");
			} 
			
			query = "SELECT COUNT(*) FROM (SELECT A, B FROM (SELECT " + actualColumnName + " A, " + predictedColumnName + " B " +
			        "FROM " + inputColumnSetName + ") WHERE A = B)";
			Long countEqualL = this.getM4Db().executeBusinessSingleValueSqlReadL(query);
			if (countEqualL == null) {
				countEqualL = new Long(0);
			}					   

			double acc = ((double) (countEqualL.longValue())) / ((double) (countL.longValue()));
			
			this.doPrint(Print.OPERATOR, "ComputeSVMError: Computed accuracy comparing '" + actualColumnName +
						 "' with '" + predictedColumnName + "' from '" + inputColumnSetName + "': " + acc);
    	}	    
    }
   
	/**
	 * Gets the epsilon.
	 * 
	 * @return Returns a Value
	 */
	public Value getEpsilon() throws M4CompilerError {
		return (Value) this.getSingleParameter("Epsilon");
	}

	/**
	 * Gets the lossFunctionNeg.
	 * 
	 * @return Returns a Value
	 */
	public Value getLossFunctionNeg() throws M4CompilerError {
		return (Value) this.getSingleParameter("LossFunctionNeg");
	}

	/**
	 * Gets the lossFunctionPos.
	 * 
	 * @return Returns a Value
	 */
	public Value getLossFunctionPos() throws M4CompilerError {
		return (Value) this.getSingleParameter("LossFunctionPos");
	}
}
/*
 * Historie
 * --------
 *
 * $Log: ComputeSVMError.java,v $
 * Revision 1.6  2006/04/11 14:10:10  euler
 * Updated license text.
 *
 * Revision 1.5  2006/04/06 16:31:10  euler
 * Prepended license remark.
 *
 * Revision 1.4  2006/03/30 16:07:12  scholz
 * fixed author tags for release
 *
 * Revision 1.3  2006/03/23 11:13:45  euler
 * Improved exception handling.
 *
 * Revision 1.2  2006/01/05 10:27:38  hakenjos
 * Removed Javadoc Warnings!
 *
 * Revision 1.1  2006/01/03 09:54:20  hakenjos
 * Initial version!
 *
 */
