Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
- In the GUI, tab-traversal on tables (such as the strategic form payoff table) now works
as expected (TAB moves one cell to the right, wrapping if appropriate; SHIFT-TAB moves
to the left, wrapping if appropriate).
- Max regret is calculated correctly for strategy and behavior profiles on games with zero player
strategies or actions (is defined to be 0 trivially) (#904)

### Removed
- Built-in plotting of logit QRE for strategic games has been removed in the GUI (#809)
Expand Down
6 changes: 5 additions & 1 deletion src/games/behavmixed.cc
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,11 @@ template <class T> T MixedBehaviorProfile<T>::GetMaxRegret() const

template <class T> T MixedBehaviorProfile<T>::GetAgentMaxRegret() const
{
return maximize_function(m_support.GetGame()->GetInfosets(),
auto infosets = m_support.GetGame()->GetInfosets();
if (infosets.size() == 0) {
return T{0};
}
return maximize_function(infosets,
[this](const auto &infoset) -> T { return this->GetRegret(infoset); });
}

Expand Down
20 changes: 14 additions & 6 deletions src/games/game.cc
Original file line number Diff line number Diff line change
Expand Up @@ -410,29 +410,37 @@ template <class T> T MixedStrategyProfile<T>::GetRegret(const GameStrategy &p_st
ComputePayoffs();

auto player = p_strategy->GetPlayer();
if (player->m_strategies.size() == 1) {
return T{0};
}
T best_other_payoff = maximize_function(
filter_if(player->GetStrategies(), [&](const auto &s) { return s != p_strategy; }),
[this, &player](const auto &strategy) -> T {
return m_cache.m_strategyValues.at(player).at(strategy);
});
return std::max(best_other_payoff - m_cache.m_strategyValues.at(player).at(p_strategy),
static_cast<T>(0));
return std::max(best_other_payoff - m_cache.m_strategyValues.at(player).at(p_strategy), T{0});
}

template <class T> T MixedStrategyProfile<T>::GetRegret(const GamePlayer &p_player) const
{
CheckVersion();
ComputePayoffs();
auto br_payoff =
maximize_function(p_player->GetStrategies(), [this, p_player](const auto &strategy) -> T {
return m_cache.m_strategyValues.at(p_player).at(strategy);
});
auto strategies = p_player->GetStrategies();
if (strategies.size() == 0) {
return T{0};
}
auto br_payoff = maximize_function(strategies, [this, p_player](const auto &strategy) -> T {
return m_cache.m_strategyValues.at(p_player).at(strategy);
});
return br_payoff - m_cache.m_payoffs.at(p_player);
}

template <class T> T MixedStrategyProfile<T>::GetMaxRegret() const
{
CheckVersion();
if (GetGame()->GetPlayers().size() == 0) {
return T{0};
}
return maximize_function(GetGame()->GetPlayers(),
[this](const auto &player) -> T { return this->GetRegret(player); });
}
Expand Down
14 changes: 14 additions & 0 deletions tests/test_nash.py
Original file line number Diff line number Diff line change
Expand Up @@ -2259,6 +2259,20 @@ def test_nash_strategy_solver_w_start(test_case: EquilibriumTestCaseWithStart, s
marks=pytest.mark.nash_logit_behavior,
id="test_logit_behavior_01",
),
pytest.param(
EquilibriumTestCase(
factory=functools.partial(games.read_from_file,
"chance_root_5_moves_no_nonterm_player_nodes.efg"),
solver=gbt.nash.logit_solve,
expected=[
[[]] # Zero-dimension edge case (two players)
],
regret_tol=TOL_LARGE,
prob_tol=TOL_LARGE,
),
marks=pytest.mark.nash_logit_behavior,
id="test_logit_behavior_degenerate",
),
]


Expand Down
Loading