diff --git a/t4t-games/src/rps.rs b/t4t-games/src/rps.rs index 6b2d65e..944dd9c 100644 --- a/t4t-games/src/rps.rs +++ b/t4t-games/src/rps.rs @@ -94,7 +94,7 @@ pub fn fire_water() -> Normal { /// /// Note that `rps1000` demonstrates that `Normal` can represent extremely large games---this game /// has a payoff table with `3^1000` entries! Such large games can be represented and played -/// without issue, but any function that iterates over the outcomes (such as +/// without issue. However, any function that iterates over the outcomes (such as /// [`is_zero_sum`](t4t::Normal::is_zero_sum) or any solution concept), will leave you waiting /// beyond the heat death of the universe. #[rustfmt::skip] diff --git a/t4t/Cargo.toml b/t4t/Cargo.toml index daad580..c56d3e9 100644 --- a/t4t/Cargo.toml +++ b/t4t/Cargo.toml @@ -17,6 +17,7 @@ log = "0.4.21" num = "0.4.3" rand = "0.8.5" rand_distr = "0.4.3" +rayon = "1.10.0" [dev-dependencies] env_logger = "0.11.3" diff --git a/t4t/src/normal.rs b/t4t/src/normal.rs index ecfcd32..8222364 100644 --- a/t4t/src/normal.rs +++ b/t4t/src/normal.rs @@ -1,4 +1,5 @@ use num::Zero; +use rayon::prelude::*; use std::cmp::Ordering; use std::collections::HashMap; use std::iter::Iterator; @@ -587,6 +588,19 @@ impl Normal { nash } + /// A variant of [`pure_nash_equilibria`] that analyzes the outcomes in parallel. + pub fn pure_nash_equlibria_parallel(&self) -> Vec> { + let (sender, receiver) = std::sync::mpsc::channel(); + self.possible_profiles() + .par_bridge() + .for_each_with(sender, |s, profile| { + if self.is_stable(profile) { + s.send(profile).unwrap(); + } + }); + receiver.iter().collect() + } + /// Return a new profile that represents a /// [Pareto improvement](https://en.wikipedia.org/wiki/Pareto_efficiency) /// on the given profile, if one exists. @@ -634,6 +648,19 @@ impl Normal { pareto } + /// A variant of [`pareto_optimal_solutions`] that analyzes the outcomes in parallel. + pub fn pareto_optimal_solutions_parallel(&self) -> Vec> { + let (sender, receiver) = std::sync::mpsc::channel(); + self.possible_profiles() + .par_bridge() + .for_each_with(sender, |s, profile| { + if self.is_pareto_optimal(profile) { + s.send(profile).unwrap(); + } + }); + receiver.iter().collect() + } + /// Get all dominated move relationships for the given player. If a move is dominated by /// multiple different moves, it will contain multiple entries in the returned vector. ///