/*******************************************************************************
*   Copyright 2013 Analog Devices, Inc.
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
********************************************************************************/

package com.analog.lyric.dimple.factorfunctions;

import static com.analog.lyric.math.Utilities.*;

import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.factorfunctions.core.FactorFunction;
import com.analog.lyric.dimple.model.values.Value;


/**
 * Parameterized discrete transition factor, which corresponds to p(y | x, A),
 * where A is a matrix of transition probabilities that are
 * parameterized as *unnormalized* probabilities.
 * The transition matrix is organized such that columns correspond to
 * the output distribution for each input state. That is, the transition
 * matrix multiplies on the left.  The domain of x and y do not need to be
 * the same.
 * <p>
 * Representing A as described, the conjugate prior for A is such that
 * each entry of the A matrix is independently distributed according to
 * a Gamma distribution, all with a common Beta parameter.
 * Depending on the solver, it may or may not be necessary to use a
 * conjugate prior (for the Gibbs solver, for example, it is not).
 * <p>
 * The variables in the argument list are ordered as follows:
 * <ol>
 * <li>y: Discrete output variable
 * <li>x: Discrete input variable
 * <li>...: The entries of the transition matrix flattened out in column major order (the standard
 * multidimensional array order used by MATLAB).
 * </ol>
 */
public class DiscreteTransitionUnnormalizedParameters extends FactorFunction
{
	/*-------
	 * State
	 */

	private final static int NUM_DATA_ARGUMENTS = 2;

	protected final int _yDimension;
	protected final int _xDimension;

	/*--------------
	 * Construction
	 */
	
	public DiscreteTransitionUnnormalizedParameters(int dimension) {this(dimension, dimension);}	// Square transition matrix

	public DiscreteTransitionUnnormalizedParameters(int yDimension, int xDimension)
	{
		super();
		_yDimension = yDimension;
		_xDimension = xDimension;
	}
	
	/*------------------------
	 * FactorFunction methods
	 */
	
    @Override
	public final double evalEnergy(Value[] arguments)
    {
    	if (arguments.length != _xDimension*_yDimension + NUM_DATA_ARGUMENTS)
    		throw new DimpleException("Incorrect number of arguments.");
    	
    	final int y = arguments[0].getIndexOrInt();				// First argument is y (output variable)
    	final int x = arguments[1].getIndexOrInt();				// Second argument is x (input variable)
    	
    	// Beginning of the column for the given value of x (matrix is scanned by columns)
    	final int colStartIndex = x * _yDimension + NUM_DATA_ARGUMENTS;
    	
    	double sum = 0;
    	for (int index = colStartIndex + _yDimension; --index>=colStartIndex;)
    	{
    		final double a = arguments[index].getDouble();
    		if (a < 0) return Double.POSITIVE_INFINITY;
    		sum += a;
    	}
    	
    	return weightToEnergy(arguments[colStartIndex + y].getDouble()) + Math.log(sum);
	}
    
    
    // Factor-specific methods
    public final int getXDimension()
    {
    	return _xDimension;
    }
    public final int getYDimension()
    {
    	return _yDimension;
    }
    public final int getNumParameters()
    {
    	return _xDimension * _yDimension;
    }
}
