/* Copyright (C) 2011 Univ. of Massachusetts Amherst, Computer Science Dept.
   This file is part of "MALLET" (MAchine Learning for LanguagE Toolkit).
   http://www.cs.umass.edu/~mccallum/mallet
   This software is provided under the terms of the Common Public License,
   version 1.0, as published by http://www.opensource.org.  For further
   information, see the file `LICENSE' included with this distribution. */

package cc.mallet.classify.constraints.ge;

/**
 * Expectation constraint for use with GE.
 * Penalizes L_2^2 difference from target expectation. 
 * 
 * Multiple constraints are grouped together here
 * to make things more efficient.
 * 
 * @author Gregory Druck
 */

public class MaxEntL2FLGEConstraints extends MaxEntFLGEConstraints {
  
  private boolean normalize;
  
  public MaxEntL2FLGEConstraints(int numFeatures, int numLabels, boolean useValues, boolean normalize) {
    super(numFeatures, numLabels, useValues);
    this.normalize = normalize;
  }
  
  public double getValue() {
    double value = 0.0;
    for (int fi : constraints.keys()) {
      MaxEntFLGEConstraint constraint = constraints.get(fi);
      if ( constraint.count > 0.0) {
        // value due to current constraint
        double featureValue = 0.0;
        for (int labelIndex = 0; labelIndex < numLabels; ++labelIndex) {
          double ex;
          if (normalize) {
            ex = constraint.expectation[labelIndex]/constraint.count;
          }
          else {
            ex = constraint.expectation[labelIndex];
          }
          featureValue -= Math.pow(constraint.target[labelIndex] - ex,2);
        }
        assert(!Double.isNaN(featureValue) &&
               !Double.isInfinite(featureValue));
        value += featureValue * constraint.weight;
      }
    }
    return value;
  }
  
  @Override
  public void addConstraint(int fi, double[] ex, double weight) {
    constraints.put(fi,new MaxEntL2FLGEConstraint(ex,weight));
  }
  
  protected class MaxEntL2FLGEConstraint extends MaxEntFLGEConstraint {
    public MaxEntL2FLGEConstraint(double[] target, double weight) {
      super(target, weight);
    }

    @Override
    public double getValue(int li) {
      assert(this.count != 0);
      
      if (normalize) {
        return 2 * this.weight * (target[li] / count - expectation[li] / (count * count));
      }
      else {
        return 2 * this.weight * (target[li] - expectation[li]);
      }
    }
  }
}
