Use private methods for the rest of the CVXOPT vectors/matrices.
We already had private methods for _zero(), _A(), and _G(). Now we
have them for the rest of the matrices and vectors as well. This
improves consistency, and maybe more importantly prevents people from
ever thinking it's safe to pass the same matrix to CVXOPT twice.
Add more docs for a few private methods and implement the do-over.
Some examples and return type documentation were added for the
_zero(), _A(), and _G() methods of SymmetricLinearGame. The _zero()
method was also uncached, since attempting to cache _A() and _G()
revealed that CVXOPT clobbers them. The zero vector appears safe, but
let's play it safe.
This also implements a two-stage retry for the solution of a game. Our
default tolerance is now 1e-6, but when we're trying to solve a game,
we try it with 1e7 first. If we can solve it with the stricter
tolerance, then great. If not, we use the default.
Take the condition number into account when evaluating test results.
This adds a ``modifier`` parameter to the assert_within_tol() method
used in the SymmetricLinearGame unit tests. The condition number of
the game is always passed in as the modifier, and that lets us allow a
larger tolerance for nasty matrices.
Our ABS_TOL is less than the CVXOPT default, and it looks like using
ours reduces the number of "unknown" results that we get. So let's try
that for now.
Change EPSILON to something more conservative in the test suite.
The old value of EPSILON was more lenient, but the new one can
actually be justified. The tests are currently pretty reliable, but
some tolerance > EPSILON failures are still occurring so this will
probably need to be massaged some more.
This replaces the "python test/" call with "./test/__main__py". It
may seem like a pointless change, but the former will always use
whatever "python" points to, and the latter will use the contents of
the shebang in "__main__.py". Distributions can fix the shebang on
that one file to ensure that the test suite is run with the proper
version of python.
Overhaul the tests to get more predictable behavior.
The most important change to the SymmetricLinearGame tests is that we
now check the condition numbers of the generated games. That prevents
most of the numerical trouble experienced before, and solves a few
open TODO items.
A RANDOM_MAX constant was introduced, and incorporated into the fudge
factor for the absolute tolerance used in tests. That contributes to
fixing the open TODO items as well.
The test code itself is pretty ugly now, but it can be fixed once they
all pass reliably (there's still a Lyapunov-like test failing).
Michael Orlitzky [Tue, 11 Oct 2016 15:24:30 +0000 (11:24 -0400)]
Attempt to recover from "unknown" solutions.
Sometimes CVXOPT was coming back with "unknown" solutions that looked
pretty close to real solutions. Now, instead of failing immediately,
we check to see if the primal/dual objectives are within ABS_TOL of
each other, and whether or not the optimal solutions lie in the
cone. If they all do, we take that as our solution and succeed rather
than raising an exception.
This required the loosening of what it means to be in the cone. Before
this commit, a point needed to be "far" inside the cone (past the
tolerance). Now it can lie outside of the cone (but still within the
tolerance). This weakens the checks that we perform on e1 and e2, but
gives us an easy way to check the optimal strategies.
Michael Orlitzky [Tue, 11 Oct 2016 14:33:56 +0000 (10:33 -0400)]
Get rid of the contains_strict() methods and compare against ABS_TOL.
Since we're comparing floating point numbers in the containment tests,
we should be doing it against a tolerance. But then, it becomes
pointless to distinguish between strict and non-strict containment.
So, this commit leaves only the __contains__() methods and checks
against options.ABS_TOL.
Michael Orlitzky [Mon, 10 Oct 2016 17:44:29 +0000 (13:44 -0400)]
Switch the docs style of cones.py and update cartesian product containment.
The docs for cones.py have all been switched (roughly) to use the
numpy style. In the process, I noticed that cartesian product
containment was a lot weirder than it needs to be. The "point" that
the __contains__() and contains_strict() methods take is now a tuple
corresponding to the factors of the cartesian product.