# TrueSkill: draw option

### Question

• The simplest TrueSkill model involving two players is fairly easy to implement. I am following the MBML book online (http://mbmlbook.com/TrueSkill_Extensions_to_the_core_model.html) and tried to implement also the draw outcome (Figure 3.33). The draw introduces a margin that separates win regions for two players according to players' performances in a particular game.

My first idea was to write the three cases like this:

```using (Variable.ForEach(n))
{
using (Variable.If(Abs(Jperfs[n] - Fperfs[n]) < 2*drawMargin))
{
outcomes[n] = 0;
}
using (Variable.If(Jperfs[n] > Fperfs[n] + drawMargin))
{
outcomes[n] = 1;   // Jill wins
}
using (Variable.If(Fperfs[n] > Jperfs[n] + drawMargin))
{
outcomes[n] = 2;   // Fred wins
}
}```

Only to realize that Infer.NET does not support Abs (absolute value of difference). Or does it?

My next model implementation was bit more involved in testing the cases:

```using (Variable.ForEach(n))
{
using (Variable.If(Jperfs[n] > Fperfs[n]))
{
var JillWins = Jperfs[n] - Fperfs[n] - drawMargin;
using (Variable.If(JillWins > 0))
outcomes[n] = 1;
using (Variable.IfNot(JillWins > 0))
outcomes[n] = 0;
}
using (Variable.If(Fperfs[n] > Jperfs[n]))
{
var FredWins = Fperfs[n] - Jperfs[n] - drawMargin;
using (Variable.If(FredWins > 0))
outcomes[n] = 2;
using (Variable.IfNot(FredWins > 0))
outcomes[n] = 0;
}
}```

Unfortunately this rewrote model does not compile. The compiler complains about not being able to do difference operation. I tried both Expectation and VMP inference engines; in both cases the model failed to compile.

The data and model variable are implemented as:

```           int[] outcomes_data = new int[] {
1,  // Jill wins
1,  // Jill wins
1,  // Jill wins
1,  // Jill wins
1,  // Jill wins
2,  // Fred wins
2,  // Fred wins
0,  // Draw
0}; // Draw

int numGames = outcomes_data.Length;
Range n = new Range(numGames);

// prior beliefs on skill
var Jskill = Variable.GaussianFromMeanAndVariance(120, 5 * 5);
var Fskill = Variable.GaussianFromMeanAndVariance(100, 40 * 40);

// performance for 1+ games for Jill
var Jperfs = Variable.Array<double>(n);
Jperfs[n] = Variable.GaussianFromMeanAndVariance(Jskill, 5 * 5).ForEach(n);

// performance for 1+ games for Fred
var Fperfs = Variable.Array<double>(n);
Fperfs[n] = Variable.GaussianFromMeanAndVariance(Fskill, 5 * 5).ForEach(n);

var drawMargin = Variable.GammaFromMeanAndVariance(1.0, 10.0);

// game outcomes (0 - Draw; 1 - Jill wins; 2 - Fred wins)
var outcomes = Variable.Array<int>(n);```

As in the two person variant, I tried to infer the skill marginal for the two players:

```// attaching data
outcomes.ObservedValue = outcomes_data;

InferenceEngine engine = new InferenceEngine(new VariationalMessagePassing());

Gaussian JskillMarginal = engine.Infer<Gaussian>(Jskill);
Gaussian FskillMarginal = engine.Infer<Gaussian>(Fskill);

Console.WriteLine("Jskill marginal = {0}", JskillMarginal);
Console.WriteLine("Fskill marginal = {0}", FskillMarginal);```

What can be the issue with my model? Did I overlook something?

Thanks

• Edited by Tuesday, November 14, 2017 12:01 AM
Monday, November 13, 2017 11:49 PM

• I managed to get this working, at least past compilation error.

My first mistake was to declare and statistically define "drawMargin" as Gamma, not Gaussian. This is what made the compilation fail. But hey, why can't I have "drawMargin" to be Gamma distributed? I do want it to be positive.

After the first mistake was fixed, there was still an exception about "index" out of bounds (???). I ended up rewriting the model slightly:

```using (Variable.ForEach(n))
{
using (Variable.If(Jperfs[n] < Fperfs[n] + drawMargin | Jperfs[n] < Fperfs[n] + drawMargin))
outcomes[n] = 0;
using (Variable.If(Jperfs[n] > Fperfs[n] + drawMargin))
outcomes[n] = 1;
using (Variable.If(Fperfs[n] > Jperfs[n] + drawMargin))
outcomes[n] = 2;
}```

The first case looks bit ugly but it is essentially negation of the 2nd and 3rd case (negation of "A and B" is "not A or not B"). It would've been nicer to write something like Abs(Jperfs[n] - Fperfs[n]) < 2*drawMargin.

One last remark: As I mentioned above, I want "drawMargin" to be positive. So I added the statement:

`Variable.ConstrainPositive(drawMargin);`

If I remove it, the inference still works and the marginals look the same (when this statement is removed). I am unsure why it works...

Leaving it here for a future traveler.

• Marked as answer by Tuesday, November 14, 2017 5:47 PM
• Edited by Tuesday, November 14, 2017 5:55 PM
Tuesday, November 14, 2017 5:47 PM

### All replies

• Here is the error message:

```MicrosoftResearch.Infer.CompilationFailedException occurred
HResult=0x80131500
Message=MessageTransform failed with 12 error(s) and 0 warning(s):
Error 0: This model is not supported with VariationalMessagePassing due to Factor.IsPositive(bool isPositive, double x). Try using a different algorithm or expressing the model differently in
Factor.IsPositive(vdouble17)
Details: [0] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[1] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[2] System.ArgumentException: Bernoulli is not of type bool for argument 1 of method IsPositiveOp.XAverageLogarithm(bool isPositive = Bernoulli)
Error 1: This model is not supported with VariationalMessagePassing due to Factor.Difference(double difference, double a, double b). Try using a different algorithm or expressing the model differently in
Factor.Difference(vdouble18, vdouble16__0_)
Details: [0] System.ArgumentException: Gamma is not of type Gaussian for argument 2 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(Gaussian a = Gaussian, Gaussian b = Gamma)
[1] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(double a = Gaussian, Gaussian b = Gamma)
[2] System.ArgumentException: Gamma is not of type double for argument 2 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(Gaussian a = Gaussian, double b = Gamma)
[3] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(double a = Gaussian, double b = Gamma)
Error 2: This model is not supported with VariationalMessagePassing due to Factor.Difference(double difference, double a, double b). Try using a different algorithm or expressing the model differently in
Factor.Difference(vdouble18, vdouble16__0_)
Details: [0] System.ArgumentException: Gamma is not of type Gaussian for argument 2 of method DoubleMinusVmpOp.AAverageLogarithm(Gaussian Difference = Gaussian, Gaussian b = Gamma)
[1] System.ArgumentException: Gamma is not of type double for argument 2 of method DoubleMinusVmpOp.AAverageLogarithm(Gaussian Difference = Gaussian, double b = Gamma)
[2] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.AAverageLogarithm(double Difference = Gaussian, Gaussian b = Gamma)
[3] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.AAverageLogarithm(double Difference = Gaussian, double b = Gamma)
Error 3: This model is not supported with VariationalMessagePassing due to Factor.Difference(double difference, double a, double b). Try using a different algorithm or expressing the model differently in
Factor.Difference(vdouble18, vdouble16__0_)
Details: [0] System.ArgumentException: Gamma is not assignable from Gaussian for result of method DoubleMinusVmpOp.BAverageLogarithm
[1] System.ArgumentException: Gaussian is not of type double for argument 2 of method DoubleMinusVmpOp.BAverageLogarithm(Gaussian Difference = Gaussian, double a = Gaussian)
[2] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.BAverageLogarithm(double Difference = Gaussian, Gaussian a = Gaussian)
[3] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.BAverageLogarithm(double Difference = Gaussian, double a = Gaussian)
Error 4: This model is not supported with VariationalMessagePassing due to Factor.IsPositive(bool isPositive, double x). Try using a different algorithm or expressing the model differently in
Factor.IsPositive(vdouble19_uses[0])
Details: [0] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[1] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[2] System.ArgumentException: Bernoulli is not of type bool for argument 1 of method IsPositiveOp.XAverageLogarithm(bool isPositive = Bernoulli)
Error 5: This model is not supported with VariationalMessagePassing due to Factor.IsPositive(bool isPositive, double x). Try using a different algorithm or expressing the model differently in
Factor.IsPositive(vdouble19_uses[1])
Details: [0] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[1] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[2] System.ArgumentException: Bernoulli is not of type bool for argument 1 of method IsPositiveOp.XAverageLogarithm(bool isPositive = Bernoulli)
Error 6: This model is not supported with VariationalMessagePassing due to Factor.IsPositive(bool isPositive, double x). Try using a different algorithm or expressing the model differently in
Factor.IsPositive(vdouble22)
Details: [0] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[1] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[2] System.ArgumentException: Bernoulli is not of type bool for argument 1 of method IsPositiveOp.XAverageLogarithm(bool isPositive = Bernoulli)
Error 7: This model is not supported with VariationalMessagePassing due to Factor.Difference(double difference, double a, double b). Try using a different algorithm or expressing the model differently in
Factor.Difference(vdouble23, vdouble16_2_0_)
Details: [0] System.ArgumentException: Gamma is not of type Gaussian for argument 2 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(Gaussian a = Gaussian, Gaussian b = Gamma)
[1] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(double a = Gaussian, Gaussian b = Gamma)
[2] System.ArgumentException: Gamma is not of type double for argument 2 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(Gaussian a = Gaussian, double b = Gamma)
[3] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.DifferenceAverageLogarithm(double a = Gaussian, double b = Gamma)
Error 8: This model is not supported with VariationalMessagePassing due to Factor.Difference(double difference, double a, double b). Try using a different algorithm or expressing the model differently in
Factor.Difference(vdouble23, vdouble16_2_0_)
Details: [0] System.ArgumentException: Gamma is not of type Gaussian for argument 2 of method DoubleMinusVmpOp.AAverageLogarithm(Gaussian Difference = Gaussian, Gaussian b = Gamma)
[1] System.ArgumentException: Gamma is not of type double for argument 2 of method DoubleMinusVmpOp.AAverageLogarithm(Gaussian Difference = Gaussian, double b = Gamma)
[2] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.AAverageLogarithm(double Difference = Gaussian, Gaussian b = Gamma)
[3] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.AAverageLogarithm(double Difference = Gaussian, double b = Gamma)
Error 9: This model is not supported with VariationalMessagePassing due to Factor.Difference(double difference, double a, double b). Try using a different algorithm or expressing the model differently in
Factor.Difference(vdouble23, vdouble16_2_0_)
Details: [0] System.ArgumentException: Gamma is not assignable from Gaussian for result of method DoubleMinusVmpOp.BAverageLogarithm
[1] System.ArgumentException: Gaussian is not of type double for argument 2 of method DoubleMinusVmpOp.BAverageLogarithm(Gaussian Difference = Gaussian, double a = Gaussian)
[2] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.BAverageLogarithm(double Difference = Gaussian, Gaussian a = Gaussian)
[3] System.ArgumentException: Gaussian is not of type double for argument 1 of method DoubleMinusVmpOp.BAverageLogarithm(double Difference = Gaussian, double a = Gaussian)
Error 10: This model is not supported with VariationalMessagePassing due to Factor.IsPositive(bool isPositive, double x). Try using a different algorithm or expressing the model differently in
Factor.IsPositive(vdouble24_uses[0])
Details: [0] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[1] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[2] System.ArgumentException: Bernoulli is not of type bool for argument 1 of method IsPositiveOp.XAverageLogarithm(bool isPositive = Bernoulli)
Error 11: This model is not supported with VariationalMessagePassing due to Factor.IsPositive(bool isPositive, double x). Try using a different algorithm or expressing the model differently in
Factor.IsPositive(vdouble24_uses[1])
Details: [0] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[1] System.ArgumentException: 'x' must not be a derived variable in method IsPositiveOp.XAverageLogarithm
[2] System.ArgumentException: Bernoulli is not of type bool for argument 1 of method IsPositiveOp.XAverageLogarithm(bool isPositive = Bernoulli)

Source=Infer.Compiler
StackTrace:
at MicrosoftResearch.Infer.ModelCompiler.GetTransformedDeclaration(ITypeDeclaration itd, MethodBase method, AttributeRegistry`2 inputAttributes)
at MicrosoftResearch.Infer.ModelCompiler.CompileWithoutParams(ITypeDeclaration itd, MethodBase method, AttributeRegistry`2 inputAttributes)
at MicrosoftResearch.Infer.InferenceEngine.Compile()
at MicrosoftResearch.Infer.InferenceEngine.BuildAndCompile(Boolean inferOnlySpecifiedVars, IEnumerable`1 vars)
at MicrosoftResearch.Infer.InferenceEngine.GetCompiledInferenceAlgorithm(Boolean inferOnlySpecifiedVars, IVariable var)
at MicrosoftResearch.Infer.InferenceEngine.Infer[TReturn](IVariable var)
at TrueSkill.MainClass.Main(String[] args) in C:\Users\vladislavsd\Documents\Visual Studio 2017\Projects\TrueSkill\TrueSkill\Program.cs:line 70
```

Tuesday, November 14, 2017 12:36 AM
• I managed to get this working, at least past compilation error.

My first mistake was to declare and statistically define "drawMargin" as Gamma, not Gaussian. This is what made the compilation fail. But hey, why can't I have "drawMargin" to be Gamma distributed? I do want it to be positive.

After the first mistake was fixed, there was still an exception about "index" out of bounds (???). I ended up rewriting the model slightly:

```using (Variable.ForEach(n))
{
using (Variable.If(Jperfs[n] < Fperfs[n] + drawMargin | Jperfs[n] < Fperfs[n] + drawMargin))
outcomes[n] = 0;
using (Variable.If(Jperfs[n] > Fperfs[n] + drawMargin))
outcomes[n] = 1;
using (Variable.If(Fperfs[n] > Jperfs[n] + drawMargin))
outcomes[n] = 2;
}```

The first case looks bit ugly but it is essentially negation of the 2nd and 3rd case (negation of "A and B" is "not A or not B"). It would've been nicer to write something like Abs(Jperfs[n] - Fperfs[n]) < 2*drawMargin.

One last remark: As I mentioned above, I want "drawMargin" to be positive. So I added the statement:

`Variable.ConstrainPositive(drawMargin);`

If I remove it, the inference still works and the marginals look the same (when this statement is removed). I am unsure why it works...

Leaving it here for a future traveler.

• Marked as answer by Tuesday, November 14, 2017 5:47 PM
• Edited by Tuesday, November 14, 2017 5:55 PM
Tuesday, November 14, 2017 5:47 PM
• drawMargin cannot be Gamma since it leads to an expensive operation of adding Gamma with Gaussian.

You could also use Variable.IsBetween.  I don't think you should be doubling drawMargin just because of Abs.

Tuesday, November 14, 2017 6:27 PM