using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace BotSharp.Algorithm.Bayesian
{
///
/// Class DataSet.
///
[DebuggerDisplay("Data set for class P({Class.Name})={Class.Probability}")]
public sealed class DataSet : IDataSet
{
///
/// The default smoothing alpha
///
public const double DefaultSmoothingAlpha = 0D;
///
/// The token count
///
private readonly ConcurrentDictionary _tokenCount = new ConcurrentDictionary();
///
/// The set size, i.e. the number of all tokens
///
private long _setSize;
///
/// Gets the number of distinct tokens,
/// i.e. every token counted at exactly once.
///
/// The token count.
///
public long TokenCount
{
get { return _tokenCount.Count; }
}
///
/// Gets the size of the set.
///
/// The size of the set.
///
public long SetSize
{
get { return _setSize; }
}
///
/// Gets the class.
///
/// The class.
public IClass Class { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The class.
/// @class
public DataSet(IClass @class)
{
if (ReferenceEquals(@class, null)) throw new ArgumentNullException("class");
Class = @class;
}
///
/// Gets the with the specified token.
///
/// The token.
/// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied.
/// TokenInformation<IToken>.
/// token
public TokenInformation this[IToken token, double alpha = DefaultSmoothingAlpha]
{
get
{
if (ReferenceEquals(token, null)) throw new ArgumentNullException("token");
long count;
if (!_tokenCount.TryGetValue(token, out count))
{
return new TokenInformation(token, 0L, 0D);
}
var percentage = GetPercentage(count, alpha);
return new TokenInformation(token, count, percentage);
}
}
///
/// Gets the number of occurrences of the given token.
///
/// The token.
/// System.Int64.
///
public long GetCount(IToken token)
{
if (ReferenceEquals(token, null)) throw new ArgumentNullException("token");
long count;
return !_tokenCount.TryGetValue(token, out count) ? 0 : count;
}
///
/// Gets the approximated percentage of the given
/// in this data set
/// by determining its occurrence count over the whole population.
///
/// The token.
/// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied.
/// System.Double.
/// token
///
public double GetPercentage(IToken token, double alpha = DefaultSmoothingAlpha)
{
if (ReferenceEquals(token, null)) throw new ArgumentNullException("token");
if (alpha < 0) throw new ArgumentOutOfRangeException("alpha", alpha, "Smoothing parameter alpha must be greater than or equal to zero.");
var count = GetCount(token);
return GetPercentage(count, alpha);
}
///
/// Gets the approximated percentage of the given
/// in this data set
/// by determining its occurrence count over the whole population.
///
/// The token count.
/// Additive smoothing parameter. If set to zero, no Laplace smoothing will be applied.
/// System.Double.
/// token
///
private double GetPercentage(long tokenCount, double alpha = DefaultSmoothingAlpha)
{
Debug.Assert(alpha >= 0, "alpha >= 0");
Debug.Assert(tokenCount >= 0, "tokenCount >= 0");
var totalCount = _setSize; // TODO: cache inverse set size
var vocabularySize = TokenCount;
return (double)(tokenCount + alpha)/(double)(totalCount + alpha*vocabularySize);
}
///
/// Adds the given tokens a single time, incrementing the
/// and, at the first addition, the .
///
/// The token.
/// The additional tokens.
///
/// token
/// or
/// additionalTokens
///
public void AddToken(IToken token, params IToken[] additionalTokens)
{
if (ReferenceEquals(token, null)) throw new ArgumentNullException("token");
if (ReferenceEquals(additionalTokens, null)) throw new ArgumentNullException("additionalTokens");
_tokenCount.AddOrUpdate(token, AddFirsIToken, IncremenITokenCount);
Interlocked.Increment(ref _setSize);
AddToken(additionalTokens);
}
///
/// Adds the given tokens a single time, incrementing the
/// and, at the first addition, the .
///
/// The tokens.
/// tokens
public void AddToken(IEnumerable tokens)
{
if (ReferenceEquals(tokens, null)) throw new ArgumentNullException("tokens");
foreach (var token in tokens)
{
_tokenCount.AddOrUpdate(token, AddFirsIToken, IncremenITokenCount);
Interlocked.Increment(ref _setSize);
}
}
///
/// Removes the given tokens a single time, decrementing the and,
/// eventually, the .
///
/// The token.
/// The additional tokens.
///
/// token
/// or
/// additionalTokens
///
///
public void RemoveTokenOnce(IToken token, params IToken[] additionalTokens)
{
if (ReferenceEquals(token, null)) throw new ArgumentNullException("token");
if (ReferenceEquals(additionalTokens, null)) throw new ArgumentNullException("additionalTokens");
RemoveSingleTokenInternal(token);
RemoveTokenOnce(additionalTokens);
}
///
/// Removes the given tokens a single time, decrementing the and,
/// eventually, the .
///
/// The tokens.
/// tokens
///
public void RemoveTokenOnce(IEnumerable tokens)
{
if (ReferenceEquals(tokens, null)) throw new ArgumentNullException("tokens");
foreach (var token in tokens)
{
RemoveSingleTokenInternal(token);
}
}
///
/// Removes the given tokens a single time, decrementing the and,
/// eventually, the .
///
/// The token.
/// The additional tokens.
///
/// token
/// or
/// additionalTokens
///
///
public void PurgeToken(IToken token, params IToken[] additionalTokens)
{
if (ReferenceEquals(token, null)) throw new ArgumentNullException("token");
if (ReferenceEquals(additionalTokens, null)) throw new ArgumentNullException("additionalTokens");
PurgeTokenInternal(token);
PurgeToken(additionalTokens);
}
///
/// Removes the given tokens a single time, decrementing the and,
/// eventually, the .
///
/// The tokens.
/// tokens
///
public void PurgeToken(IEnumerable tokens)
{
if (ReferenceEquals(tokens, null)) throw new ArgumentNullException("tokens");
foreach (var token in tokens)
{
PurgeTokenInternal(token);
}
}
///
/// Purges the tokens fulfilling the given predicate.
///
/// The predicate.
public void PurgeWhere(Predicate predicate)
{
var candidateForPurge = from pair in _tokenCount
let tokenCount = new TokenCount(pair.Key, pair.Value)
where predicate(tokenCount)
select pair.Key;
PurgeToken(candidateForPurge);
}
///
/// Removes the single token internally.
///
/// The token.
private void RemoveSingleTokenInternal(IToken token)
{
long count;
while (_tokenCount.TryGetValue(token, out count))
{
var newValue = count - 1;
var collectionUpdated = _tokenCount.TryUpdate(token, newValue: newValue, comparisonValue: count);
if (!collectionUpdated) continue;
Interlocked.Decrement(ref _setSize);
if (newValue == 0)
{
// explicit removal if the count is zero
var collection = _tokenCount as ICollection>;
collection.Remove(new KeyValuePair(token, 0));
}
break;
}
}
///
/// Purges a single token internally.
///
/// The token.
private void PurgeTokenInternal(IToken token)
{
long count;
if (!_tokenCount.TryRemove(token, out count)) return;
// decrement 'count' times
// TODO: use Interlocked.CompareExchange
for (int i = 0; i < count; ++i)
{
Interlocked.Decrement(ref _setSize);
}
}
///
/// Factory to initialize the value in for the given .
///
/// The token.
/// System.Int64.
private static long AddFirsIToken(IToken token)
{
return 1;
}
///
/// Factory to increment the value in for the given .
///
/// The token.
/// The number of tokens.
/// System.Int64.
private static long IncremenITokenCount(IToken token, long count)
{
return count + 1;
}
///
/// Returns an enumerator that iterates through the collection.
///
/// A that can be used to iterate through the collection.
public IEnumerator GetEnumerator()
{
return _tokenCount.Select(token => new TokenCount(token.Key, token.Value)).GetEnumerator();
}
///
/// Returns an enumerator that iterates through a collection.
///
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}