locked
Implementing an efficient TrueSkill Through Time model RRS feed

  • Question

  • Hello Infer.Net community,

    I'm currently trying to recreate the model shown in the "Trueskill Through Time" paper (http://papers.nips.cc/paper/3331-trueskill-through-time-revisiting-the-history-of-chess.pdf) of having one big interconnected model between matches to allow forwards & backwards propagation using Infer.Net. I've read this implementation of TrueSkill from the forums (http://social.microsoft.com/Forums/en-US/94bee5c7-056c-4933-81d0-cbc80bd4d888/trueskill-implementation-help-migrated-from-communityresearchmicrosoftcom?forum=infer.net) and have also read the article on how to create efficient large, irregular graphs (http://research.microsoft.com/en-us/um/cambridge/projects/infernet/docs/How%20to%20represent%20large%20irregular%20graphs.aspx) and have been trying to in effect combine the two methods to make the TTT model. The problem I've run in to is I'm not sure how to create the most efficient referencing (and storing) of previous skill variables. In the "efficient large, irregular graphs" example they store Skill as one variable in a VariableArray and then reference it via using the winnerVar[game] as an index; how would this work when one is trying to store different variables for Skill that depend on T (time), as well as creating links between Skill(T) and Skill(T-1) using the Tau factor? I've tried to think of how to do this in an analogous manner to the "Small, fast model" as opposed to the "Big, slow model" in order for the inference to be efficient and have gotten stuck - please could someone point me in the correct direction as to how to approach re-creating this model?

    Thanks very much!

    Friday, May 9, 2014 3:32 PM

Answers

  • Here is a complete example of how to implement the "Trueskill Through Time" paper efficiently.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecision = Variable.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantage = Variable.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = false;  // make this true to infer additional parameters
                if (!inferParameters)
                {
                    // fix the true parameters
                    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                    performancePrecision.ObservedValue = parameters.performancePrecision;
                    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                }
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skill[year][w], performancePrecision);
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skill[year][b], performancePrecision);
                        Variable<double> white_drawMargin = Variable.Copy(drawMargin[year][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMargin[year][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantage;
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
                year.AddAttribute(new Sequential());   // helps inference converge faster
    
                engine.NumberOfIterations = 10;
                var skillPost = engine.Infer<Gaussian[][]>(skill);
                var drawMarginPost = engine.Infer<Gaussian[][]>(drawMargin);
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", engine.Infer<Gamma>(performancePrecision).GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", engine.Infer<Gaussian>(whiteAdvantage), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision) 
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }
    

    • Marked as answer by MarkG87 Sunday, May 18, 2014 6:21 PM
    Saturday, May 17, 2014 6:56 AM
    Owner

All replies

  • Here is a complete example of how to implement the "Trueskill Through Time" paper efficiently.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecision = Variable.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantage = Variable.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = false;  // make this true to infer additional parameters
                if (!inferParameters)
                {
                    // fix the true parameters
                    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                    performancePrecision.ObservedValue = parameters.performancePrecision;
                    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                }
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skill[year][w], performancePrecision);
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skill[year][b], performancePrecision);
                        Variable<double> white_drawMargin = Variable.Copy(drawMargin[year][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMargin[year][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantage;
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
                year.AddAttribute(new Sequential());   // helps inference converge faster
    
                engine.NumberOfIterations = 10;
                var skillPost = engine.Infer<Gaussian[][]>(skill);
                var drawMarginPost = engine.Infer<Gaussian[][]>(drawMargin);
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", engine.Infer<Gamma>(performancePrecision).GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", engine.Infer<Gaussian>(whiteAdvantage), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision) 
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }
    

    • Marked as answer by MarkG87 Sunday, May 18, 2014 6:21 PM
    Saturday, May 17, 2014 6:56 AM
    Owner
  • Tom,

    Wow, amazing, thanks so much for that! Two questions - 1) Would this model need to be re-written using SharedVariables if the player count/game count grew significantly or will it run fine as-is? 2) I see you're referring to the skill of the player from the previous year - does this model assume sequential play i.e. no players who missed out playing for a number of years e.g. Bobby Fischer?

    Monday, May 19, 2014 12:12 PM
  • 1.  Yes, you would eventually need to use SharedVariables if the player count grew large enough.

    2. The model is the same as the one from the paper. 

    Monday, May 19, 2014 1:54 PM
    Owner
  • When I run the model above the Gaussians for the skill estimates have very high variances (~30000 compared to means of ~1000). This seems odd to me as I thought that the variances would be essentially the uncertainty in the skill estimates, yet the mean estimates are generally much closer to the true values than would be implied by those high variances (I've not seen one not within ~200 of the true value having ran it a few times). What am I not understanding correctly here?
    Friday, May 23, 2014 10:21 AM
  • The standard deviation is sqrt(30000) = 173.
    Friday, May 23, 2014 11:09 AM
    Owner
  • Tom,

    Oh of course, apologies for such a stupid question!

    I was thinking of adding each game to the dataset incrementally and running a separate model between games on the next game in sequence in order to use the predicted skill parameters to calculate the odds of each player winning the next game beforehand. Is the best way to implement this sort of mechanic to have the model run inference a very low number of times between additions to the dataset and just update the priors between each addition to the dataset based off the previous dataset's posteriors? I worry if doing this will mean that there's not enough inference passes over the new, incremental data to fully take account of them?

    Friday, May 23, 2014 4:38 PM
  • In TrueSkill Through Time, you never update priors since it is not incremental.  It is supposed to find the jointly optimal inference given a set of games.
    Tuesday, May 27, 2014 9:56 AM
    Owner
  • Tom,

    A few questions on the inference: 1) I get the warnings about "[X] year is marked Sequential but engine.Compiler.UseSerialSchedules = false", should I add a line of code to enable that? 2) When I try running the code with "engine.Compiler.UseParallelForLoops = true;" it doesn't seem to work - is there a way to modify the code so it supports multi-threading or will it not work with a model this big? 3) How long should convergence take? I've tried running that model using my own dataset (~5000 players, ~45k games, ~10 years) and the parameters are still changing (skills moving and variance estimates decreasing) after 1000 iterations.

    Thanks very much again for all your help and support on this, it's very much appreciated!

    Thursday, May 29, 2014 5:05 PM
  • I have found a solution to my point 3) above - it seems that the choice of priors is very important to achieving convergence. With the default priors in the code above I found that after ~50 iterations there is a large amount of skill 'inflation' between the years - the vast majority of player's skill ratings increased between years regardless and the average skill went up significantly (and the skill variances also tended to go up from the prior instead of decreasing). Further iterations were simply attempting to 'undo' this process, albeit this happened quite slowly (hence why I found it still converging after 1000 iterations). When I lowered the Performance & skill change precision priors rates significantly I found that I reduces this problem significantly, the current priors I am using are:

    var skillPrior = new Gaussian(2000, 800 * 800);
    var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
    var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 1 * 1);

    N.B. I have adapted this code to remove the draw mechanic so there are no other priors.

    I have also found that I cannot decrease the rates much further below these settings without Infer.net throwing an error during convergence, e.g. the following settings throw an error ("Improper distribution during inference (Gaussian(m/v=0.2488, 1/v=0)).  Cannot perform inference on this model."):

    var skillPrior = new Gaussian(2000, 800 * 800);
    var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 1 * 1);
    var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 1 * 1);

    Is it the case that the higher rate settings are too restrictive as priors and that lower rates give the model more flexibility to converge on the appropriate solutions for the dataset? Is there a way to allow the model to run on lower rate (less restrictive?) priors without throwing inference errors? In general, how should one go about choosing good priors to optimise the convergence process?

    Monday, June 2, 2014 9:56 AM
  • In Infer.NET 2.5 Beta 2, you should remove these two lines:

     year.AddAttribute(new Sequential());   // helps inference converge faster
    
     engine.NumberOfIterations = 10;
    

    I'm not seeing any problems running with UseParallelForLoops=true.  What issues are you seeing?

    As for choosing priors, convergence is mostly affected by shape.  So if you want to make the rate smaller, make a shape a bit bigger.

    Monday, June 2, 2014 1:51 PM
    Owner
  • Tom,

    Removing the line:

     year.AddAttribute(new Sequential());  
     

    Has fixed the convergence problems I was having (skill inflation is completely gone and a wider range of priors converges on a correct solution) and has also meant the parallelforloops line now means the thread count has increased from 12 to ~30 when performing inference. With the parallelforloops enabled I get a CPU profile like this:

    https://www.dropbox.com/s/vnxsb9nzjj3sr98/CPU%20profile.jpg

    Is this as expected because of some parts of the code that cannot be run in parallel?

    Tuesday, June 3, 2014 1:10 PM
  • I've realised that converting the code to using SharedVariables will allow better parallelization and to handle larger datasets so I've been trying to re-write the example given above to use the SharedVariables approach. I've ran in to problems trying to construct the 2D SharedVariable arrays needed - below is the code I've got so far where I've tried to copy the example given for a 1D array here (http://research.microsoft.com/en-us/um/cambridge/projects/infernet/docs/Shared%20variable%20arrays.aspx) and extrapolating it to a 2D array (which I can't get to work):

    using GaussianArray = DistributionStructArray<Gaussian, double>;
        using Gaussian2DArray = DistributionStructArray2D<Gaussian, double>;
        class Program
        {
            private static GaussianArray CreateUniformGaussianArray(int length)
            {
                Gaussian[] result = new Gaussian[length];
                for (int i = 0; i < length; i++)
                {
                    result[i] = Gaussian.Uniform();
                }
                return (GaussianArray)Distribution<double>.Array<Gaussian>(result);
            }
            private static Gaussian2DArray CreateUniformGaussian2DArray(int length1, int length2)
            {
                Gaussian[,] result = new Gaussian[length1,length2];
                for (int i = 0; i < length1; i++)
                {
                    for (int y = 0; y < length2; y++)
                    {
                        result[i,y] = Gaussian.Uniform();
                    }
                }
                return (Gaussian2DArray)Distribution<double>.Array<Gaussian>(result);
            }
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = SharedVariable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = SharedVariable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecision = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = SharedVariable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = SharedVariable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantage = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                // My attempts below to create the 2D skill array
                var skilltest1D = SharedVariable<double>.Random(year, CreateUniformGaussianArray(nPlayers)); // Works to create the 1D Array
                var skilltest2D = SharedVariable<double>.Random(year, CreateUniformGaussian2DArray(nYears, nPlayers)); // Doesn't work
                var skilltest2D2 = SharedVariable<double>.Random(year, SharedVariable<double>.Random(player, CreateUniformGaussian2DArray(nYears, nPlayers))); // Neither does this

    So my first problem is how to successfully construct a 2D Shared Variable array for the skill and drawmargin variables.

    Secondly I figured the best way to construct the model setup would be to have two models, one that takes the data for each match and performs inference on the relevant variables and a second that performs inference within the skill and drawmargin variables based on the relationships set up in the code. Here is the code I have written for the match model:

    // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
    
                Model GameModel = new Model(nPlayers * nYears);
    
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skill[year][w].GetCopyFor(GameModel), performancePrecision.GetCopyFor(GameModel));
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skill[year][b].GetCopyFor(GameModel), performancePrecision.GetCopyFor(GameModel));
                        Variable<double> white_drawMargin = Variable.Copy(drawMargin[year][w].GetCopyFor(GameModel));
                        Variable<double> black_drawMargin = Variable.Copy(drawMargin[year][b].GetCopyFor(GameModel));
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantage.GetCopyFor(GameModel);
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }

    And here is the code I have for the skill and drawmargin model:

    Model skillChange = new Model(nYears);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean.GetCopyFor(skillChange), drawMarginPrecision.GetCopyFor(skillChange)).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean.GetCopyFor(skillChange), drawMarginPrecision.GetCopyFor(skillChange));
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player].GetCopyFor(skillChange), skillChangePrecision.GetCopyFor(skillChange));
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision.GetCopyFor(skillChange));
                            }
                        }
                    }
                }

    Is this the best approach to implementing SharedVariables for the TTT model? Have I specified my models correctly above? How do I declare the skill & drawmargin 2D arrays in SharedVariables?

    Monday, June 9, 2014 11:12 AM
  • You can't use SharedVariables with 2D arrays.  You must use a jagged array instead.  You division into Models is correct, except for the batch counts.  The dynamics model has to be done as a single batch because the skills in different years directly refer to each other.  The game model should be batched into groups of games, not nPlayers * nYears.  The grouping of games can be arbitrary and doesn't have to correspond to years.
    Monday, June 9, 2014 1:09 PM
    Owner
  • Thanks Tom, I've had a go at trying to specify the jagged array for the skill variable but I don't think I'm understanding the syntax correctly. The code I've tried is (using the other code above):

    VariableArray<double> skillyear = Variable.Array<double>(year);
    var skill = SharedVariable<double>.Random(skillyear, player, CreateUniformGaussianArray(nPlayers));

    What have I got wrong here?

    Monday, June 9, 2014 2:29 PM
  • The third argument must be a jagged Gaussian array as shown in Jagged shared variable arrays.
    Monday, June 9, 2014 3:24 PM
    Owner
  • I've rewritten my code above using the jagged shared variable arrays however when I come to compile the model I get the build error "Range 'year' is already open in a ForEach block.  Use a cloned range instead." for the line: 

    skill.GetCopyFor(skillChange)[year][player] = Variable.Random(skillPrior).ForEach(player);

    Given this section of the code is otherwise unchanged apart from using GetCopyFor to convert the SharedVariable array into the necessary double array I'm not sure why this error occurs. Please could you advise me what needs to be changed? Full code below:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = SharedVariable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = SharedVariable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecision = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = SharedVariable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = SharedVariable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantage = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skill = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMargin = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                Model skillChange = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill.GetCopyFor(skillChange)[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin.GetCopyFor(skillChange)[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean.GetCopyFor(skillChange), drawMarginPrecision.GetCopyFor(skillChange)).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill.GetCopyFor(skillChange)[year][player] = Variable.Random(skillPrior);
                                drawMargin.GetCopyFor(skillChange)[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean.GetCopyFor(skillChange), drawMarginPrecision.GetCopyFor(skillChange));
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill.GetCopyFor(skillChange)[year][player] = Variable.GaussianFromMeanAndPrecision(skill.GetCopyFor(skillChange)[y - 1][player], skillChangePrecision.GetCopyFor(skillChange));
                                drawMargin.GetCopyFor(skillChange)[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin.GetCopyFor(skillChange)[y - 1][player], drawMarginChangePrecision.GetCopyFor(skillChange));
                            }
                        }
                    }
                }
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                    // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
    
                Model GameModel = new Model(nYears);
    
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skill.GetCopyFor(GameModel)[year][w], performancePrecision.GetCopyFor(GameModel));
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skill.GetCopyFor(GameModel)[year][b], performancePrecision.GetCopyFor(GameModel));
                        Variable<double> white_drawMargin = Variable.Copy(drawMargin.GetCopyFor(GameModel)[year][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMargin.GetCopyFor(GameModel)[year][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantage.GetCopyFor(GameModel);
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
    
                for (int pass = 0; pass < 5; pass++)
                {
                    for (int y = 0; y < nYears; y++)
                    {
                        GameModel.InferShared(engine, y);
                    }
                }
                for (int pass = 0; pass < 5; pass++)
                {
                    for (int y = 0; y < 1; y++)
                    {
                        skillChange.InferShared(engine, y);
                    }
                }
    
                var skillPost = skill.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMargin.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", drawMarginMean.Marginal<Gaussian>(), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", drawMarginPrecision.Marginal<Gamma>().GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecision.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", skillChangePrecision.Marginal<Gamma>().GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", drawMarginChangePrecision.Marginal<Gamma>().GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantage.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }


    Monday, July 14, 2014 4:41 PM
  • When you call GetCopyFor, it needs to be outside of any ForEach block.  Otherwise it thinks you want to create a copy for every iteration of the block.
    Monday, July 14, 2014 6:01 PM
    Owner
  • I've rewritten the code to move the GetCopyFor's outside the blocks, but now I get the error "Cannot assign to array more than once." on line:

    skillCopy[year][player] = Variable.Random(skillPrior).ForEach(player);

    Surely I've not assigned anything to the array at this stage? I'm not sure what's generating this bug. Full copy of the code below:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = SharedVariable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = SharedVariable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecision = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = SharedVariable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = SharedVariable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantage = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skill = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMargin = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                Model skillChange = new Model(1);
    
                var skillCopy = skill.GetCopyFor(skillChange);
                var drawMarginCopy = drawMargin.GetCopyFor(skillChange);
                var drawMarginMeanCopy = drawMarginMean.GetCopyFor(skillChange);
                var drawMarginPrecisionCopy = drawMarginPrecision.GetCopyFor(skillChange);
                var skillChangePrecisionCopy = skillChangePrecision.GetCopyFor(skillChange);
                var drawMarginChangePrecisionCopy = drawMarginChangePrecision.GetCopyFor(skillChange);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skillCopy[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMarginCopy[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMeanCopy, drawMarginPrecisionCopy).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skillCopy[year][player] = Variable.Random(skillPrior);
                                drawMarginCopy[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMeanCopy, drawMarginPrecisionCopy);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skillCopy[year][player] = Variable.GaussianFromMeanAndPrecision(skillCopy[y - 1][player], skillChangePrecisionCopy);
                                drawMarginCopy[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginCopy[y - 1][player], drawMarginChangePrecisionCopy);
                            }
                        }
                    }
                }
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                    // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
    
                Model GameModel = new Model(nYears);
    
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skill.GetCopyFor(GameModel)[year][w], performancePrecision.GetCopyFor(GameModel));
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skill.GetCopyFor(GameModel)[year][b], performancePrecision.GetCopyFor(GameModel));
                        Variable<double> white_drawMargin = Variable.Copy(drawMargin.GetCopyFor(GameModel)[year][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMargin.GetCopyFor(GameModel)[year][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantage.GetCopyFor(GameModel);
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
    
                for (int pass = 0; pass < 5; pass++)
                {
                    for (int y = 0; y < nYears; y++)
                    {
                        GameModel.InferShared(engine, y);
                    }
                }
                for (int pass = 0; pass < 5; pass++)
                {
                    for (int y = 0; y < 1; y++)
                    {
                        skillChange.InferShared(engine, y);
                    }
                }
    
                var skillPost = skill.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMargin.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", drawMarginMean.Marginal<Gaussian>(), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", drawMarginPrecision.Marginal<Gamma>().GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecision.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", skillChangePrecision.Marginal<Gamma>().GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", drawMarginChangePrecision.Marginal<Gamma>().GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantage.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }

    Wednesday, July 16, 2014 2:50 PM
  • That is a duplicate assignment, since skillCopy was already assigned by the GetCopyFor operation.
    Thursday, July 17, 2014 1:50 PM
    Owner
  • I'm unsure how to go about architecting the appropriate use of SharedVariables in this case. The problem I have is how to replicate the skill/drawMargin inter-relationship loop when the skill and drawMargin variables are no longer Infer.Net Variables but SharedVariables. I can't reference the SharedVariables directly within the loop as I understand that a copy of the SharedVariable is needed, however when I create the copy of the SharedVariable (as the variable skillCopy above) I then can't assign the relationships within the skillCopy variable as I run into the 'already assigned' issue. How do I recreate the function of the skill/drawMargin inter-relationship loop with SharedVariables?
    Thursday, July 17, 2014 2:58 PM
  • You need to use the approach documented in Defining shared variables within a model.
    Thursday, July 17, 2014 3:10 PM
    Owner
  • Thanks Tom, I've rewritten the code based on that example and it compiles successfully now, however I must be making some other mistake as the inference on the SharedVariables appears not to have worked - when I output the marginals to the console they are unchanged compared to the priors (although the non-Shared Variables are inferring correctly). What do I need to change in the following code so Infer.Net will correctly infer the marginals of the SharedVariables?

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecisionShared = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantageShared = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skillShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMarginShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                Model skillSharedDef = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                skillShared.SetDefinitionTo(skillSharedDef, skill);
                drawMarginShared.SetDefinitionTo(skillSharedDef, drawMargin);
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
    
                Model GameModel = new Model(nYears);
    
                var skillCopy = skillShared.GetCopyFor(GameModel);
                var performancePrecisionCopy = performancePrecisionShared.GetCopyFor(GameModel);
                var drawMarginCopy = drawMarginShared.GetCopyFor(GameModel);
                var whiteAdvantageCopy = whiteAdvantageShared.GetCopyFor(GameModel);
    
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[year][w], performancePrecisionCopy);
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[year][b], performancePrecisionCopy);
                        Variable<double> white_drawMargin = Variable.Copy(drawMarginCopy[year][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMarginCopy[year][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantageCopy;
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
    
                for (int pass = 0; pass < 5; pass++)
                {
                    for (int y = 0; y < nYears; y++)
                    {
                        GameModel.InferShared(engine, y);
                    }
                }
                for (int pass = 0; pass < 5; pass++)
                {
                    for (int y = 0; y < 1; y++)
                    {
                        skillSharedDef.InferShared(engine, y);
                    }
                }
    
                var skillPost = skillShared.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMarginShared.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecisionShared.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantageShared.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }
    Thursday, July 17, 2014 6:40 PM
  • This issue is the way that you are looping through the sub-models.  You want to do something like this:

                for (int pass = 0; pass < 5; pass++)
                {
                    skillSharedDef.InferShared(engine, 0);
                    for (int y = 0; y < nYears; y++)
                    {
                        Console.WriteLine("year {0}", y);
                        // set observations for this year...
                        GameModel.InferShared(engine, y);
                    }
                }
    This way the priors are set before you process the games, and you visit all the sub-models on every pass.  (See the code for Shared variable LDA for an example of this pattern.) Also, if you are batching by year, then you need to set the observations for that year before you run inference in GameModel.


    Friday, July 18, 2014 1:22 PM
    Owner
  • Thanks Tom, that works for me in the simple case of: 

    for (int pass = 0; pass < 10; pass++)
                {
                    skillSharedDef.InferShared(engine, 0);
                    GameModel.InferShared(engine, 0);
                }

    However when I try to split by year I run in to problems. I tried modifying your code above by creating variables that only have one year's worth of observations in and looping through them:

    for (int pass = 0; pass < 10; pass++)
                {
                    skillSharedDef.InferShared(engine, 0);
                    for (int y = 0; y < nYears; y++)
                    {
                        var whiteDataYear = new int[nYears][];
                        whiteDataYear[y] = whiteData[y];
                        var blackDataYear = new int[nYears][];
                        blackDataYear[y] = blackData[y];
                        var outcomeDataYear = new int[nYears][];
                        outcomeDataYear[y] = outcomeData[y];
                        whitePlayer = Variable.Observed(whiteDataYear, year, game).Named("whitePlayer");
                        blackPlayer = Variable.Observed(blackDataYear, year, game).Named("blackPlayer");
                        outcome = Variable.Observed(outcomeDataYear, year, game).Named("outcome");
                        Console.WriteLine("Year {0}", y);
                        GameModel.InferShared(engine, y);
                    }
                }

    However inference under this code doesn't work correctly; the program runs but the outputs are much worse than the simple loop before and it also takes significantly longer to run. What have I done wrong here?

    Saturday, July 19, 2014 1:37 PM
  • The problem is that you are re-assigning the variables whitePlayer etc.  You just want to change their ObservedValue property.
    Tuesday, July 22, 2014 11:25 AM
    Owner
  • I've rewritten the code to change the ObservedValue properties instead as below:

    for (int pass = 0; pass < 10; pass++)
                {
                    skillSharedDef.InferShared(engine, 0);
                    for (int y = 0; y < nYears; y++)
                    {
                        var whiteDataYear = new int[nYears][];
                        var blackDataYear = new int[nYears][];
                        var outcomeDataYear = new int[nYears][];
                        whiteDataYear[y] = whiteData[y];
                        blackDataYear[y] = blackData[y];
                        outcomeDataYear[y] = outcomeData[y];
                        whitePlayer.ObservedValue = whiteDataYear;
                        blackPlayer.ObservedValue = blackDataYear;
                        outcome.ObservedValue = outcomeDataYear;
                        Console.WriteLine("Year {0}", y);
                        GameModel.InferShared(engine, y);
                    }
                }


    However this throws an error in the Infer.net model code:

    // Create array for replicates of 'vdouble32_F'
    			DistributionRefArray<DistributionStructArray<Gaussian,double>,double[]> vdouble32_F = new DistributionRefArray<DistributionStructArray<Gaussian,double>,double[]>(10);
    			Parallel.For(0, 10, delegate(int year) {
    				// Create array for replicates of 'vdouble32_F'
    				vdouble32_F[year] = new DistributionStructArray<Gaussian,double>(this.NGames[year]);
    				for(int game = 0; game<this.NGames[year]; game++) {
    					if (this.Outcome[year][game]==0) { //Error occurs on this line: "Object reference not set to an instance of an object."
    						vdouble32_F[year][game] = Gaussian.Uniform();
    					}
    				}
    			});

    So the error "Object reference not set to an instance of an object." gets thrown because the other years in the data array are null values. To try get around this I re-wrote the code to feed in placeholder arrays:

    for (int y = 0; y < nYears; y++)
                    {
                        var whiteDataYear = new int[nYears][];
                        var blackDataYear = new int[nYears][];
                        var outcomeDataYear = new int[nYears][];
                        for (int z = 0; z < nYears; z++)
                        {
                            if (z == y)
                            {
                                whiteDataYear[y] = whiteData[y];
                                blackDataYear[y] = blackData[y];
                                outcomeDataYear[y] = outcomeData[y];
                            }
                            else
                            {
                                var placeholder = new int[outcomeData[z].Length];
                                whiteDataYear[z] = placeholder;
                                blackDataYear[z] = placeholder;
                                outcomeDataYear[z] = placeholder;
                            }
                        }
                        whitePlayer.ObservedValue = whiteDataYear;
                        blackPlayer.ObservedValue = blackDataYear;
                        outcome.ObservedValue = outcomeDataYear;
                        Console.WriteLine("Year {0}", y);
                        GameModel.InferShared(engine, y);
                    }

    Now this code runs successfully but the outputs from inference are wrong - I think because the placeholder values are actually zeros and the model treats it as data to learn off.

    I'm not sure how to get around the 'null' issue without putting in placeholder data that will be incorrectly inferred off. How should I structure the variables I set as the ObservedValues within the model to avoid this problem?

    Wednesday, July 23, 2014 1:04 PM
  • Set the number of years to 1 in the GameModel.
    Thursday, July 24, 2014 8:02 AM
    Owner
  • Sorry Tom I'm not sure if I understand you correctly. Do you mean setting the number of batch years to 1 like so?:

    Model GameModel = new Model(1);

    I tried this with both versions of the inference loop code above and neither worked. Full code below:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
                engine.Compiler.UseParallelForLoops = true;
                engine.NumberOfIterations = 10;
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecisionShared = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantageShared = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skillShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMarginShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                Model skillSharedDef = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                skillShared.SetDefinitionTo(skillSharedDef, skill);
                drawMarginShared.SetDefinitionTo(skillSharedDef, drawMargin);
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                Range game = new Range(nGames[year]).Named("game");
                var whitePlayer = Variable.Observed(whiteData, year, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, year, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, year, game).Named("outcome");
    
                Model GameModel = new Model(1);
    
                var skillCopy = skillShared.GetCopyFor(GameModel);
                var performancePrecisionCopy = performancePrecisionShared.GetCopyFor(GameModel);
                var drawMarginCopy = drawMarginShared.GetCopyFor(GameModel);
                var whiteAdvantageCopy = whiteAdvantageShared.GetCopyFor(GameModel);
    
                using (Variable.ForEach(year))
                {
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[year][game];
                        var b = blackPlayer[year][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[year][w], performancePrecisionCopy);
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[year][b], performancePrecisionCopy);
                        Variable<double> white_drawMargin = Variable.Copy(drawMarginCopy[year][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMarginCopy[year][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantageCopy;
                        using (Variable.Case(outcome[year][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[year][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[year][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
    
                for (int pass = 0; pass < 10; pass++)
                {
                    skillSharedDef.InferShared(engine, 0);
                    for (int y = 0; y < nYears; y++)
                    {
                        var whiteDataYear = new int[nYears][];
                        var blackDataYear = new int[nYears][];
                        var outcomeDataYear = new int[nYears][];
                        whiteDataYear[y] = whiteData[y];
                        blackDataYear[y] = blackData[y];
                        outcomeDataYear[y] = outcomeData[y];
                        whitePlayer.ObservedValue = whiteDataYear;
                        blackPlayer.ObservedValue = blackDataYear;
                        outcome.ObservedValue = outcomeDataYear;
                        Console.WriteLine("Year {0}", y);
                        GameModel.InferShared(engine, y);
                    }
                }
    
                var skillPost = skillShared.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMarginShared.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecisionShared.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantageShared.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }

    Thursday, July 24, 2014 4:55 PM
  • No, the number of batches must be nYears.  But each batch should operate on a single year.  Thus the observed variables will be for a single year.
    Monday, July 28, 2014 12:41 PM
    Owner
  • I've tried rewriting the code to not loop through years but to operate on a single year. However I run in to problems with the 'game' Range as I'm not sure how to structure the code to handle the change in the size of the 'game' Range whilst looping through the years to perform inference. The code I've tried is:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
                engine.Compiler.UseParallelForLoops = true;
                engine.NumberOfIterations = 10;
    
                int nPlayers = 10;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecisionShared = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantageShared = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skillShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMarginShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                Model skillSharedDef = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                skillShared.SetDefinitionTo(skillSharedDef, skill);
                drawMarginShared.SetDefinitionTo(skillSharedDef, drawMargin);
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, year).Named("nGames");
                int currentyear = 0;
                Range game = new Range(nGames[currentyear]).Named("game");
    
                var whitePlayer = Variable.Observed(whiteData[currentyear], game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData[currentyear], game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData[currentyear], game).Named("outcome");
    
                Model GameModel = new Model(nYears);
    
                var skillCopy = skillShared.GetCopyFor(GameModel);
                var performancePrecisionCopy = performancePrecisionShared.GetCopyFor(GameModel);
                var drawMarginCopy = drawMarginShared.GetCopyFor(GameModel);
                var whiteAdvantageCopy = whiteAdvantageShared.GetCopyFor(GameModel);
               
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[game];
                        var b = blackPlayer[game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[currentyear][w], performancePrecisionCopy);
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[currentyear][b], performancePrecisionCopy);
                        Variable<double> white_drawMargin = Variable.Copy(drawMarginCopy[currentyear][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMarginCopy[currentyear][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantageCopy;
                        using (Variable.Case(outcome[game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                
    
                for (int pass = 0; pass < 10; pass++)
                {
                    skillSharedDef.InferShared(engine, 0);
                    for (int y = 0; y < nYears; y++)
                    {
                        currentyear = y;
                        whitePlayer.ObservedValue = whiteData[currentyear];
                        blackPlayer.ObservedValue = blackData[currentyear];
                        outcome.ObservedValue = outcomeData[currentyear];
                        Console.WriteLine("Year {0}", y);
                        GameModel.InferShared(engine, y);
                    }
                }
    
                var skillPost = skillShared.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMarginShared.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecisionShared.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantageShared.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 1000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }

    The line that throws the error is:

    whitePlayer.ObservedValue = whiteData[currentyear];
    Because the game Range is different between years and hence the size of the whitePlayer VariableArray should change between years. How should I structure the code to avoid this problem?

    Monday, July 28, 2014 2:42 PM
  • currentYear needs to be an observed variable.
    Monday, July 28, 2014 2:50 PM
    Owner
  • Thanks for all your help Tom, got it working now! Are there any plans in the future for Infer.net to support splitting work across a computer cluster?
    Tuesday, July 29, 2014 1:40 PM
  • I've attempted to re-write the model to take data in one array instead of a jagged array based off years (I did this because I wanted finer control over the batch sizes to optimise performance instead of being limited to annual batches) however I've ran into problems. The code I have compiles and iterates over the Skill model fine, however when it comes to the Game model it compiles but uses extremely large amounts of memory compared to the previous model (Even for sample sizes of only ~10000 games it uses greater than my free space of ~4 gigs) and if it does finish loading the memory it then iterates very slowly compared to the previous model. What am I doing in the code that's causing this performance degradation?

    using GaussianArray = DistributionStructArray<Gaussian, double>;
    using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
    using GaussianArrayArrayArray = DistributionRefArray<DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>, double[][]>;
      
    class TrainModel
        {
            public int[][] AllPlayers;
            public InferenceEngine engine;
            public VariableArray<VariableArray<double>, double[][]> skill;
            public ISharedVariableArray<VariableArray<double>, double[][]> skillShared;
            public SharedVariable<double> PerformancePrecision;
            public Variable<double> skillChangePrecision;
            public VariableArray<int> firstYear;
            public TrainModel(int[] Year, int[] Player1Data, int[] Player2Data, int[] firstYears)
            {
                engine = new InferenceEngine();
                //engine.Compiler.UseSerialSchedules = true;
                engine.ShowProgress = true;
                //engine.Compiler.ReturnCopies = false;
                //engine.Compiler.FreeMemory = true;
                engine.Compiler.UseParallelForLoops = true;
                engine.NumberOfIterations = 20;
    
                AllPlayers = new int[2][];
                AllPlayers[0] = Player1Data;
                AllPlayers[1] = Player2Data;
                int nPlayers = (from array in AllPlayers from arr in array select arr).Distinct().Count();
                int nYears = (from array in Year select array).Distinct().Count();
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(2000, 800 * 800);
                var PerformancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
    
                PerformancePrecision = SharedVariable<double>.Random(PerformancePrecisionPrior).Named("PerformancePrecision");
                skillChangePrecision = Variable.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
    
                skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                skillShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
    
                Model skillSharedDef = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                            }
                        }
                    }
                }
    
                skillShared.SetDefinitionTo(skillSharedDef, skill);
    
                // Learn the skills from the data
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => firstYears[i]);
                var nGames = Variable.Observed(Player1Data.Length).Named("nGames");
    
                Range game = new Range(nGames).Named("game");
                var whitePlayer = Variable.Observed(Player1Data, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(Player2Data, game).Named("blackPlayer");
                var CurrentYear = Variable.Observed(Year, game).Named("CurrentYear");
    
                Model GameModel = new Model(1);
    
                var skillCopy = skillShared.GetCopyFor(GameModel).Named("skillCopy");
                var PerformancePrecisionCopy = PerformancePrecision.GetCopyFor(GameModel).Named("PerformancePrecisionCopy");
    
                using (Variable.ForEach(game))
                {
                    var w = whitePlayer[game];
                    var b = blackPlayer[game];
                    var currentyear = CurrentYear[game];
                    Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[currentyear][w], PerformancePrecisionCopy);
                    Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[currentyear][b], PerformancePrecisionCopy);
                    Variable<double> white_delta = white_performance - black_performance;
                    Variable.ConstrainTrue(white_delta > 0);
                }
    
                for (int pass = 0; pass < 20; pass++)
                {
                    Console.Write(" Pass: {0} /", pass);
                    skillSharedDef.InferShared(engine, 0);
                    GameModel.InferShared(engine, 0);
                }
            }

    Sunday, August 10, 2014 12:16 PM
  • It's because you are not batching up the data any more.  You are processing it all at once.
    Monday, August 11, 2014 11:26 AM
    Owner
  • Tom,

    I think there must also be another factor at play as the original version of the code without shared variables (so no batching at all with data arranged by years) works fine on my ~100k matches data set and uses only ~100 MB of memory to run (For reference the yearly-indexed, shared variable version of the code also only uses ~200MB on the ~100k data set) whereas even a smaller dataset of ~20k matches uses 11 GB of memory using the non-yearly arranged code in my last post (and is also at least an order of magnitude slower to iterate across compared to the original version even though neither do any batching). I'd have expected the performance of both sets of code on the same data set to be very similar as neither use any batching.

    Here is a modification of the code in your original post set to a comparable dataset to the one I am using (100k matches, 5000 players, 10 years) which should demonstrate the problem:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                InferenceEngine engine = new InferenceEngine();
                engine.Compiler.UseParallelForLoops = true;
                engine.NumberOfIterations = 20;
    
                int nPlayers = 5000;
                int nYears = 10;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecisionShared = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantageShared = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skillShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMarginShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                Model skillSharedDef = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                skillShared.SetDefinitionTo(skillSharedDef, skill);
                drawMarginShared.SetDefinitionTo(skillSharedDef, drawMargin);
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[] whiteData, blackData, outcomeData, yearData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData, out yearData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                var nGames = Variable.Observed(outcomeData.Length).Named("nGames");
    
                Range game = new Range(nGames).Named("game");
    
                var whitePlayer = Variable.Observed(whiteData, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, game).Named("outcome");
                var yeararray = Variable.Observed(yearData, game).Named("yeararray");
    
                Model GameModel = new Model(1);
    
                var skillCopy = skillShared.GetCopyFor(GameModel);
                var performancePrecisionCopy = performancePrecisionShared.GetCopyFor(GameModel);
                var drawMarginCopy = drawMarginShared.GetCopyFor(GameModel);
                var whiteAdvantageCopy = whiteAdvantageShared.GetCopyFor(GameModel);
    
                using (Variable.ForEach(game))
                {
                    var w = whitePlayer[game];
                    var b = blackPlayer[game];
                    var currentyear = yeararray[game];
                    Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[currentyear][w], performancePrecisionCopy);
                    Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[currentyear][b], performancePrecisionCopy);
                    Variable<double> white_drawMargin = Variable.Copy(drawMarginCopy[currentyear][w]);
                    Variable<double> black_drawMargin = Variable.Copy(drawMarginCopy[currentyear][b]);
                    Variable<double> white_delta = white_performance - black_performance + whiteAdvantageCopy;
                    using (Variable.Case(outcome[game], 0))
                    { // black wins
                        Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                    }
                    using (Variable.Case(outcome[game], 1))
                    { // draw
                        Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                    }
                    using (Variable.Case(outcome[game], 2))
                    { // white wins
                        Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                    }
                }
    
    
                for (int pass = 0; pass < 10; pass++)
                {
                    Console.WriteLine("Pass: {0}", pass);
                    skillSharedDef.InferShared(engine, 0);
                    GameModel.InferShared(engine, 0);
                }
    
                var skillPost = skillShared.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMarginShared.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecisionShared.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantageShared.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[] whiteData, out int[] blackData, out int[] outcomeData, out int[] yearData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 100000;
                var whitePlayer = new List<int>();
                var blackPlayer = new List<int>();
                var outcomes = new List<int>();
                var years = new List<int>();
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer.Add(w);
                        blackPlayer.Add(b);
                        outcomes.Add(outcome);
                        years.Add(year);
                        break;
                    }
                }
                whiteData = whitePlayer.ToArray();
                blackData = blackPlayer.ToArray();
                outcomeData = outcomes.ToArray();
                yearData = years.ToArray();
            }
        }
    }

    Monday, August 11, 2014 6:00 PM
  • This is because the type of indexing that you are using is not optimized well by Infer.NET.  You should get better results if you change your arrays to be indexed by [player][year] instead of [year][player].
    Tuesday, August 12, 2014 4:42 PM
    Owner
  • Thanks Tom, I assume you mean for the 'skill' variable only as that's the only Jagged array left in the model?
    Tuesday, August 12, 2014 4:46 PM
  • Yes.
    Tuesday, August 12, 2014 4:48 PM
    Owner
  • I made that change and it has sped up the code significantly - thanks Tom. I'm still a little unsure why that method of indexing is so much slower than the previous though. Both are using Observed Variables to index a 2D Jagged array, the only difference I can see is that the slower one uses 2 Variable.Array elements to index and the faster method uses 1 Variable.Array element and a single Variable.Observed parameter. Please can you elaborate on what the difference is that's causing the speed delay so I can avoid using it in future? Is there anyway to architect the solution above (in the Monday, August 11 6PM post) to use the faster indexing method?
    Wednesday, August 13, 2014 12:56 PM
  • In the expression "skill[year][player]", if "year" and "player" change according to two separate loop counters, as in the original model, then the generated code will be efficient.  If "year" and "player" change according to a single loop counter, as in the model of August 11 6PM, then the generated code is not efficient and you want the larger range to come first.  I'm not sure why you needed to abandon the original double loop structure over year then game.  Different chunks can share the same year, but contain different games.
    Wednesday, August 13, 2014 1:31 PM
    Owner
  • Thanks Tom, that makes more sense. The reason I'd tried abandoning the double loop structure was because in my dataset I have each match down to a specific date so I'd tried moving from having the variables & data grouped by years (11 years in sample) to months (~130 months in sample) to see if I could get finer resolution on how player's skills changed within years. When I do this obviously I have to use a much higher batch count (one per month) and I'd noticed that running the data in this way wasn't using my processing power very efficiently - whilst the data grouped by years uses ~30% of my CPU power grouping by months uses only ~6%. Given that the factor graph is also much larger grouping by months due to increasing the number of variables in 'skill' by an order of magnitude the combined effect was to slow down the inference drastically (and this effect wasn't just due to the increased size of the factor graph as the non-shared variables version isn't drastically slower whether I group by months or years). I wondered if perhaps that performance was being limited by using too many batches such that Infer.net wasn't able to use the available CPU power effectively (I'm running this on a 64 core server, so maximising parallelisation makes a big difference in run speed) and wanted to try different batch sizes to optimise performance instead of being limited to just the size of the 'year' variable. Does this approach make sense or is there some other way to tweak performance using larger numbers of 'years'?
    Thursday, August 14, 2014 12:02 PM
  • To quote my post from June 09, 2014 1:09 PM: "The grouping of games can be arbitrary and doesn't have to correspond to years."  A batch can have games from multiple years in it.
    Thursday, August 14, 2014 12:22 PM
    Owner
  • I saw that which is why I was trying the single loop method to allow me to specify multiple years within a loop. The code I was using before for the GameModel double loop was:

    for (int y = 0; y < nYears; y++)
                    {
                        currentyear.ObservedValue = y;
                        whitePlayer.ObservedValue = Player1Data[y];
                        blackPlayer.ObservedValue = Player2Data[y];
                        Console.Write(" {0}", y);
                        GameModel.InferShared(engine, y);
                    }

    Given that structure I can't see how I could specify multiple years within a batch without the re-write I was attempting - surely the currentyear needs to become a VariableArray indexed by game else it can only take one Observed Value per batch?

    Thursday, August 14, 2014 1:38 PM
  • You don't need to specify a year per game.  If a batch has 3 years in it, then currentyear would be an array with 3 elements, and whitePlayer is jagged with first dimension of size 3.
    Thursday, August 14, 2014 3:13 PM
    Owner
  • I've written code that I think should be creating as many batches as defined in the BatchTotal variable (I copied the method from the LDA example), however I think I must be making a mistake somewhere as the code is slower to infer than the previous SharedVariable approach I did which could only batch by year. It also runs slower the number of batches that are used, with a single batch being the fastest. What am I doing wrong here?

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Diagnostics;
    using MicrosoftResearch.Infer;
    using MicrosoftResearch.Infer.Models;
    using MicrosoftResearch.Infer.Utils;
    using MicrosoftResearch.Infer.Distributions;
    using MicrosoftResearch.Infer.Maths;
    using MicrosoftResearch.Infer.Factors;
    
    namespace ChessAnalysis
    {
        using GaussianArray = DistributionStructArray<Gaussian, double>;
        using GaussianArrayArray = DistributionRefArray<DistributionStructArray<Gaussian, double>, double[]>;
        class Program
        {
            static void Main(string[] args)
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                InferenceEngine engine = new InferenceEngine();
                engine.Compiler.UseParallelForLoops = true;
                engine.NumberOfIterations = 10;
    
                int nPlayers = 500;
                int nYears = 10;
                int BatchTotal = 1;
                Rand.Restart(1);
    
                var skillPrior = new Gaussian(1200, 800 * 800);
                var drawMarginMeanPrior = new Gaussian(700, 500 * 500);
                var drawMarginPrecisionPrior = Gamma.FromShapeAndRate(2, 500 * 500);
                var performancePrecisionPrior = Gamma.FromShapeAndRate(2, 800 * 800);
                var skillChangePrecisionPrior = Gamma.FromShapeAndRate(2, 26 * 26);
                var drawMarginChangePrecisionPrior = Gamma.FromShapeAndRate(2, 10 * 10);
                var whiteAdvantagePrior = new Gaussian(0, 200 * 200);
    
                var drawMarginMean = Variable<double>.Random(drawMarginMeanPrior).Named("drawMarginMean");
                var drawMarginPrecision = Variable<double>.Random(drawMarginPrecisionPrior).Named("drawMarginPrecision");
                var performancePrecisionShared = SharedVariable<double>.Random(performancePrecisionPrior).Named("performancePrecision");
                var skillChangePrecision = Variable<double>.Random(skillChangePrecisionPrior).Named("skillChangePrecision");
                var drawMarginChangePrecision = Variable<double>.Random(drawMarginChangePrecisionPrior).Named("drawMarginChangePrecision");
                var whiteAdvantageShared = SharedVariable<double>.Random(whiteAdvantagePrior).Named("whiteAdvantage");
    
                Range player = new Range(nPlayers).Named("player");
                Range year = new Range(nYears).Named("year");
                VariableArray<int> firstYear = Variable.Array<int>(player).Named("firstYear");
    
                var priorskill = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var skillShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priorskill);
                var priordm = new GaussianArrayArray(new GaussianArray(Gaussian.Uniform(), player.SizeAsInt), year.SizeAsInt);
                var drawMarginShared = SharedVariable<double>.Random(Variable.Array<double>(player), year, priordm);
    
                var skill = Variable.Array(Variable.Array<double>(player), year).Named("skill");
                var drawMargin = Variable.Array(Variable.Array<double>(player), year).Named("drawMargin");
    
                Model skillSharedDef = new Model(1);
    
                using (var yearBlock = Variable.ForEach(year))
                {
                    var y = yearBlock.Index;
                    using (Variable.If(y == 0))
                    {
                        skill[year][player] = Variable.Random(skillPrior).ForEach(player);
                        drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision).ForEach(player);
                    }
                    using (Variable.If(y > 0))
                    {
                        using (Variable.ForEach(player))
                        {
                            Variable<bool> isFirstYear = (firstYear[player] >= y).Named("isFirstYear");
                            using (Variable.If(isFirstYear))
                            {
                                skill[year][player] = Variable.Random(skillPrior);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMarginMean, drawMarginPrecision);
                            }
                            using (Variable.IfNot(isFirstYear))
                            {
                                skill[year][player] = Variable.GaussianFromMeanAndPrecision(skill[y - 1][player], skillChangePrecision);
                                drawMargin[year][player] = Variable.GaussianFromMeanAndPrecision(drawMargin[y - 1][player], drawMarginChangePrecision);
                            }
                        }
                    }
                }
    
                skillShared.SetDefinitionTo(skillSharedDef, skill);
                drawMarginShared.SetDefinitionTo(skillSharedDef, drawMargin);
    
                // Sample parameter values according to the above model
                firstYear.ObservedValue = Util.ArrayInit(nPlayers, i => Rand.Int(nYears));
                Parameters parameters = new Parameters();
                parameters.drawMarginMean = drawMarginMeanPrior.Sample();
                parameters.drawMarginPrecision = drawMarginPrecisionPrior.Sample();
                parameters.performancePrecision = performancePrecisionPrior.Sample();
                parameters.skillChangePrecision = skillChangePrecisionPrior.Sample();
                parameters.drawMarginChangePrecision = drawMarginChangePrecisionPrior.Sample();
                parameters.whiteAdvantage = whiteAdvantagePrior.Sample();
                parameters.skill = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => skillPrior.Sample()));
                parameters.drawMargin = Util.ArrayInit(nYears, y => Util.ArrayInit(nPlayers, i => Gaussian.Sample(parameters.drawMarginMean, parameters.drawMarginPrecision)));
                for (int y = 0; y < nYears; y++)
                {
                    for (int i = 0; i < nPlayers; i++)
                    {
                        if (y > firstYear.ObservedValue[i])
                        {
                            parameters.skill[y][i] = Gaussian.Sample(parameters.skill[y - 1][i], parameters.skillChangePrecision);
                            parameters.drawMargin[y][i] = Gaussian.Sample(parameters.drawMargin[y - 1][i], parameters.drawMarginChangePrecision);
                        }
                    }
                }
    
                // Sample game outcomes
                int[][] whiteData, blackData, outcomeData;
                GenerateData(parameters, firstYear.ObservedValue, out whiteData, out blackData, out outcomeData);
    
                bool inferParameters = true;  // make this true to infer additional parameters
                //if (!inferParameters)
                //{
                // fix the true parameters
                //    drawMarginMean.ObservedValue = parameters.drawMarginMean;
                //    drawMarginPrecision.ObservedValue = parameters.drawMarginPrecision;
                //    performancePrecision.ObservedValue = parameters.performancePrecision;
                //    skillChangePrecision.ObservedValue = parameters.skillChangePrecision;
                //    drawMarginChangePrecision.ObservedValue = parameters.drawMarginChangePrecision;
                //}
    
                // Learn the skills from the data
                var nYearSubset = Variable.Observed(0).Named("nYearSubset");
                Range yearsubset = new Range(nYearSubset).Named("yearsubset");
                var currentyear = Variable.Observed(new int[0], yearsubset).Named("currentyear");
    
                int[] nGamesData = Util.ArrayInit(nYears, y => outcomeData[y].Length);
                var nGames = Variable.Observed(nGamesData, yearsubset).Named("nGames");
    
                Range game = new Range(nGames[yearsubset]).Named("game");
    
                var whitePlayer = Variable.Observed(whiteData, yearsubset, game).Named("whitePlayer");
                var blackPlayer = Variable.Observed(blackData, yearsubset, game).Named("blackPlayer");
                var outcome = Variable.Observed(outcomeData, yearsubset, game).Named("outcome");
    
                int NumBatches = Math.Min(BatchTotal, nYears + 1);
                Model GameModel = new Model(NumBatches);
    
                var skillCopy = skillShared.GetCopyFor(GameModel);
                var performancePrecisionCopy = performancePrecisionShared.GetCopyFor(GameModel);
                var drawMarginCopy = drawMarginShared.GetCopyFor(GameModel);
                var whiteAdvantageCopy = whiteAdvantageShared.GetCopyFor(GameModel);
    
                using (Variable.ForEach(yearsubset))
                {
                    var cy = currentyear[yearsubset];
                    using (Variable.ForEach(game))
                    {
                        var w = whitePlayer[yearsubset][game];
                        var b = blackPlayer[yearsubset][game];
                        Variable<double> white_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[cy][w], performancePrecisionCopy);
                        Variable<double> black_performance = Variable.GaussianFromMeanAndPrecision(skillCopy[cy][b], performancePrecisionCopy);
                        Variable<double> white_drawMargin = Variable.Copy(drawMarginCopy[cy][w]);
                        Variable<double> black_drawMargin = Variable.Copy(drawMarginCopy[cy][b]);
                        Variable<double> white_delta = white_performance - black_performance + whiteAdvantageCopy;
                        using (Variable.Case(outcome[yearsubset][game], 0))
                        { // black wins
                            Variable.ConstrainTrue(white_delta + white_drawMargin < 0);
                        }
                        using (Variable.Case(outcome[yearsubset][game], 1))
                        { // draw
                            Variable.ConstrainBetween(white_delta, -white_drawMargin, black_drawMargin);
                        }
                        using (Variable.Case(outcome[yearsubset][game], 2))
                        { // white wins
                            Variable.ConstrainTrue(white_delta - black_drawMargin > 0);
                        }
                    }
                }
                double numYearsPerBatch = ((double)nYears) / NumBatches;
                if (numYearsPerBatch == 0) numYearsPerBatch = 1;
                int[] boundary = new int[NumBatches + 1];
                boundary[0] = 0;
                double currBoundary = 0.0;
                for (int batch = 1; batch <= NumBatches; batch++)
                {
                    currBoundary += numYearsPerBatch;
                    int bnd = (int)currBoundary;
                    if (bnd > nYears) bnd = nYears;
                    boundary[batch] = bnd;
                }
                boundary[NumBatches] = nYears;
                for (int pass = 0; pass < 5; pass++)
                {
                    Console.WriteLine("Pass: {0}", pass);
                    skillSharedDef.InferShared(engine, 0);
                    for (int batch = 0; batch < NumBatches; batch++)
                    {
                        int startYear = boundary[batch];
                        int endYear = boundary[batch + 1];
                        int numYearsInBatch = endYear - startYear;
                        nYearSubset.ObservedValue = numYearsInBatch;
                        int[] currentYearInBatch = new int[numYearsInBatch];
                        int[][] whitePlayerInBatch = new int[numYearsInBatch][];
                        int[][] blackPlayerInBatch = new int[numYearsInBatch][];
                        int[][] outcomeInBatch = new int[numYearsInBatch][];
                        int[] nGamesDataInBatch = new int[numYearsInBatch];
                        for (int i = 0, j = startYear; j < endYear; i++, j++)
                        {
                            currentYearInBatch[i] = j;
                            whitePlayerInBatch[i] = whiteData[j];
                            blackPlayerInBatch[i] = blackData[j];
                            nGamesDataInBatch[i] = whiteData[j].Length;
                            outcomeInBatch[i] = outcomeData[j];
                        }
                        currentyear.ObservedValue = currentYearInBatch;
                        whitePlayer.ObservedValue = whitePlayerInBatch;
                        blackPlayer.ObservedValue = blackPlayerInBatch;
                        outcome.ObservedValue = outcomeInBatch;
                        nGames.ObservedValue = nGamesDataInBatch;
                        Console.WriteLine("Year {0}", batch);
                        GameModel.InferShared(engine, batch);
                    }
                }
    
                var skillPost = skillShared.Marginal<Gaussian[][]>();
                var drawMarginPost = drawMarginShared.Marginal<Gaussian[][]>();
    
                // compare estimates to the true values
                if (inferParameters)
                {
                    Console.WriteLine("drawMargin mean = {0} (truth = {1})", engine.Infer<Gaussian>(drawMarginMean), parameters.drawMarginMean);
                    Console.WriteLine("drawMargin precision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginPrecision).GetMean(), parameters.drawMarginPrecision);
                    Console.WriteLine("performancePrecision = {0} (truth = {1})", performancePrecisionShared.Marginal<Gamma>().GetMean(), parameters.performancePrecision);
                    Console.WriteLine("skillChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(skillChangePrecision).GetMean(), parameters.skillChangePrecision);
                    Console.WriteLine("drawMarginChangePrecision = {0} (truth = {1})", engine.Infer<Gamma>(drawMarginChangePrecision).GetMean(), parameters.drawMarginChangePrecision);
                }
                Console.WriteLine("white advantage = {0} (truth = {1})", whiteAdvantageShared.Marginal<Gaussian>(), parameters.whiteAdvantage);
                int countPrinted = 0;
                for (int y = 0; y < nYears; y++)
                {
                    for (int p = 0; p < nPlayers; p++)
                    {
                        if (y >= firstYear.ObservedValue[p])
                        {
                            if (++countPrinted > 3)
                                break;
                            Console.WriteLine("skill[{0}][{1}] = {2} (truth = {3:g4})", y, p, skillPost[y][p], parameters.skill[y][p]);
                            Console.WriteLine("drawMargin[{0}][{1}] = {2} (truth = {3:g4})", y, p, drawMarginPost[y][p], parameters.drawMargin[y][p]);
                        }
                    }
                }
                stopwatch.Stop();
                Console.WriteLine("Time elapsed: {0}", stopwatch.Elapsed);
                while (true) { }
            }
    
            public class Parameters
            {
                public double drawMarginMean, drawMarginPrecision, performancePrecision, skillChangePrecision, drawMarginChangePrecision, whiteAdvantage;
                public double[][] skill, drawMargin;
            }
    
            public static void GenerateData(Parameters parameters, int[] firstYear, out int[][] whiteData, out int[][] blackData, out int[][] outcomeData)
            {
                int nYears = parameters.skill.Length;
                int nPlayers = parameters.skill[0].Length;
                int nGames = 10000;
                var whitePlayer = Util.ArrayInit(nYears, year => new List<int>());
                var blackPlayer = Util.ArrayInit(nYears, year => new List<int>());
                var outcomes = Util.ArrayInit(nYears, year => new List<int>());
                for (int game = 0; game < nGames; game++)
                {
                    while (true)
                    {
                        int w = Rand.Int(nPlayers);
                        int b = Rand.Int(nPlayers);
                        if (w == b)
                            continue;
                        int minYear = Math.Max(firstYear[w], firstYear[b]);
                        int year = Rand.Int(minYear, nYears);
                        double white_delta = parameters.whiteAdvantage + Gaussian.Sample(parameters.skill[year][w], parameters.performancePrecision)
                            - Gaussian.Sample(parameters.skill[year][b], parameters.performancePrecision);
                        double white_drawMargin = parameters.drawMargin[year][w];
                        double black_drawMargin = parameters.drawMargin[year][b];
                        int outcome;
                        if (white_delta > black_drawMargin)
                            outcome = 2;  // white wins
                        else if (white_delta < -white_drawMargin)
                            outcome = 0;  // black wins
                        else
                            outcome = 1;  // draw
                        whitePlayer[year].Add(w);
                        blackPlayer[year].Add(b);
                        outcomes[year].Add(outcome);
                        break;
                    }
                }
                whiteData = Util.ArrayInit(nYears, year => whitePlayer[year].ToArray());
                blackData = Util.ArrayInit(nYears, year => blackPlayer[year].ToArray());
                outcomeData = Util.ArrayInit(nYears, year => outcomes[year].ToArray());
            }
        }
    }

    Thursday, August 14, 2014 11:08 PM
  • If you look at the generated code, you'll see that the available parallelism is limited by the number of years in a batch.  So with a lot of processors you want many years per batch.
    Friday, August 15, 2014 2:48 PM
    Owner
  • This model is now included as an example in Infer.NET 2.6.
    Saturday, November 29, 2014 6:06 PM
    Owner