mirror of
https://github.com/furyfire/trueskill.git
synced 2025-04-19 12:24:28 +00:00
Deleted C# code (it was just there to note the fork)
This commit is contained in:
@ -1,33 +0,0 @@
|
||||
using Moserware.Numerics;
|
||||
using Moserware.Skills.FactorGraphs;
|
||||
|
||||
namespace Moserware.Skills.TrueSkill.Factors
|
||||
{
|
||||
public abstract class GaussianFactor : Factor<GaussianDistribution>
|
||||
{
|
||||
protected GaussianFactor(string name)
|
||||
: base(name)
|
||||
{
|
||||
}
|
||||
|
||||
/// Sends the factor-graph message with and returns the log-normalization constant
|
||||
protected override double SendMessage(Message<GaussianDistribution> message,
|
||||
Variable<GaussianDistribution> variable)
|
||||
{
|
||||
GaussianDistribution marginal = variable.Value;
|
||||
GaussianDistribution messageValue = message.Value;
|
||||
double logZ = GaussianDistribution.LogProductNormalization(marginal, messageValue);
|
||||
variable.Value = marginal*messageValue;
|
||||
return logZ;
|
||||
}
|
||||
|
||||
public override Message<GaussianDistribution> CreateVariableToMessageBinding(
|
||||
Variable<GaussianDistribution> variable)
|
||||
{
|
||||
return CreateVariableToMessageBinding(variable,
|
||||
new Message<GaussianDistribution>(
|
||||
GaussianDistribution.FromPrecisionMean(0, 0),
|
||||
"message from {0} to {1}", this, variable));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using Moserware.Numerics;
|
||||
using Moserware.Skills.FactorGraphs;
|
||||
|
||||
namespace Moserware.Skills.TrueSkill.Factors
|
||||
{
|
||||
/// <summary>
|
||||
/// Factor representing a team difference that has exceeded the draw margin.
|
||||
/// </summary>
|
||||
/// <remarks>See the accompanying math paper for more details.</remarks>
|
||||
public class GaussianGreaterThanFactor : GaussianFactor
|
||||
{
|
||||
private readonly double _Epsilon;
|
||||
|
||||
public GaussianGreaterThanFactor(double epsilon, Variable<GaussianDistribution> variable)
|
||||
: base(String.Format("{0} > {1:0.000}", variable, epsilon))
|
||||
{
|
||||
_Epsilon = epsilon;
|
||||
CreateVariableToMessageBinding(variable);
|
||||
}
|
||||
|
||||
public override double LogNormalization
|
||||
{
|
||||
get
|
||||
{
|
||||
GaussianDistribution marginal = Variables[0].Value;
|
||||
GaussianDistribution message = Messages[0].Value;
|
||||
GaussianDistribution messageFromVariable = marginal/message;
|
||||
return -GaussianDistribution.LogProductNormalization(messageFromVariable, message)
|
||||
+
|
||||
Math.Log(
|
||||
GaussianDistribution.CumulativeTo((messageFromVariable.Mean - _Epsilon)/
|
||||
messageFromVariable.StandardDeviation));
|
||||
}
|
||||
}
|
||||
|
||||
protected override double UpdateMessage(Message<GaussianDistribution> message,
|
||||
Variable<GaussianDistribution> variable)
|
||||
{
|
||||
GaussianDistribution oldMarginal = variable.Value.Clone();
|
||||
GaussianDistribution oldMessage = message.Value.Clone();
|
||||
GaussianDistribution messageFromVar = oldMarginal/oldMessage;
|
||||
|
||||
double c = messageFromVar.Precision;
|
||||
double d = messageFromVar.PrecisionMean;
|
||||
|
||||
double sqrtC = Math.Sqrt(c);
|
||||
|
||||
double dOnSqrtC = d/sqrtC;
|
||||
|
||||
double epsilsonTimesSqrtC = _Epsilon*sqrtC;
|
||||
d = messageFromVar.PrecisionMean;
|
||||
|
||||
double denom = 1.0 - TruncatedGaussianCorrectionFunctions.WExceedsMargin(dOnSqrtC, epsilsonTimesSqrtC);
|
||||
|
||||
double newPrecision = c/denom;
|
||||
double newPrecisionMean = (d +
|
||||
sqrtC*
|
||||
TruncatedGaussianCorrectionFunctions.VExceedsMargin(dOnSqrtC, epsilsonTimesSqrtC))/
|
||||
denom;
|
||||
|
||||
GaussianDistribution newMarginal = GaussianDistribution.FromPrecisionMean(newPrecisionMean, newPrecision);
|
||||
|
||||
GaussianDistribution newMessage = oldMessage*newMarginal/oldMarginal;
|
||||
|
||||
/// Update the message and marginal
|
||||
message.Value = newMessage;
|
||||
|
||||
variable.Value = newMarginal;
|
||||
|
||||
/// Return the difference in the new marginal
|
||||
return newMarginal - oldMarginal;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using Moserware.Numerics;
|
||||
using Moserware.Skills.FactorGraphs;
|
||||
|
||||
namespace Moserware.Skills.TrueSkill.Factors
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects two variables and adds uncertainty.
|
||||
/// </summary>
|
||||
/// <remarks>See the accompanying math paper for more details.</remarks>
|
||||
public class GaussianLikelihoodFactor : GaussianFactor
|
||||
{
|
||||
private readonly double _Precision;
|
||||
|
||||
public GaussianLikelihoodFactor(double betaSquared, Variable<GaussianDistribution> variable1,
|
||||
Variable<GaussianDistribution> variable2)
|
||||
: base(String.Format("Likelihood of {0} going to {1}", variable2, variable1))
|
||||
{
|
||||
_Precision = 1.0/betaSquared;
|
||||
CreateVariableToMessageBinding(variable1);
|
||||
CreateVariableToMessageBinding(variable2);
|
||||
}
|
||||
|
||||
public override double LogNormalization
|
||||
{
|
||||
get { return GaussianDistribution.LogRatioNormalization(Variables[0].Value, Messages[0].Value); }
|
||||
}
|
||||
|
||||
private double UpdateHelper(Message<GaussianDistribution> message1, Message<GaussianDistribution> message2,
|
||||
Variable<GaussianDistribution> variable1, Variable<GaussianDistribution> variable2)
|
||||
{
|
||||
GaussianDistribution message1Value = message1.Value.Clone();
|
||||
GaussianDistribution message2Value = message2.Value.Clone();
|
||||
|
||||
GaussianDistribution marginal1 = variable1.Value.Clone();
|
||||
GaussianDistribution marginal2 = variable2.Value.Clone();
|
||||
|
||||
double a = _Precision/(_Precision + marginal2.Precision - message2Value.Precision);
|
||||
|
||||
GaussianDistribution newMessage = GaussianDistribution.FromPrecisionMean(
|
||||
a*(marginal2.PrecisionMean - message2Value.PrecisionMean),
|
||||
a*(marginal2.Precision - message2Value.Precision));
|
||||
|
||||
GaussianDistribution oldMarginalWithoutMessage = marginal1/message1Value;
|
||||
|
||||
GaussianDistribution newMarginal = oldMarginalWithoutMessage*newMessage;
|
||||
|
||||
/// Update the message and marginal
|
||||
|
||||
message1.Value = newMessage;
|
||||
variable1.Value = newMarginal;
|
||||
|
||||
/// Return the difference in the new marginal
|
||||
return newMarginal - marginal1;
|
||||
}
|
||||
|
||||
public override double UpdateMessage(int messageIndex)
|
||||
{
|
||||
switch (messageIndex)
|
||||
{
|
||||
case 0:
|
||||
return UpdateHelper(Messages[0], Messages[1],
|
||||
Variables[0], Variables[1]);
|
||||
case 1:
|
||||
return UpdateHelper(Messages[1], Messages[0],
|
||||
Variables[1], Variables[0]);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using Moserware.Numerics;
|
||||
using Moserware.Skills.FactorGraphs;
|
||||
|
||||
namespace Moserware.Skills.TrueSkill.Factors
|
||||
{
|
||||
/// <summary>
|
||||
/// Supplies the factor graph with prior information.
|
||||
/// </summary>
|
||||
/// <remarks>See the accompanying math paper for more details.</remarks>
|
||||
public class GaussianPriorFactor : GaussianFactor
|
||||
{
|
||||
private readonly GaussianDistribution _NewMessage;
|
||||
|
||||
public GaussianPriorFactor(double mean, double variance, Variable<GaussianDistribution> variable)
|
||||
: base(String.Format("Prior value going to {0}", variable))
|
||||
{
|
||||
_NewMessage = new GaussianDistribution(mean, Math.Sqrt(variance));
|
||||
CreateVariableToMessageBinding(variable,
|
||||
new Message<GaussianDistribution>(
|
||||
GaussianDistribution.FromPrecisionMean(0, 0), "message from {0} to {1}",
|
||||
this, variable));
|
||||
}
|
||||
|
||||
protected override double UpdateMessage(Message<GaussianDistribution> message,
|
||||
Variable<GaussianDistribution> variable)
|
||||
{
|
||||
GaussianDistribution oldMarginal = variable.Value.Clone();
|
||||
Message<GaussianDistribution> oldMessage = message;
|
||||
GaussianDistribution newMarginal =
|
||||
GaussianDistribution.FromPrecisionMean(
|
||||
oldMarginal.PrecisionMean + _NewMessage.PrecisionMean - oldMessage.Value.PrecisionMean,
|
||||
oldMarginal.Precision + _NewMessage.Precision - oldMessage.Value.Precision);
|
||||
variable.Value = newMarginal;
|
||||
message.Value = _NewMessage;
|
||||
return oldMarginal - newMarginal;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,251 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Moserware.Numerics;
|
||||
using Moserware.Skills.FactorGraphs;
|
||||
|
||||
namespace Moserware.Skills.TrueSkill.Factors
|
||||
{
|
||||
/// <summary>
|
||||
/// Factor that sums together multiple Gaussians.
|
||||
/// </summary>
|
||||
/// <remarks>See the accompanying math paper for more details.</remarks>
|
||||
public class GaussianWeightedSumFactor : GaussianFactor
|
||||
{
|
||||
private readonly List<int[]> _VariableIndexOrdersForWeights = new List<int[]>();
|
||||
|
||||
// This following is used for convenience, for example, the first entry is [0, 1, 2]
|
||||
// corresponding to v[0] = a1*v[1] + a2*v[2]
|
||||
private readonly double[][] _Weights;
|
||||
private readonly double[][] _WeightsSquared;
|
||||
|
||||
public GaussianWeightedSumFactor(Variable<GaussianDistribution> sumVariable,
|
||||
Variable<GaussianDistribution>[] variablesToSum)
|
||||
: this(sumVariable,
|
||||
variablesToSum,
|
||||
variablesToSum.Select(v => 1.0).ToArray()) // By default, set the weight to 1.0
|
||||
{
|
||||
}
|
||||
|
||||
public GaussianWeightedSumFactor(Variable<GaussianDistribution> sumVariable,
|
||||
Variable<GaussianDistribution>[] variablesToSum, double[] variableWeights)
|
||||
: base(CreateName(sumVariable, variablesToSum, variableWeights))
|
||||
{
|
||||
_Weights = new double[variableWeights.Length + 1][];
|
||||
_WeightsSquared = new double[_Weights.Length][];
|
||||
|
||||
// The first weights are a straightforward copy
|
||||
// v_0 = a_1*v_1 + a_2*v_2 + ... + a_n * v_n
|
||||
_Weights[0] = new double[variableWeights.Length];
|
||||
Array.Copy(variableWeights, _Weights[0], variableWeights.Length);
|
||||
_WeightsSquared[0] = _Weights[0].Select(w => w*w).ToArray();
|
||||
|
||||
// 0..n-1
|
||||
_VariableIndexOrdersForWeights.Add(Enumerable.Range(0, 1 + variablesToSum.Length).ToArray());
|
||||
|
||||
|
||||
// The rest move the variables around and divide out the constant.
|
||||
// For example:
|
||||
// v_1 = (-a_2 / a_1) * v_2 + (-a3/a1) * v_3 + ... + (1.0 / a_1) * v_0
|
||||
// By convention, we'll put the v_0 term at the end
|
||||
|
||||
for (int weightsIndex = 1; weightsIndex < _Weights.Length; weightsIndex++)
|
||||
{
|
||||
var currentWeights = new double[variableWeights.Length];
|
||||
_Weights[weightsIndex] = currentWeights;
|
||||
|
||||
var variableIndices = new int[variableWeights.Length + 1];
|
||||
variableIndices[0] = weightsIndex;
|
||||
|
||||
var currentWeightsSquared = new double[variableWeights.Length];
|
||||
_WeightsSquared[weightsIndex] = currentWeightsSquared;
|
||||
|
||||
// keep a single variable to keep track of where we are in the array.
|
||||
// This is helpful since we skip over one of the spots
|
||||
int currentDestinationWeightIndex = 0;
|
||||
|
||||
for (int currentWeightSourceIndex = 0;
|
||||
currentWeightSourceIndex < variableWeights.Length;
|
||||
currentWeightSourceIndex++)
|
||||
{
|
||||
if (currentWeightSourceIndex == (weightsIndex - 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double currentWeight = (-variableWeights[currentWeightSourceIndex]/variableWeights[weightsIndex - 1]);
|
||||
|
||||
if (variableWeights[weightsIndex - 1] == 0)
|
||||
{
|
||||
// HACK: Getting around division by zero
|
||||
currentWeight = 0;
|
||||
}
|
||||
|
||||
currentWeights[currentDestinationWeightIndex] = currentWeight;
|
||||
currentWeightsSquared[currentDestinationWeightIndex] = currentWeight*currentWeight;
|
||||
|
||||
variableIndices[currentDestinationWeightIndex + 1] = currentWeightSourceIndex + 1;
|
||||
currentDestinationWeightIndex++;
|
||||
}
|
||||
|
||||
// And the final one
|
||||
double finalWeight = 1.0/variableWeights[weightsIndex - 1];
|
||||
|
||||
if (variableWeights[weightsIndex - 1] == 0)
|
||||
{
|
||||
// HACK: Getting around division by zero
|
||||
finalWeight = 0;
|
||||
}
|
||||
currentWeights[currentDestinationWeightIndex] = finalWeight;
|
||||
currentWeightsSquared[currentDestinationWeightIndex] = finalWeight*finalWeight;
|
||||
variableIndices[variableIndices.Length - 1] = 0;
|
||||
_VariableIndexOrdersForWeights.Add(variableIndices);
|
||||
}
|
||||
|
||||
CreateVariableToMessageBinding(sumVariable);
|
||||
|
||||
foreach (var currentVariable in variablesToSum)
|
||||
{
|
||||
CreateVariableToMessageBinding(currentVariable);
|
||||
}
|
||||
}
|
||||
|
||||
public override double LogNormalization
|
||||
{
|
||||
get
|
||||
{
|
||||
ReadOnlyCollection<Variable<GaussianDistribution>> vars = Variables;
|
||||
ReadOnlyCollection<Message<GaussianDistribution>> messages = Messages;
|
||||
|
||||
double result = 0.0;
|
||||
|
||||
// We start at 1 since offset 0 has the sum
|
||||
for (int i = 1; i < vars.Count; i++)
|
||||
{
|
||||
result += GaussianDistribution.LogRatioNormalization(vars[i].Value, messages[i].Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private double UpdateHelper(double[] weights, double[] weightsSquared,
|
||||
IList<Message<GaussianDistribution>> messages,
|
||||
IList<Variable<GaussianDistribution>> variables)
|
||||
{
|
||||
// Potentially look at http://mathworld.wolfram.com/NormalSumDistribution.html for clues as
|
||||
// to what it's doing
|
||||
|
||||
GaussianDistribution message0 = messages[0].Value.Clone();
|
||||
GaussianDistribution marginal0 = variables[0].Value.Clone();
|
||||
|
||||
// The math works out so that 1/newPrecision = sum of a_i^2 /marginalsWithoutMessages[i]
|
||||
double inverseOfNewPrecisionSum = 0.0;
|
||||
double anotherInverseOfNewPrecisionSum = 0.0;
|
||||
double weightedMeanSum = 0.0;
|
||||
double anotherWeightedMeanSum = 0.0;
|
||||
|
||||
for (int i = 0; i < weightsSquared.Length; i++)
|
||||
{
|
||||
// These flow directly from the paper
|
||||
|
||||
inverseOfNewPrecisionSum += weightsSquared[i]/
|
||||
(variables[i + 1].Value.Precision - messages[i + 1].Value.Precision);
|
||||
|
||||
GaussianDistribution diff = (variables[i + 1].Value/messages[i + 1].Value);
|
||||
anotherInverseOfNewPrecisionSum += weightsSquared[i]/diff.Precision;
|
||||
|
||||
weightedMeanSum += weights[i]
|
||||
*
|
||||
(variables[i + 1].Value.PrecisionMean - messages[i + 1].Value.PrecisionMean)
|
||||
/
|
||||
(variables[i + 1].Value.Precision - messages[i + 1].Value.Precision);
|
||||
|
||||
anotherWeightedMeanSum += weights[i]*diff.PrecisionMean/diff.Precision;
|
||||
}
|
||||
|
||||
double newPrecision = 1.0/inverseOfNewPrecisionSum;
|
||||
double anotherNewPrecision = 1.0/anotherInverseOfNewPrecisionSum;
|
||||
|
||||
double newPrecisionMean = newPrecision*weightedMeanSum;
|
||||
double anotherNewPrecisionMean = anotherNewPrecision*anotherWeightedMeanSum;
|
||||
|
||||
GaussianDistribution newMessage = GaussianDistribution.FromPrecisionMean(newPrecisionMean, newPrecision);
|
||||
GaussianDistribution oldMarginalWithoutMessage = marginal0/message0;
|
||||
|
||||
GaussianDistribution newMarginal = oldMarginalWithoutMessage*newMessage;
|
||||
|
||||
/// Update the message and marginal
|
||||
|
||||
messages[0].Value = newMessage;
|
||||
variables[0].Value = newMarginal;
|
||||
|
||||
/// Return the difference in the new marginal
|
||||
return newMarginal - marginal0;
|
||||
}
|
||||
|
||||
public override double UpdateMessage(int messageIndex)
|
||||
{
|
||||
ReadOnlyCollection<Message<GaussianDistribution>> allMessages = Messages;
|
||||
ReadOnlyCollection<Variable<GaussianDistribution>> allVariables = Variables;
|
||||
|
||||
Guard.ArgumentIsValidIndex(messageIndex, allMessages.Count, "messageIndex");
|
||||
|
||||
var updatedMessages = new List<Message<GaussianDistribution>>();
|
||||
var updatedVariables = new List<Variable<GaussianDistribution>>();
|
||||
|
||||
int[] indicesToUse = _VariableIndexOrdersForWeights[messageIndex];
|
||||
|
||||
// The tricky part here is that we have to put the messages and variables in the same
|
||||
// order as the weights. Thankfully, the weights and messages share the same index numbers,
|
||||
// so we just need to make sure they're consistent
|
||||
for (int i = 0; i < allMessages.Count; i++)
|
||||
{
|
||||
updatedMessages.Add(allMessages[indicesToUse[i]]);
|
||||
updatedVariables.Add(allVariables[indicesToUse[i]]);
|
||||
}
|
||||
|
||||
return UpdateHelper(_Weights[messageIndex], _WeightsSquared[messageIndex], updatedMessages, updatedVariables);
|
||||
}
|
||||
|
||||
private static string CreateName(Variable<GaussianDistribution> sumVariable,
|
||||
IList<Variable<GaussianDistribution>> variablesToSum, double[] weights)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(sumVariable.ToString());
|
||||
sb.Append(" = ");
|
||||
for (int i = 0; i < variablesToSum.Count; i++)
|
||||
{
|
||||
bool isFirst = (i == 0);
|
||||
|
||||
if (isFirst && (weights[i] < 0))
|
||||
{
|
||||
sb.Append("-");
|
||||
}
|
||||
|
||||
sb.Append(Math.Abs(weights[i]).ToString("0.00"));
|
||||
sb.Append("*[");
|
||||
sb.Append(variablesToSum[i]);
|
||||
sb.Append("]");
|
||||
|
||||
bool isLast = (i == variablesToSum.Count - 1);
|
||||
|
||||
if (!isLast)
|
||||
{
|
||||
if (weights[i + 1] >= 0)
|
||||
{
|
||||
sb.Append(" + ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(" - ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using Moserware.Numerics;
|
||||
using Moserware.Skills.FactorGraphs;
|
||||
|
||||
namespace Moserware.Skills.TrueSkill.Factors
|
||||
{
|
||||
/// <summary>
|
||||
/// Factor representing a team difference that has not exceeded the draw margin.
|
||||
/// </summary>
|
||||
/// <remarks>See the accompanying math paper for more details.</remarks>
|
||||
public class GaussianWithinFactor : GaussianFactor
|
||||
{
|
||||
private readonly double _Epsilon;
|
||||
|
||||
public GaussianWithinFactor(double epsilon, Variable<GaussianDistribution> variable)
|
||||
: base(String.Format("{0} <= {1:0.000}", variable, epsilon))
|
||||
{
|
||||
_Epsilon = epsilon;
|
||||
CreateVariableToMessageBinding(variable);
|
||||
}
|
||||
|
||||
public override double LogNormalization
|
||||
{
|
||||
get
|
||||
{
|
||||
GaussianDistribution marginal = Variables[0].Value;
|
||||
GaussianDistribution message = Messages[0].Value;
|
||||
GaussianDistribution messageFromVariable = marginal/message;
|
||||
double mean = messageFromVariable.Mean;
|
||||
double std = messageFromVariable.StandardDeviation;
|
||||
double z = GaussianDistribution.CumulativeTo((_Epsilon - mean)/std)
|
||||
-
|
||||
GaussianDistribution.CumulativeTo((-_Epsilon - mean)/std);
|
||||
|
||||
return -GaussianDistribution.LogProductNormalization(messageFromVariable, message) + Math.Log(z);
|
||||
}
|
||||
}
|
||||
|
||||
protected override double UpdateMessage(Message<GaussianDistribution> message,
|
||||
Variable<GaussianDistribution> variable)
|
||||
{
|
||||
GaussianDistribution oldMarginal = variable.Value.Clone();
|
||||
GaussianDistribution oldMessage = message.Value.Clone();
|
||||
GaussianDistribution messageFromVariable = oldMarginal/oldMessage;
|
||||
|
||||
double c = messageFromVariable.Precision;
|
||||
double d = messageFromVariable.PrecisionMean;
|
||||
|
||||
double sqrtC = Math.Sqrt(c);
|
||||
double dOnSqrtC = d/sqrtC;
|
||||
|
||||
double epsilonTimesSqrtC = _Epsilon*sqrtC;
|
||||
d = messageFromVariable.PrecisionMean;
|
||||
|
||||
double denominator = 1.0 - TruncatedGaussianCorrectionFunctions.WWithinMargin(dOnSqrtC, epsilonTimesSqrtC);
|
||||
double newPrecision = c/denominator;
|
||||
double newPrecisionMean = (d +
|
||||
sqrtC*
|
||||
TruncatedGaussianCorrectionFunctions.VWithinMargin(dOnSqrtC, epsilonTimesSqrtC))/
|
||||
denominator;
|
||||
|
||||
GaussianDistribution newMarginal = GaussianDistribution.FromPrecisionMean(newPrecisionMean, newPrecision);
|
||||
GaussianDistribution newMessage = oldMessage*newMarginal/oldMarginal;
|
||||
|
||||
/// Update the message and marginal
|
||||
message.Value = newMessage;
|
||||
variable.Value = newMarginal;
|
||||
|
||||
/// Return the difference in the new marginal
|
||||
return newMarginal - oldMarginal;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user