/// <summary> /// Trains the specified game. /// </summary> /// <param name="game">The game.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns> /// The <see cref="Results" />. /// </returns> public override Results Train(Game game, IList <string> players, Marginals priors) { var twoPlayer = game as TwoPlayerGame; if (twoPlayer == null) { throw new InvalidOperationException("Multi-player/team games not supported"); } var skills = players.Select(ia => priors.Skills[ia]).ToArray(); this.skill1Prior.ObservedValue = skills[0]; this.skill2Prior.ObservedValue = skills[1]; this.outcome.ObservedValue = (int)twoPlayer.Outcome; this.drawMarginPrior.ObservedValue = priors.DrawMargin; return(new Results { Posteriors = new Posteriors( players, new Gaussian[] { this.engine.Infer <Gaussian>(this.player1skill), this.engine.Infer <Gaussian>(this.player2skill) }, this.engine.Infer <Gaussian>(this.drawMargin)) }); }
/// <summary> /// Trains the specified game. /// </summary> /// <param name="game">The game.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns> /// The <see cref="Results" />. /// </returns> /// <exception cref="System.NotSupportedException">Multi-team games not supported</exception> public override Results Train(Game game, IList<string> players, Marginals priors) { var teamGame = game as TeamGame; if (teamGame == null) { throw new NotSupportedException("Multi-team games not supported"); } this.numberOfPlayers.ObservedValue = teamGame.Players.Count; this.skillPriors.ObservedValue = teamGame.Players.Select(ia => priors.Skills[ia]).ToArray(); this.drawMarginPrior.ObservedValue = priors.DrawMargin; this.team1Count.ObservedValue = teamGame.TeamCounts[0]; this.team2Count.ObservedValue = teamGame.TeamCounts[1]; this.team1Players.ObservedValue = teamGame.Teams[0].PlayerScores.Keys.Select(players.IndexOf).ToArray(); this.team2Players.ObservedValue = teamGame.Teams[1].PlayerScores.Keys.Select(players.IndexOf).ToArray(); this.outcome.ObservedValue = (int)teamGame.Outcome; return new Results { Posteriors = new Posteriors( players, this.engine.Infer<Gaussian[]>(this.skills), this.engine.Infer<Gaussian>(this.drawMargin)) }; }
/// <summary> /// Predicts the outcome (ranking) of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="TwoPlayerPrediction" />. /// </returns> /// <exception cref="System.InvalidOperationException">Multi-player/team games not supported</exception> public override Prediction PredictOutcome(Game game, Marginals posteriors) { var twoPlayer = game as TwoPlayerGame; if (twoPlayer == null) { return(null); } var skills = game.Players.Select(p => posteriors.Skills[p]).ToArray(); this.skill1Prior.ObservedValue = skills[0]; this.skill2Prior.ObservedValue = skills[1]; var outcomePosterior = this.engine.Infer <Bernoulli>(this.outcome); return(new TwoPlayerPrediction { Actual = twoPlayer.Outcome, Predicted = outcomePosterior.GetProbTrue() > 0.5 ? MatchOutcome.Player1Win : MatchOutcome.Player2Win, LogProbOfTruth = twoPlayer.Outcome == MatchOutcome.Draw ? double.NaN : outcomePosterior.GetLogProb(twoPlayer.Outcome == MatchOutcome.Player1Win), IncludeDraws = false }); }
/// <summary> /// Trains the specified game. /// </summary> /// <param name="game">The game.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns> /// The <see cref="Results" />. /// </returns> public override Results Train(Game game, IList <string> players, Marginals priors) { var multiPlayerGame = game as MultiPlayerGame; if (multiPlayerGame == null) { throw new InvalidOperationException("Incorrect game type"); } this.numberOfPlayers.ObservedValue = players.Count; this.skillPriors.ObservedValue = players.Select(ia => priors.Skills[ia]).ToArray(); this.drawMarginPrior.ObservedValue = priors.DrawMargin; this.playersPerGame.ObservedValue = multiPlayerGame.Players.Count; // order the indices of the players by descending order of score this.playerIndices.ObservedValue = multiPlayerGame.PlayersInDescendingScoreOrder.Select(players.IndexOf).ToArray(); this.scores.ObservedValue = multiPlayerGame.Scores.OrderByDescending(x => x).ToArray(); return(new Results { Posteriors = new Posteriors( players, this.engine.Infer <Gaussian[]>(this.skills), this.engine.Infer <Gaussian>(this.drawMargin)) }); }
/// <summary> /// Samples from the model. /// </summary> /// <param name="truth">The truth.</param> /// <param name="players">The players.</param> /// <param name="performanceVariance">The performance variance.</param> /// <param name="drawMargin">The draw margin.</param> /// <returns> /// The <see cref="Game" />. /// </returns> public static Game Sample(Marginals truth, IList <string> players, double performanceVariance, double drawMargin) { double perf1 = new Gaussian(truth.Skills[players[0]].GetMean(), performanceVariance).Sample(); double perf2 = new Gaussian(truth.Skills[players[1]].GetMean(), performanceVariance).Sample(); double diff = perf1 - perf2; MatchOutcome outcome = diff < drawMargin ? MatchOutcome.Draw : (diff > 0 ? MatchOutcome.Player1Win : MatchOutcome.Player2Win); return(TwoPlayerGame.CreateGame(Guid.NewGuid().ToString(), players[0], players[1], outcome)); }
/// <summary> /// Runs the specified model. /// </summary> /// <param name="games">The games.</param> /// <param name="count">The count.</param> /// <param name="verbose">if set to <c>true</c> [verbose].</param> public override void Run(IEnumerable <Game> games, int count, bool verbose) { this.PlayerPosteriors = this.Priors.Skills.ToDictionary(ia => ia.Key, ia => new List <Gaussian> { ia.Value }); this.DrawMargins = new List <Gaussian> { this.Priors.DrawMargin }; this.Predictions = new List <Prediction>(); using (new CodeTimer(this.Name)) { foreach (var game in games) { var priors = new Marginals { DrawMargin = this.DrawMargins.Last() }; foreach (var player in game.Players) { if (!this.PlayerPosteriors.ContainsKey(player)) { this.PlayerPosteriors[player] = new List <Gaussian> { this.SkillPrior }; } priors.Skills[player] = this.PlayerPosteriors[player].Last(); } // Predict outcome of game var prediction = PredictModel?.PredictOutcome(game, priors); if (prediction != null) { this.Predictions.Add(prediction); } // Train model using this game this.LastResults = this.TrainModel.Train(game, game.Players, priors); foreach (var player in game.Players) { this.PlayerPosteriors[player].Add(this.LastResults.Posteriors.Skills[player]); } this.DrawMargins.Add(this.LastResults.Posteriors.DrawMargin); if (verbose) { var post = this.LastResults.Posteriors.Skills.Select(ia => $"{ia.Key}: {ia.Value}").ToArray(); Console.WriteLine(@"Game {0}, Posteriors: {1}", game.Id, string.Join(", ", post)); } } } }
/// <summary> /// Samples from the model. /// </summary> /// <param name="truth">The truth.</param> /// <param name="players">The players.</param> /// <param name="performanceVariance">The performance variance.</param> /// <returns>The <see cref="Game"/>.</returns> public static TwoPlayerGame Sample(Marginals truth, IList <string> players, double performanceVariance) { double perf1 = new Gaussian(truth.Skills[players[0]].GetMean(), performanceVariance).Sample(); double perf2 = new Gaussian(truth.Skills[players[1]].GetMean(), performanceVariance).Sample(); return(TwoPlayerGame.CreateGame( Guid.NewGuid().ToString(), players[0], players[1], perf1 > perf2 ? MatchOutcome.Player1Win : MatchOutcome.Player2Win)); }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="TwoPlayerPrediction" />. /// </returns> /// <exception cref="System.InvalidOperationException">Multi-player/team games not supported</exception> public TwoPlayerPrediction PredictOutcome(TwoPlayerGame game, Marginals posteriors) { return(new TwoPlayerPrediction { Actual = game.Outcome, Predicted = this.Parameters.IncludeDraws ? (MatchOutcome)this.OutcomeDistribution.Sample() : (Rand.Int(2) == 0 ? MatchOutcome.Player1Win : MatchOutcome.Player2Win), IncludeDraws = this.Parameters.IncludeDraws }); }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="TwoTeamPrediction" />. /// </returns> public TwoTeamPrediction PredictOutcome(TeamGame game, Marginals posteriors) { return(new TwoTeamPrediction { Actual = game.Outcome, Predicted = this.Parameters.IncludeDraws ? (TeamMatchOutcome)this.OutcomeDistribution.Sample() : (Rand.Int(2) == 0 ? TeamMatchOutcome.Team1Win : TeamMatchOutcome.Team2Win), IncludeDraws = this.Parameters.IncludeDraws }); }
/// <summary> /// Trains the specified games. /// </summary> /// <param name="games">The games.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns>The <see cref="IList{Results}"/>.</returns> public IList <Results> Train(IList <Game> games, IList <string> players, Marginals priors) { var results = new List <Results> { new Results { Posteriors = priors } }; foreach (var game in games) { results.Add(this.Train(game, players, results.Last().Posteriors)); } return(results); }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="Prediction" />. /// </returns> public override Prediction PredictOutcome(Game game, Marginals posteriors) { var teamGame = game as TeamGame; if (teamGame == null) { // unsupported game type return null; } this.numberOfPlayers.ObservedValue = teamGame.Players.Count; this.skillPriors.ObservedValue = teamGame.Players.Select(ia => posteriors.Skills[ia]).ToArray(); this.drawMarginPrior.ObservedValue = posteriors.DrawMargin; this.team1Count.ObservedValue = teamGame.TeamCounts[0]; this.team2Count.ObservedValue = teamGame.TeamCounts[1]; this.team1Players.ObservedValue = teamGame.Teams[0].PlayerScores.Keys.Select(teamGame.Players.IndexOf).ToArray(); this.team2Players.ObservedValue = teamGame.Teams[1].PlayerScores.Keys.Select(teamGame.Players.IndexOf).ToArray(); var outcomePosterior = this.engine.Infer<Discrete>(this.outcome); double logProbOfTruth = outcomePosterior.GetLogProb((int)teamGame.Outcome); // check if multimodal if (outcomePosterior.GetMode() == 0 && Math.Abs(outcomePosterior.Evaluate(0) - outcomePosterior.Evaluate(2)) < double.Epsilon) { // Random outcome var randomOutcome = Rand.Int(2) == 0 ? TeamMatchOutcome.Team1Win : TeamMatchOutcome.Team2Win; return new TwoTeamPrediction { Actual = teamGame.Outcome, Predicted = randomOutcome, IncludeDraws = true, LogProbOfTruth = logProbOfTruth }; } return new TwoTeamPrediction { Actual = teamGame.Outcome, Predicted = (TeamMatchOutcome)outcomePosterior.GetMode(), LogProbOfTruth = logProbOfTruth, IncludeDraws = true }; }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="Prediction" />. /// </returns> public Prediction PredictOutcome(Game game, Marginals posteriors) { var twoPlayer = game as TwoPlayerGame; if (twoPlayer != null) { return(this.PredictOutcome(twoPlayer, posteriors)); } var teamGame = game as TeamGame; if (teamGame != null) { return(this.PredictOutcome(teamGame, posteriors)); } // Unsupported game type return(null); }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="Prediction" />. /// </returns> /// <exception cref="System.InvalidOperationException">Multi-player/team games not supported</exception> public override Prediction PredictOutcome(Game game, Marginals posteriors) { var twoPlayer = game as TwoPlayerGame; if (twoPlayer == null) { return(null); } var skills = game.Players.Select(p => posteriors.Skills[p]).ToArray(); this.skill1Prior.ObservedValue = skills[0]; this.skill2Prior.ObservedValue = skills[1]; this.drawMarginPrior.ObservedValue = posteriors.DrawMargin; var outcomePosterior = this.engine.Infer <Discrete>(this.outcome); // check if multimodal if (outcomePosterior.GetMode() == 0 && Math.Abs(outcomePosterior.Evaluate(0) - outcomePosterior.Evaluate(2)) < double.Epsilon) { // Random outcome var randomOutcome = Rand.Int(2) == 0 ? MatchOutcome.Player1Win : MatchOutcome.Player2Win; return(new TwoPlayerPrediction { Actual = twoPlayer.Outcome, Predicted = randomOutcome, IncludeDraws = true }); } return(new TwoPlayerPrediction { Actual = twoPlayer.Outcome, Predicted = (MatchOutcome)outcomePosterior.GetMode(), LogProbOfTruth = outcomePosterior.GetLogProb((int)twoPlayer.Outcome), IncludeDraws = true }); }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="Prediction" />. /// </returns> public abstract Prediction PredictOutcome(Game game, Marginals posteriors);
/// <summary> /// Trains the specified game. /// </summary> /// <param name="game">The game.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns>The <see cref="Results"/>.</returns> public abstract Results Train(Game game, IList <string> players, Marginals priors);
/// <summary> /// Trains the specified game. /// </summary> /// <param name="game">The game.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns> /// The <see cref="Results" />. /// </returns> public Results Train(Game game, IList <string> players, Marginals priors) { return(new Results { Posteriors = priors }); }
/// <summary> /// Trains the specified games. /// </summary> /// <param name="games">The games.</param> /// <param name="players">The players.</param> /// <param name="priors">The priors.</param> /// <returns> /// The <see cref="IList{Results}" />. /// </returns> public IList <Results> Train(IList <Game> games, IList <string> players, Marginals priors) { return(games.Select(ia => new Results { Posteriors = priors }).ToList()); }
/// <summary> /// Predicts the outcome of a game. /// </summary> /// <param name="game">The game.</param> /// <param name="posteriors">The posteriors.</param> /// <returns> /// The <see cref="Prediction" />. /// </returns> public override Prediction PredictOutcome(Game game, Marginals posteriors) { // Not supported for this class return(null); }
/// <summary> /// Samples the specified inputs. /// </summary> /// <param name="performanceVariance">The performance variance.</param> /// <param name="playerIndices">The player indices.</param> /// <param name="truth">The truth.</param> /// <param name="startIndex">The start index.</param> /// <returns> /// The <see cref="GameCollection" />. /// </returns> public static GameCollection Sample(double performanceVariance, int[][] playerIndices, Marginals truth, int startIndex = 0) { // var random = new Random(0); var players = truth.Skills.Keys.ToArray(); var games = new GameCollection(ia => ia.Id); int count = playerIndices.Length; for (int i = 0; i < count; i++) { // Because this model relies on the fact that the game scores are provided in descending order, // we need to randomise the player order var randomIndices = playerIndices[i].OrderBy(ia => Guid.NewGuid()).ToArray(); // Skills var skills = playerIndices[i].Select(ia => truth.Skills[players[ia]].Point).ToArray(); // Sample peformances to get scores var scores = skills.Select(ia => new Gaussian(ia, performanceVariance).Sample()).ToArray(); var game = new MultiPlayerGame { Id = (startIndex + i + 1).ToString("D") }; for (int j = 0; j < playerIndices[i].Length; j++) { // game.Teams.Add(new Team { Players = new[] { players[randomIndices[j]] }, Score = (int)scores[j] }); game.Players.Add(players[randomIndices[j]]); game.Scores.Add((int)scores[j]); } games.Add(game); } return(games); }