//Copyright (C) 2005 Richard J. Northedge
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//This file is based on the GISModel.java source file found in the
//original java implementation of MaxEnt. That source file contains the following header:
// Copyright (C) 2001 Jason Baldridge and Gann Bierner
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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 Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
using System;
using System.Collections.Generic;
using System.Text;
namespace BotSharp.MachineLearning
{
///
/// A maximum entropy model which has been trained using the Generalized
/// Iterative Scaling procedure.
///
///
/// Tom Morton and Jason Baldridge
///
///
/// Richard J. Northedge
///
///
/// based on GISModel.java, $Revision: 1.13 $, $Date: 2004/06/11 20:51:44 $
///
public sealed class GisModel : IMaximumEntropyModel
{
private readonly IO.IGisModelReader _reader;
private readonly string[] _outcomeNames;
private readonly int _outcomeCount;
private readonly double _initialProbability;
private readonly double _correctionConstantInverse;
private readonly int[] _featureCounts;
///
/// Constructor for a maximum entropy model trained using the
/// Generalized Iterative Scaling procedure.
///
///
/// A reader providing the data for the model.
///
public GisModel(IO.IGisModelReader reader)
{
this._reader = reader;
_outcomeNames = reader.GetOutcomeLabels();
CorrectionConstant = reader.CorrectionConstant;
CorrectionParameter = reader.CorrectionParameter;
_outcomeCount = _outcomeNames.Length;
_initialProbability = Math.Log(1.0 / _outcomeCount);
_correctionConstantInverse = 1.0 / CorrectionConstant;
_featureCounts = new int[_outcomeCount];
}
// implementation of IMaxentModel -------
///
/// Returns the number of outcomes for this model.
///
///
/// The number of outcomes.
///
public int OutcomeCount
{
get
{
return (_outcomeCount);
}
}
///
/// Evaluates a context.
///
///
/// A list of string names of the contextual predicates
/// which are to be evaluated together.
///
///
/// An array of the probabilities for each of the different
/// outcomes, all of which sum to 1.
///
public double[] Evaluate(string[] context)
{
return Evaluate(context, new double[_outcomeCount]);
}
///
/// Use this model to evaluate a context and return an array of the
/// likelihood of each outcome given that context.
///
///
/// The names of the predicates which have been observed at
/// the present decision point.
///
///
/// This is where the distribution is stored.
///
///
/// The normalized probabilities for the outcomes given the
/// context. The indexes of the double[] are the outcome
/// ids, and the actual string representation of the
/// outcomes can be obtained from the method
/// GetOutcome(int outcomeIndex).
///
public double[] Evaluate(string[] context, double[] outcomeSums)
{
for (int outcomeIndex = 0; outcomeIndex < _outcomeCount; outcomeIndex++)
{
outcomeSums[outcomeIndex] = _initialProbability;
_featureCounts[outcomeIndex] = 0;
}
foreach (string con in context)
{
_reader.GetPredicateData(con, _featureCounts, outcomeSums);
}
double normal = 0.0;
for (int outcomeIndex = 0;outcomeIndex < _outcomeCount; outcomeIndex++)
{
outcomeSums[outcomeIndex] = Math.Exp((outcomeSums[outcomeIndex] * _correctionConstantInverse) + ((1.0 - (_featureCounts[outcomeIndex] / CorrectionConstant)) * CorrectionParameter));
normal += outcomeSums[outcomeIndex];
}
for (int outcomeIndex = 0; outcomeIndex < _outcomeCount;outcomeIndex++)
{
outcomeSums[outcomeIndex] /= normal;
}
return outcomeSums;
}
///
/// Return the name of the outcome corresponding to the highest likelihood
/// in the parameter outcomes.
///
///
/// A double[] as returned by the Evaluate(string[] context)
/// method.
///
///
/// The name of the most likely outcome.
///
public string GetBestOutcome(double[] outcomes)
{
int bestOutcomeIndex = 0;
for (int currentOutcome = 1; currentOutcome < outcomes.Length; currentOutcome++)
if (outcomes[currentOutcome] > outcomes[bestOutcomeIndex])
{
bestOutcomeIndex = currentOutcome;
}
return _outcomeNames[bestOutcomeIndex];
}
///
/// Return a string matching all the outcome names with all the
/// probabilities produced by the Evaluate(string[] context)
/// method.
///
///
/// A double[] as returned by the
/// eval(string[] context)
/// method.
///
///
/// string containing outcome names paired with the normalized
/// probability (contained in the double[] outcomes)
/// for each one.
///
public string GetAllOutcomes(double[] outcomes)
{
if (outcomes.Length != _outcomeNames.Length)
{
throw new ArgumentException("The double array sent as a parameter to GisModel.GetAllOutcomes() must not have been produced by this model.");
}
else
{
var outcomeInfo = new StringBuilder(outcomes.Length * 2);
outcomeInfo.Append(_outcomeNames[0]).Append("[").Append(outcomes[0].ToString("0.0000", System.Globalization.CultureInfo.CurrentCulture)).Append("]");
for (int currentOutcome = 1; currentOutcome < outcomes.Length; currentOutcome++)
{
outcomeInfo.Append(" ").Append(_outcomeNames[currentOutcome]).Append("[").Append(outcomes[currentOutcome].ToString("0.0000", System.Globalization.CultureInfo.CurrentCulture)).Append("]");
}
return outcomeInfo.ToString();
}
}
///
/// Return the name of an outcome corresponding to an integer ID value.
///
///
/// An outcome ID.
///
///
/// The name of the outcome associated with that ID.
///
public string GetOutcomeName(int outcomeIndex)
{
return _outcomeNames[outcomeIndex];
}
///
/// Gets the index associated with the string name of the given outcome.
///
///
/// the string name of the outcome for which the
/// index is desired
///
///
/// the index if the given outcome label exists for this
/// model, -1 if it does not.
///
public int GetOutcomeIndex(string outcome)
{
for (int iCurrentOutcomeName = 0; iCurrentOutcomeName < _outcomeNames.Length; iCurrentOutcomeName++)
{
if (_outcomeNames[iCurrentOutcomeName] == outcome)
{
return iCurrentOutcomeName;
}
}
return - 1;
}
///
/// Provides the predicates data structure which is part of the encoding of the maxent model
/// information. This method will usually only be needed by
/// GisModelWriters.
///
///
/// Dictionary containing PatternedPredicate objects.
///
public Dictionary GetPredicates()
{
return _reader.GetPredicates();
}
///
/// Provides the list of outcome patterns used by the predicates. This method will usually
/// only be needed by GisModelWriters.
///
///
/// Array of outcome patterns.
///
public int[][] GetOutcomePatterns()
{
return _reader.GetOutcomePatterns();
}
///
/// Provides the outcome names data structure which is part of the encoding of the maxent model
/// information. This method will usually only be needed by
/// GisModelWriters.
///
///
/// Array containing the outcome names.
///
public string[] GetOutcomeNames()
{
return _outcomeNames;
}
///
/// Provides the model's correction constant.
/// This property will usually only be needed by GisModelWriters.
///
public int CorrectionConstant { get; private set; }
///
/// Provides the model's correction parameter.
/// This property will usually only be needed by GisModelWriters.
///
public double CorrectionParameter { get; private set; }
}
}