Skip to content

Commit 7264eff

Browse files
authored
Fix early termination of strategic form QRE on small probabilities. (#817)
An earlier refactoring introduced a round-trip whereby the equation system for logit QRE in the strategic form is expressed in log-probabilities, but when log-probs are used in the calculation it is via first exponentiating and taking logs. The effect of this is that very small probabilities (<10^{-300} or so) get truncated to zero, which then gives a NaN when converting back to log-probs. This corrects this regression to retain log probabilities as computed by the path-following.
1 parent 7797284 commit 7264eff

File tree

1 file changed

+20
-5
lines changed

1 file changed

+20
-5
lines changed

src/solvers/logit/nfglogit.cc

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ void PointToProfile(MixedStrategyProfile<double> &p_profile, const Vector<double
3838
}
3939
}
4040

41+
void PointToLogProfile(MixedStrategyProfile<double> &p_profile, const Vector<double> &p_point)
42+
{
43+
for (size_t i = 1; i < p_point.size(); i++) {
44+
p_profile[i] = p_point[i];
45+
}
46+
}
47+
4148
Vector<double> ProfileToPoint(const LogitQREMixedStrategyProfile &p_profile)
4249
{
4350
Vector<double> point(p_profile.size() + 1);
@@ -75,6 +82,7 @@ class Equation {
7582
virtual ~Equation() = default;
7683

7784
virtual double Value(const MixedStrategyProfile<double> &p_profile,
85+
const MixedStrategyProfile<double> &p_logProfile,
7886
const Vector<double> &p_strategyValues, double p_lambda) const = 0;
7987
virtual void Gradient(const MixedStrategyProfile<double> &p_profile,
8088
const Vector<double> &p_strategyValues,
@@ -105,13 +113,15 @@ class SumToOneEquation final : public Equation {
105113

106114
~SumToOneEquation() override = default;
107115
double Value(const MixedStrategyProfile<double> &p_profile,
116+
const MixedStrategyProfile<double> &p_logProfile,
108117
const Vector<double> &p_strategyValues, double p_lambda) const override;
109118
void Gradient(const MixedStrategyProfile<double> &p_profile,
110119
const Vector<double> &p_strategyValues, const Matrix<double> &p_strategyDerivs,
111120
double p_lambda, Vector<double> &p_gradient) const override;
112121
};
113122

114123
double SumToOneEquation::Value(const MixedStrategyProfile<double> &p_profile,
124+
const MixedStrategyProfile<double> &p_logProfile,
115125
const Vector<double> &p_strategyValues, double p_lambda) const
116126
{
117127
double value = -1.0;
@@ -157,17 +167,19 @@ class RatioEquation final : public Equation {
157167

158168
~RatioEquation() override = default;
159169
double Value(const MixedStrategyProfile<double> &p_profile,
170+
const MixedStrategyProfile<double> &p_logProfile,
160171
const Vector<double> &p_strategyValues, double p_lambda) const override;
161172
void Gradient(const MixedStrategyProfile<double> &p_profile,
162173
const Vector<double> &p_strategyValues, const Matrix<double> &p_strategyDerivs,
163174
double p_lambda, Vector<double> &p_gradient) const override;
164175
};
165176

166177
double RatioEquation::Value(const MixedStrategyProfile<double> &p_profile,
178+
const MixedStrategyProfile<double> &p_logProfile,
167179
const Vector<double> &p_strategyValues, double p_lambda) const
168180
{
169-
return (std::log(p_profile[m_strategyIndex]) - std::log(p_profile[m_refStrategyIndex]) -
170-
p_lambda * (p_strategyValues[m_strategyIndex] - p_strategyValues[m_refStrategyIndex]));
181+
return p_logProfile[m_strategyIndex] - p_logProfile[m_refStrategyIndex] -
182+
p_lambda * (p_strategyValues[m_strategyIndex] - p_strategyValues[m_refStrategyIndex]);
171183
}
172184

173185
void RatioEquation::Gradient(const MixedStrategyProfile<double> &p_profile,
@@ -209,13 +221,14 @@ class EquationSystem {
209221
private:
210222
std::vector<std::shared_ptr<Equation>> m_equations;
211223
const Game &m_game;
212-
mutable MixedStrategyProfile<double> m_profile;
224+
mutable MixedStrategyProfile<double> m_profile, m_logProfile;
213225
mutable Vector<double> m_strategyValues;
214226
mutable Matrix<double> m_strategyDerivs;
215227
};
216228

217229
EquationSystem::EquationSystem(const Game &p_game)
218230
: m_game(p_game), m_profile(p_game->NewMixedStrategyProfile(0.0)),
231+
m_logProfile(p_game->NewMixedStrategyProfile(0.0)),
219232
m_strategyValues(m_profile.MixedProfileLength()),
220233
m_strategyDerivs(m_profile.MixedProfileLength(), m_profile.MixedProfileLength())
221234
{
@@ -232,13 +245,15 @@ EquationSystem::EquationSystem(const Game &p_game)
232245
void EquationSystem::GetValue(const Vector<double> &p_point, Vector<double> &p_lhs) const
233246
{
234247
PointToProfile(m_profile, p_point);
248+
PointToLogProfile(m_logProfile, p_point);
235249
const double lambda = p_point.back();
236250
int col = 1;
237251
for (const auto &strategy : m_game->GetStrategies()) {
238252
m_strategyValues[col++] = m_profile.GetPayoff(strategy);
239253
}
240-
std::transform(m_equations.begin(), m_equations.end(), p_lhs.begin(),
241-
[this, lambda](auto e) { return e->Value(m_profile, m_strategyValues, lambda); });
254+
std::transform(m_equations.begin(), m_equations.end(), p_lhs.begin(), [this, lambda](auto e) {
255+
return e->Value(m_profile, m_logProfile, m_strategyValues, lambda);
256+
});
242257
}
243258

244259
void EquationSystem::GetJacobian(const Vector<double> &p_point, Matrix<double> &p_jac) const

0 commit comments

Comments
 (0)