44 #include <ilqgames/cost/player_cost.h> 45 #include <ilqgames/dynamics/multi_player_flat_system.h> 46 #include <ilqgames/dynamics/multi_player_integrable_system.h> 47 #include <ilqgames/utils/compute_strategy_costs.h> 48 #include <ilqgames/utils/operating_point.h> 49 #include <ilqgames/utils/quadratic_cost_approximation.h> 50 #include <ilqgames/utils/strategy.h> 51 #include <ilqgames/utils/types.h> 53 #include <glog/logging.h> 54 #include <Eigen/Dense> 60 bool NumericalCheckLocalNashEquilibrium(
61 const std::vector<PlayerCost>& player_costs,
62 const std::vector<Strategy>& strategies,
63 const OperatingPoint& operating_point,
64 const MultiPlayerIntegrableSystem& dynamics,
const VectorXf& x0,
65 float max_perturbation,
bool open_loop) {
66 CHECK_EQ(strategies.size(), player_costs.size());
67 CHECK_EQ(strategies.size(), dynamics.NumPlayers());
68 CHECK_EQ(x0.size(), dynamics.XDim());
70 const size_t num_time_steps = strategies[0].Ps.size();
71 CHECK_EQ(num_time_steps, strategies[0].alphas.size());
75 const bool was_integrating_using_euler =
76 MultiPlayerIntegrableSystem::IntegrationUsesEuler();
77 if (!was_integrating_using_euler)
78 MultiPlayerIntegrableSystem::IntegrateUsingEuler();
79 const std::vector<float> nominal_costs = ComputeStrategyCosts(
80 player_costs, strategies, operating_point, dynamics, x0, open_loop);
84 std::vector<Strategy> perturbed_strategies_lower(strategies);
85 std::vector<Strategy> perturbed_strategies_upper(strategies);
86 for (PlayerIndex ii = 0; ii < dynamics.NumPlayers(); ii++) {
87 for (
size_t kk = 0; kk < num_time_steps - 1; kk++) {
88 VectorXf& alphak_lower = perturbed_strategies_lower[ii].alphas[kk];
89 VectorXf& alphak_upper = perturbed_strategies_upper[ii].alphas[kk];
91 for (
size_t jj = 0; jj < alphak_lower.size(); jj++) {
92 alphak_lower(jj) -= max_perturbation;
93 alphak_upper(jj) += max_perturbation;
96 const std::vector<float> perturbed_costs_lower =
97 ComputeStrategyCosts(player_costs, perturbed_strategies_lower,
98 operating_point, dynamics, x0, open_loop);
99 const std::vector<float> perturbed_costs_upper =
100 ComputeStrategyCosts(player_costs, perturbed_strategies_upper,
101 operating_point, dynamics, x0, open_loop);
104 if (std::min(perturbed_costs_lower[ii], perturbed_costs_upper[ii]) <
118 if (!was_integrating_using_euler)
119 MultiPlayerIntegrableSystem::IntegrateUsingRK4();
124 alphak_lower = strategies[ii].alphas[kk];
125 alphak_upper = strategies[ii].alphas[kk];
131 MultiPlayerIntegrableSystem::IntegrateUsingRK4();
135 bool NumericalCheckLocalNashEquilibrium(
const Problem& problem,
136 float max_perturbation,
138 return NumericalCheckLocalNashEquilibrium(
139 problem.PlayerCosts(), problem.CurrentStrategies(),
140 problem.CurrentOperatingPoint(), *problem.Dynamics(),
141 problem.InitialState(), max_perturbation, open_loop);
144 bool CheckSufficientLocalNashEquilibrium(
145 const std::vector<PlayerCost>& player_costs,
146 const OperatingPoint& operating_point,
147 const std::shared_ptr<const MultiPlayerIntegrableSystem> dynamics) {
149 const PlayerIndex num_players = player_costs.size();
150 const size_t num_time_steps = operating_point.xs.size();
151 const Dimension xdim = operating_point.xs[0].size();
154 std::vector<QuadraticCostApproximation> quadraticization(
155 num_players, QuadraticCostApproximation(xdim));
158 for (
size_t kk = 0; kk < num_time_steps; kk++) {
159 const Time t = operating_point.t0 +
static_cast<Time
>(kk) * time::kTimeStep;
160 VectorXf x = operating_point.xs[kk];
161 std::vector<VectorXf> us = operating_point.us[kk];
164 if (dynamics.get() && dynamics->TreatAsLinear()) {
166 *
static_cast<const MultiPlayerFlatSystem*
>(dynamics.get());
169 x = dyn.FromLinearSystemState(x.eval());
170 us = dyn.LinearizingControls(x, std::vector<VectorXf>(us));
173 std::transform(player_costs.begin(), player_costs.end(),
174 quadraticization.begin(),
175 [&t, &x, &us](
const PlayerCost& cost) {
176 return cost.Quadraticize(t, x, us);
180 constexpr
float kErrorMargin = 1e-4;
181 for (
const auto& q : quadraticization) {
182 const auto eig_Q = Eigen::SelfAdjointEigenSolver<MatrixXf>(q.state.hess);
183 if (eig_Q.eigenvalues().minCoeff() < -kErrorMargin) {
191 for (
const auto& entry : q.control) {
193 Eigen::SelfAdjointEigenSolver<MatrixXf>(entry.second.hess);
194 if (eig_R.eigenvalues().minCoeff() < -kErrorMargin)
return false;
202 bool CheckSufficientLocalNashEquilibrium(
const Problem& problem) {
203 return CheckSufficientLocalNashEquilibrium(problem.PlayerCosts(),
204 problem.CurrentOperatingPoint(),