+ opts = {'show_progress': False}
+ soln_dict = solvers.conelp(self._c(),
+ self._G(),
+ self._h(),
+ self.C().cvxopt_dims(),
+ self.A(),
+ self.b(),
+ primalstart=self.player1_start(),
+ dualstart=self.player2_start(),
+ options=opts)
+ except ValueError as error:
+ if str(error) == 'math domain error':
+ # Oops, CVXOPT tried to take the square root of a
+ # negative number. Report some details about the game
+ # rather than just the underlying CVXOPT crash.
+ printing.options['dformat'] = DEBUG_FLOAT_FORMAT
+ raise PoorScalingException(self)
+ else:
+ raise error
+
+ # The optimal strategies are named ``p`` and ``q`` in the
+ # background documentation, and we need to extract them from
+ # the CVXOPT ``x`` and ``z`` variables. The objective values
+ # :math:`nu` and :math:`omega` can also be found in the CVXOPT
+ # ``x`` and ``y`` variables; however, they're stored
+ # conveniently as separate entries in the solution dictionary.
+ p1_value = -soln_dict['primal objective']
+ p2_value = -soln_dict['dual objective']
+ p1_optimal = soln_dict['x'][1:]
+ p2_optimal = soln_dict['z'][self.dimension():]
+
+ # The "status" field contains "optimal" if everything went
+ # according to plan. Other possible values are "primal
+ # infeasible", "dual infeasible", "unknown", all of which mean
+ # we didn't get a solution.
+ #
+ # The "infeasible" ones are the worst, since they indicate
+ # that CVXOPT is convinced the problem is infeasible (and that
+ # cannot happen).
+ if soln_dict['status'] in ['primal infeasible', 'dual infeasible']:
+ printing.options['dformat'] = 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
+ # candidates are good enough, we take them. We do the same
+ # check (perhaps pointlessly so) for "optimal" results.
+ #
+ # First we check that the primal/dual objective values are
+ # 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) > self.tolerance_scale(soln)*ABS_TOL:
+ printing.options['dformat'] = DEBUG_FLOAT_FORMAT
+ raise GameUnsolvableException(self, soln_dict)
+
+ # And we also check that the points it gave us belong to the
+ # cone, just in case...
+ if (p1_optimal not in self._K) or (p2_optimal not in self._K):
+ printing.options['dformat'] = DEBUG_FLOAT_FORMAT
+ raise GameUnsolvableException(self, soln_dict)