printing.options['dformat'] = options.FLOAT_FORMAT
+
class Solution:
"""
A representation of the solution of a linear game. It should contain
if not self._e2 in K:
raise ValueError('the point e2 must lie in the interior of K')
+ # Initial value of cached method.
+ self._L_specnorm_value = None
def __str__(self):
"""
p = self.e2() / (norm(self.e2()) ** 2)
dist = self.K().ball_radius(self.e1())
- nu = - specnorm(self.L())/(dist*norm(self.e2()))
+ nu = - self._L_specnorm()/(dist*norm(self.e2()))
x = matrix([nu, p], (self.dimension() + 1, 1))
s = - self._G()*x
"""
q = self.e1() / (norm(self.e1()) ** 2)
dist = self.K().ball_radius(self.e2())
- omega = specnorm(self.L())/(dist*norm(self.e1()))
+ omega = self._L_specnorm()/(dist*norm(self.e1()))
y = matrix([omega])
z2 = q
z1 = y*self.e2() - self.L().trans()*z2
return {'y': y, 'z': z}
+ def _L_specnorm(self):
+ """
+ Compute the spectral norm of ``L`` and cache it.
+ """
+ if self._L_specnorm_value is None:
+ self._L_specnorm_value = specnorm(self.L())
+ return self._L_specnorm_value
+
+ def epsilon_scale(self, solution):
+ # Don't return anything smaller than 1... we can't go below
+ # out "minimum tolerance."
+ norm_p1_opt = norm(solution.player1_optimal())
+ norm_p2_opt = norm(solution.player2_optimal())
+ scale = self._L_specnorm()*(norm_p1_opt + norm_p2_opt)
+ return max(1, scale)
+
+
def solution(self):
"""
Solve this linear game and return a :class:`Solution`.
self.A(),
self.b(),
primalstart=self.player1_start(),
+ dualstart=self.player2_start(),
options=opts)
except ValueError as error:
if str(error) == 'math domain error':
printing.options['dformat'] = options.DEBUG_FLOAT_FORMAT
raise GameUnsolvableException(self, soln_dict)
+ # For the game value, we could use any of:
+ #
+ # * p1_value
+ # * p2_value
+ # * (p1_value + p2_value)/2
+ # * the game payoff
+ #
+ # We want the game value to be the payoff, however, so it
+ # makes the most sense to just use that, even if it means we
+ # can't test the fact that p1_value/p2_value are close to the
+ # payoff.
+ payoff = self.payoff(p1_optimal, p2_optimal)
+ soln = Solution(payoff, p1_optimal, p2_optimal)
+
# The "optimal" and "unknown" results, we actually treat the
# same. Even if CVXOPT bails out due to numerical difficulty,
# it will have some candidate points in mind. If those
# close enough (one could be low by ABS_TOL, the other high by
# it) because otherwise CVXOPT might return "unknown" and give
# us two points in the cone that are nowhere near optimal.
- if abs(p1_value - p2_value) > 2*options.ABS_TOL:
+ #
+ if abs(p1_value - p2_value) > self.epsilon_scale(soln)*options.ABS_TOL:
printing.options['dformat'] = options.DEBUG_FLOAT_FORMAT
raise GameUnsolvableException(self, soln_dict)
printing.options['dformat'] = options.DEBUG_FLOAT_FORMAT
raise GameUnsolvableException(self, soln_dict)
- # For the game value, we could use any of:
- #
- # * p1_value
- # * p2_value
- # * (p1_value + p2_value)/2
- # * the game payoff
- #
- # We want the game value to be the payoff, however, so it
- # makes the most sense to just use that, even if it means we
- # can't test the fact that p1_value/p2_value are close to the
- # payoff.
- payoff = self.payoff(p1_optimal, p2_optimal)
- return Solution(payoff, p1_optimal, p2_optimal)
+ return soln
def condition(self):