Developer API Documentation

This section contains API documentation that is of interest to developers, but not so much to end users. It documents our internal modules, exceptions, the test suite, and supporting code.

dunshire.errors module

Errors that can occur when solving a linear game.

exception GameUnsolvableException(game, solution_dict)[source]

Bases: Exception

An exception raised when a game cannot be solved.

Every linear game has a solution. If we can’t solve the conic program associated with a linear game, then something is wrong with either the model or the input, and this exception should be raised.

Parameters:
  • game (SymmetricLinearGame) – A copy of the game whose solution failed.

  • solution_dict (dict) – The solution dictionary returned from the failed cone program.

Examples

>>> from dunshire import *
>>> K = IceCream(2)
>>> L = [[1,2],[3,4]]
>>> e1 = [1, 0.1]
>>> e2 = [3, 0.1]
>>> G = SymmetricLinearGame(L,K,e1,e2)
>>> d = {'residual as dual infeasibility certificate': None,
...      'y': matrix([1,1]),
...      'dual slack': 8.779496368228267e-10,
...      'z': matrix([1,1,0,0]),
...      's': None,
...      'primal infeasibility': None,
...      'status': 'primal infeasible',
...      'dual infeasibility': None,
...      'relative gap': None,
...      'iterations': 5,
...      'primal slack': None,
...      'x': None,
...      'dual objective': 1.0,
...      'primal objective': None,
...      'gap': None,
...      'residual as primal infeasibility certificate':
...          3.986246886102996e-09}
>>> print(GameUnsolvableException(G,d))
Solution failed with result "primal infeasible."
The linear game (L, K, e1, e2) where
  L = [ 1  2]
      [ 3  4],
  K = Lorentz "ice cream" cone in the real 2-space,
  e1 = [1.0000000]
       [0.1000000],
  e2 = [3.0000000]
       [0.1000000]
CVXOPT returned:
  dual infeasibility: None
  dual objective: 1.0
  dual slack: 8.779496368228267e-10
  gap: None
  iterations: 5
  primal infeasibility: None
  primal objective: None
  primal slack: None
  relative gap: None
  residual as dual infeasibility certificate: None
  residual as primal infeasibility certificate: 3.986246886102996e-09
  s: None
  status: primal infeasible
  x: None
  y:
    [ 1]
    [ 1]
  z:
    [ 1]
    [ 1]
    [ 0]
    [ 0]
exception PoorScalingException(game)[source]

Bases: Exception

An exception raised when poor scaling leads to solution errors.

Under certain circumstances, a problem that should be solvable can trigger errors in CVXOPT. The end result is the following ValueError:

Traceback (most recent call last):
...
  return math.sqrt(x[offset] - a) * math.sqrt(x[offset] + a)
ValueError: math domain error

This happens when one of the arguments to math.sqrt() is negative, but the underlying cause is elusive. We’re blaming it on “poor scaling,” whatever that means.

Similar issues have been discussed a few times on the CVXOPT mailing list; for example,

  1. https://groups.google.com/forum/#!msg/cvxopt/TeQGdc2b4Xc/j5_mQME_rvUJ

  2. https://groups.google.com/forum/#!topic/cvxopt/HZrRfaoM0pk

  3. https://groups.google.com/forum/#!topic/cvxopt/riFSxB31zU4

Parameters:

game (SymmetricLinearGame) – A copy of the game whose solution failed.

Examples

>>> from dunshire import *
>>> K = IceCream(2)
>>> L = [[1,2],[3,4]]
>>> e1 = [1, 0.1]
>>> e2 = [3, 0.1]
>>> G = SymmetricLinearGame(L,K,e1,e2)
>>> print(PoorScalingException(G))
Solution failed due to poor scaling.
The linear game (L, K, e1, e2) where
  L = [ 1  2]
      [ 3  4],
  K = Lorentz "ice cream" cone in the real 2-space,
  e1 = [1.0000000]
       [0.1000000],
  e2 = [3.0000000]
       [0.1000000]

dunshire.matrices module

Utility functions for working with CVXOPT matrices (instances of the class:cvxopt.base.matrix class).

append_col(left, right)[source]

Append two matrices side-by-side.

Parameters:
  • left (matrix) – The “original” matrix, the one that will wind up on the left.

  • right (matrix) – The matrix to be appended on the right of left.

Returns:

A new matrix consisting of right appended to the right of left.

Return type:

matrix

Examples

>>> A = matrix([1,2,3,4], (2,2))
>>> B = matrix([5,6,7,8,9,10], (2,3))
>>> print(A)
[ 1  3]
[ 2  4]

>>> print(B)
[  5   7   9]
[  6   8  10]

>>> print(append_col(A,B))
[  1   3   5   7   9]
[  2   4   6   8  10]
append_row(top, bottom)[source]

Append two matrices top-to-bottom.

Parameters:
  • top (matrix) – The “original” matrix, the one that will wind up on top.

  • bottom (matrix) – The matrix to be appended below top.

Returns:

A new matrix consisting of bottom appended below top.

Return type:

matrix

Examples

>>> A = matrix([1,2,3,4], (2,2))
>>> B = matrix([5,6,7,8,9,10], (3,2))
>>> print(A)
[ 1  3]
[ 2  4]

>>> print(B)
[  5   8]
[  6   9]
[  7  10]

>>> print(append_row(A,B))
[  1   3]
[  2   4]
[  5   8]
[  6   9]
[  7  10]
condition_number(mat)[source]

Return the condition number of the given matrix.

The condition number of a matrix quantifies how hard it is to do numerical computation with that matrix. It is usually defined as the ratio of the norm of the matrix to the norm of its inverse, and therefore depends on the norm used. One way to compute the condition number with respect to the 2-norm is as the ratio of the matrix’s largest and smallest singular values. Since we have easy access to those singular values, that is the algorithm we use.

The larger the condition number is, the worse the matrix is.

Parameters:

mat (matrix) – The matrix whose condition number you want.

Returns:

The nonnegative condition number of mat.

Return type:

float

Examples

>>> condition_number(identity(3))
1.0
>>> A = matrix([[2,1],[1,2]])
>>> abs(condition_number(A) - 3.0) < options.ABS_TOL
True
>>> A = matrix([[2,1j],[-1j,2]])
>>> abs(condition_number(A) - 3.0) < options.ABS_TOL
True
eigenvalues(symmat)[source]

Return the eigenvalues of the given symmetric real matrix.

On the surface, this appears redundant to the eigenvalues_re() function. However, if we know in advance that our input is symmetric, a better algorithm can be used.

Parameters:

symmat (matrix) – The real symmetric matrix whose eigenvalues you want.

Returns:

A list of the eigenvalues (in no particular order) of symmat.

Return type:

list of float

Raises:

TypeError – If the input matrix is not symmetric.

Examples

>>> A = matrix([[2,1],[1,2]], tc='d')
>>> eigenvalues(A)
[1.0, 3.0]

If the input matrix is not symmetric, it may not have real eigenvalues, and we don’t know what to do:

>>> A = matrix([[1,2],[3,4]])
>>> eigenvalues(A)
Traceback (most recent call last):
...
TypeError: input must be a symmetric real matrix
eigenvalues_re(anymat)[source]

Return the real parts of the eigenvalues of the given square matrix.

Parameters:

anymat (matrix) – The square matrix whose eigenvalues you want.

Returns:

A list of the real parts (in no particular order) of the eigenvalues of anymat.

Return type:

list of float

Raises:

TypeError – If the input matrix is not square.

Examples

This is symmetric and has two real eigenvalues:

>>> A = matrix([[2,1],[1,2]], tc='d')
>>> sorted(eigenvalues_re(A))
[1.0, 3.0]

But this rotation matrix has eigenvalues i and -i, both of whose real parts are zero:

>>> A = matrix([[0,-1],[1,0]])
>>> eigenvalues_re(A)
[0.0, 0.0]

If the input matrix is not square, it doesn’t have eigenvalues:

>>> A = matrix([[1,2],[3,4],[5,6]])
>>> eigenvalues_re(A)
Traceback (most recent call last):
...
TypeError: input matrix must be square
identity(domain_dim, typecode='i')[source]

Create an identity matrix of the given dimensions.

Parameters:
  • domain_dim (int) – The dimension of the vector space on which the identity will act.

  • typecode ({'i', 'd', 'z'}, optional) – The type code for the returned matrix, defaults to ‘i’ for integers. Can also be ‘d’ for real double, or ‘z’ for complex double.

Returns:

A domain_dim-by-domain_dim dense integer identity matrix.

Return type:

matrix

Raises:

ValueError – If you ask for the identity on zero or fewer dimensions.

Examples

>>> print(identity(3))
[ 1  0  0]
[ 0  1  0]
[ 0  0  1]
inner_product(vec1, vec2)[source]

Compute the Euclidean inner product of two vectors.

Parameters:
  • vec1 (matrix) – The first vector, whose inner product with vec2 you want.

  • vec2 (matrix) – The second vector, whose inner product with vec1 you want.

Returns:

The inner product of vec1 and vec2.

Return type:

float

Raises:

TypeError – If the lengths of vec1 and vec2 differ.

Examples

>>> x = [1,2,3]
>>> y = [3,4,1]
>>> inner_product(x,y)
14
>>> x = matrix([1,1,1])
>>> y = matrix([2,3,4], (1,3))
>>> inner_product(x,y)
9
>>> x = [1,2,3]
>>> y = [1,1]
>>> inner_product(x,y)
Traceback (most recent call last):
...
TypeError: the lengths of vec1 and vec2 must match
norm(matrix_or_vector)[source]

Return the Frobenius norm of a matrix or vector.

When the input is a vector, its matrix-Frobenius norm is the same thing as its vector-Euclidean norm.

Parameters:

matrix_or_vector (matrix) – The matrix or vector whose norm you want.

Returns:

The norm of matrix_or_vector.

Return type:

float

Examples

>>> v = matrix([1,1])
>>> norm(v)
1.414...
>>> A = matrix([1,1,1,1], (2,2))
>>> norm(A)
2.0...
specnorm(mat)[source]

Return the spectral norm of a matrix.

The spectral norm of a matrix is its largest singular value, and it corresponds to the operator norm induced by the vector Euclidean norm.

Parameters:
  • mat (matrix) – The matrix whose spectral norm you want.

  • Examples

    >>> specnorm(identity(3))
    1.0
    
    >>> specnorm(5*identity(4))
    5.0
    

vec(mat)[source]

Create a long vector in column-major order from mat.

Parameters:

mat (matrix) – Any sort of real matrix that you want written as a long vector.

Returns:

An len(mat)-by-1 long column vector containign the entries of mat in column major order.

Return type:

matrix

Examples

>>> A = matrix([[1,2],[3,4]])
>>> print(A)
[ 1  3]
[ 2  4]
>>> print(vec(A))
[ 1]
[ 2]
[ 3]
[ 4]

Note that if mat is a vector, this function is a no-op:

>>> v = matrix([1,2,3,4], (4,1))
>>> print(v)
[ 1]
[ 2]
[ 3]
[ 4]

>>> print(vec(v))
[ 1]
[ 2]
[ 3]
[ 4]

dunshire.options module

A place to collect the various options that “can be passed to the underlying engine.” Just kidding, they’re constants and you can’t change them. But this makes the user interface real simple.

ABS_TOL = 1e-06

The absolute tolerance used in all “are these numbers equal” and “is this number less than (or equal to) that other number” tests. The CVXOPT default is 1e-7, but loosening that a little reduces the number of “unknown” solutions that we get during random testing. Whether or not it improves the solubility of real problems is a question for the philosophers.

DEBUG_FLOAT_FORMAT = '%.20f'

The float output format to use when something goes wrong. If we need to reproduce a random test case, for example, then we need all of the digits of the things involved. If we try to recreate the problem using only, say, the first seven digits of each number, then the resulting game might not reproduce the failure.

FLOAT_FORMAT = '%.7f'

The default output format for floating point numbers.

test module

The whole test suite.

This module compiles the doctests and unittests from the rest of the codebase into one big TestSuite() and the runs it. It also provides a function build_suite() that merely builds the suite; the result can be used by setuptools.

build_suite(doctests=True)[source]

Build our test suite, separately from running it.

Parameters:

doctests (bool) – Do you want to build the doctests, too? During random testing, the answer may be “no.”

run_suite(suite, verbosity)[source]

Run all of the unit and doctests for the dunshire and test packages.

test.matrices_test

Unit tests for the functions in the dunshire.matrices module.

class AppendColTest(methodName='runTest')[source]

Bases: TestCase

Tests for the append_col() function.

test_new_dimensions()[source]

If we append one matrix to another side-by-side, then the result should have the same number of rows as the two original matrices. However, the number of their columns should add up to the number of columns in the new combined matrix.

class AppendRowTest(methodName='runTest')[source]

Bases: TestCase

Tests for the dunshire.matrices.append_row() function.

test_new_dimensions()[source]

If we append one matrix to another top-to-bottom, then the result should have the same number of columns as the two original matrices. However, the number of their rows should add up to the number of rows in the the new combined matrix.

class ConditionNumberTest(methodName='runTest')[source]

Bases: TestCase

Tests for the dunshire.matrices.condition_number() function.

test_condition_number_ge_one()[source]

From the way that it is defined, the condition number should always be greater than or equal to one.

class EigenvaluesRealPartTest(methodName='runTest')[source]

Bases: TestCase

Tests for the dunshire.matrices.eigenvalues_re() function.

test_eigenvalues_re_input_not_clobbered()[source]

The eigenvalue functions provided by CVXOPT/LAPACK like to overwrite the matrices that you pass into them as arguments. This test makes sure that our dunshire.matrices.eigenvalues_re() function does not do the same.

We use a deepcopy here in case the copy used in the dunshire.matrices.eigenvalues_re() function is insufficient. If copy didn’t work and this test used it too, then this test would pass when it shouldn’t.

test_eigenvalues_re_of_identity()[source]

All eigenvalues of the identity matrix should be one.

class EigenvaluesTest(methodName='runTest')[source]

Bases: TestCase

Tests for the dunshire.matrices.eigenvalues() function.

test_eigenvalues_input_not_clobbered()[source]

The eigenvalue functions provided by CVXOPT/LAPACK like to overwrite the matrices that you pass into them as arguments. This test makes sure that our eigenvalues() function does not do the same.

We use a deepcopy here in case the copy used in the eigenvalues() function is insufficient. If copy didn’t work and this test used it too, then this test would pass when it shouldn’t.

test_eigenvalues_of_identity()[source]

All eigenvalues of the identity matrix should be one.

test_eigenvalues_of_symmat_are_real()[source]

A real symmetric matrix has real eigenvalues, so if we start with a symmetric matrix, then the two functions dunshire.matrices.eigenvalues() and dunshire.matrices.eigenvalues_re() should agree on it.

class InnerProductTest(methodName='runTest')[source]

Bases: TestCase

Tests for the dunshire.matrices.inner_product() function.

test_inner_product_with_self_is_norm_squared()[source]

Ensure that the func:dunshire.matrices.inner_product and dunshire.matrices.norm() functions are compatible by checking that the square of the norm of a vector is its inner product with itself.

class NormTest(methodName='runTest')[source]

Bases: TestCase

Tests for the dunshire.matrices.norm() function.

test_norm_is_nonnegative()[source]

Test one of the properties of a norm, that it is nonnegative.

test.randomgen

Random thing generators used in the rest of the test suite.

MAX_COND = 125

The maximum condition number of a randomly-generated game. When the condition number of the games gets too high, we start to see PoorScalingException being thrown. There’s no science to choosing the upper bound – it got lowered until those exceptions stopped popping up. It’s at 125 because 129 doesn’t work.

RANDOM_MAX = 10

When generating random real numbers or integers, this is used as the largest allowed magnitude. It keeps our condition numbers down and other properties within reason.

random_diagonal_matrix(dims)[source]

Generate a random square matrix with zero off-diagonal entries.

These matrices are Lyapunov-like on the nonnegative orthant, as is fairly easy to see.

Parameters:

dims (int) – The number of rows/columns you want in the returned matrix.

Returns:

A new matrix whose diagonal entries are random floats chosen using random_scalar() and whose off-diagonal entries are zero.

Return type:

matrix

Examples

>>> A = random_diagonal_matrix(3)
>>> A.size
(3, 3)
>>> A[0,1] == A[0,2] == A[1,0] == A[2,0] == A[1,2] == A[2,1] == 0
True
random_game()[source]

Return a random game.

One of the functions,

is chosen at random and used to generate a random game.

Returns:

A random game.

Return type:

SymmetricLinearGame

Examples

>>> random_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_icecream_game()[source]

Generate a random game over the ice-cream cone.

We generate each of L, K, e1, and e2 randomly within the constraints of the ice-cream cone, and then construct a game from them. The process is repeated until we generate a game with a condition number under MAX_COND.

Returns:

A random game over some ice-cream cone.

Return type:

SymmetricLinearGame

Examples

>>> random_icecream_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_ll_game()[source]

Return a random Lyapunov-like game.

One of the functions,

is chosen at random and used to generate a random game.

Returns:

A random Lyapunov-like game.

Return type:

SymmetricLinearGame

Examples

>>> random_ll_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_ll_icecream_game()[source]

Return a random Lyapunov game over some ice-cream cone.

We first construct a random_icecream_game() and then modify it to have a random_lyapunov_like_icecream() operator. That process is repeated until the condition number of the resulting game is within MAX_COND.

Returns:

A random game over some ice-cream cone whose dunshire.games.SymmetricLinearGame.payoff() method is based on a Lyapunov-like dunshire.games.SymmetricLinearGame.L() operator.

Return type:

SymmetricLinearGame

Examples

>>> random_ll_icecream_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_ll_orthant_game()[source]

Return a random Lyapunov game over some nonnegative orthant.

We first construct a random_orthant_game() and then modify it to have a random_diagonal_matrix() as its operator. Such things are Lyapunov-like on the nonnegative orthant. That process is repeated until the condition number of the resulting game is within MAX_COND.

Returns:

A random game over some nonnegative orthant whose dunshire.games.SymmetricLinearGame.payoff() method is based on a Lyapunov-like dunshire.games.SymmetricLinearGame.L() operator.

Return type:

SymmetricLinearGame

Examples

>>> random_ll_orthant_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_lyapunov_like_icecream(dims)[source]

Generate a random matrix Lyapunov-like on the ice-cream cone.

The form of these matrices is cited in Gowda and Tao [GowdaTao]. The scalar a and the vector b (using their notation) are easy to generate. The submatrix D is a little trickier, but it can be found noticing that \(C + C^{T} = 0\) for a skew-symmetric matrix \(C\) implying that \(C + C^{T} + \left(2a\right)I = \left(2a\right)I\). Thus we can stick an \(aI\) with each of \(C,C^{T}\) and let those be our \(D,D^{T}\).

Parameters:

dims (int) – The dimension of the ice-cream cone (not of the matrix you want!) on which the returned matrix should be Lyapunov-like.

Returns:

A new matrix, Lyapunov-like on the ice-cream cone in dims dimensions, whose free entries are random floats chosen uniformly between negative and positive RANDOM_MAX.

Return type:

matrix

References

[GowdaTao]

M. S. Gowda and J. Tao. On the bilinearity rank of a proper cone and Lyapunov-like transformations. Mathematical Programming, 147:155-170, 2014.

Examples

>>> L = random_lyapunov_like_icecream(3)
>>> L.size
(3, 3)
>>> from dunshire.options import ABS_TOL
>>> from dunshire.matrices import inner_product
>>> x = matrix([1,1,0])
>>> s = matrix([1,-1,0])
>>> abs(inner_product(L*x, s)) < ABS_TOL
True
random_matrix(row_count, column_count=None)[source]

Generate a random matrix.

Parameters:
  • row_count (int) – The number of rows you want in the returned matrix.

  • column_count (int) – The number of columns you want in the returned matrix (default: the same as row_count).

Returns:

A new matrix whose entries are random floats chosen uniformly between negative and positive RANDOM_MAX.

Return type:

matrix

Examples

>>> A = random_matrix(3)
>>> A.size
(3, 3)
>>> A = random_matrix(3,2)
>>> A.size
(3, 2)
random_natural()[source]

Generate a random natural number.

Returns:

A random natural number between 1 and RANDOM_MAX, inclusive.

Return type:

int

Examples

>>> 1 <= random_natural() <= RANDOM_MAX
True
random_nn_scalar()[source]

Generate a random nonnegative scalar.

Returns:

A random nonnegative real number between zero and RANDOM_MAX, inclusive.

Return type:

float

Examples

>>> 0 <= random_nn_scalar() <= RANDOM_MAX
True
random_nn_scaling(G)[source]

Scale the given game by a random nonnegative amount.

We re-attempt the scaling with a new random number until the resulting scaled game has an acceptable condition number.

Parameters:

G (SymmetricLinearGame) – The game that you would like to scale.

Returns:

A pair containing the both the scaling factor and the new scaled game.

Return type:

(float, SymmetricLinearGame)

Examples

>>> from dunshire.matrices import norm
>>> from dunshire.options import ABS_TOL
>>> G = random_orthant_game()
>>> (alpha, H) = random_nn_scaling(G)
>>> alpha >= 0
True
>>> G.K() == H.K()
True
>>> norm(G.e1() - H.e1()) < ABS_TOL
True
>>> norm(G.e2() - H.e2()) < ABS_TOL
True
random_nonnegative_matrix(row_count, column_count=None)[source]

Generate a random matrix with nonnegative entries.

Parameters:
  • row_count (int) – The number of rows you want in the returned matrix.

  • column_count (int) – The number of columns you want in the returned matrix (default: the same as row_count).

Returns:

A new matrix whose entries are chosen by random_nn_scalar().

Return type:

matrix

Examples

>>> A = random_nonnegative_matrix(3)
>>> A.size
(3, 3)
>>> all([entry >= 0 for entry in A])
True
>>> A = random_nonnegative_matrix(3,2)
>>> A.size
(3, 2)
>>> all([entry >= 0 for entry in A])
True
random_orthant_game()[source]

Generate a random game over the nonnegative orthant.

We generate each of L, K, e1, and e2 randomly within the constraints of the nonnegative orthant, and then construct a game from them. The process is repeated until we generate a game with a condition number under MAX_COND.

Returns:

A random game over some nonnegative orthant.

Return type:

SymmetricLinearGame

Examples

>>> random_orthant_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_positive_orthant_game()[source]

Return a random game over the nonnegative orthant with a positive operator.

We first construct a random_orthant_game() and then modify it to have a random_nonnegative_matrix() as its operator. That process is repeated until the condition number of the resulting game is within MAX_COND.

Returns:

A random game over some nonnegative orthant whose dunshire.games.SymmetricLinearGame.payoff() method is based on a positive dunshire.games.SymmetricLinearGame.L() operator.

Return type:

SymmetricLinearGame

Examples

>>> random_positive_orthant_game()
<dunshire.games.SymmetricLinearGame object at 0x...>
random_scalar()[source]

Generate a random scalar.

Returns:

A random real number between negative and positive RANDOM_MAX, inclusive.

Return type:

float

Examples

>>> abs(random_scalar()) <= RANDOM_MAX
True
random_skew_symmetric_matrix(dims)[source]

Generate a random skew-symmetrix matrix.

Parameters:

dims (int) – The number of rows/columns you want in the returned matrix.

Returns:

A new skew-matrix whose strictly above-diagonal entries are random floats chosen with random_scalar().

Return type:

matrix

Examples

>>> A = random_skew_symmetric_matrix(3)
>>> A.size
(3, 3)
>>> from dunshire.options import ABS_TOL
>>> from dunshire.matrices import norm
>>> A = random_skew_symmetric_matrix(random_natural())
>>> norm(A + A.trans()) < ABS_TOL
True
random_translation(G)[source]

Translate the given game by a random amount.

We re-attempt the translation with new random scalars until the resulting translated game has an acceptable condition number.

Parameters:

G (SymmetricLinearGame) – The game that you would like to translate.

Returns:

A pair containing the both the translation distance and the new scaled game.

Return type:

(float, SymmetricLinearGame)

Examples

>>> from dunshire.matrices import norm
>>> from dunshire.options import ABS_TOL
>>> G = random_orthant_game()
>>> (alpha, H) = random_translation(G)
>>> G.K() == H.K()
True
>>> norm(G.e1() - H.e1()) < ABS_TOL
True
>>> norm(G.e2() - H.e2()) < ABS_TOL
True

test.symmetric_linear_game_test module

Unit tests for the SymmetricLinearGame class.

class SymmetricLinearGameTest(methodName='runTest')[source]

Bases: TestCase

Tests for the SymmetricLinearGame and Solution classes.

assert_lyapunov_works(G)[source]

Check that Lyapunov games act the way we expect.

assert_opposite_game_works(G)[source]

Check the value of the “opposite” game that gives rise to a value that is the negation of the original game. Comes from some corollary.

assert_orthogonality(G)[source]

Two orthogonality relations hold at an optimal solution, and we check them here.

assert_player1_start_valid(G)[source]

Ensure that player one’s starting point satisfies both the equality and cone inequality in the CVXOPT primal problem.

assert_player2_start_valid(G)[source]

Check that player two’s starting point satisfies both the cone inequality in the CVXOPT dual problem.

assert_scaling_works(G)[source]

Test that scaling L by a nonnegative number scales the value of the game by the same number.

assert_solutions_dont_change(G)[source]

Solve G twice and check that the solutions agree.

assert_translation_works(G)[source]

Check that translating L by alpha*(e1*e2.trans()) increases the value of the associated game by alpha.

assert_within_tol(first, second, modifier=1)[source]

Test that first and second are equal within a multiple of our default tolerances.

Parameters:
  • first (float) – The first number to compare.

  • second (float) – The second number to compare.

  • modifier (float) – A scaling factor (default: 1) applied to the default tolerance for this comparison. If you have a poorly- conditioned matrix, for example, you may want to set this greater than one.

test_condition_lower_bound()[source]

Ensure that the condition number of a game is greater than or equal to one.

It should be safe to compare these floats directly: we compute the condition number as the ratio of one nonnegative real number to a smaller nonnegative real number.

test_lyapunov_icecream()[source]

Test that a Lyapunov game on the ice-cream cone works.

test_lyapunov_orthant()[source]

Test that a Lyapunov game on the nonnegative orthant works.

test_opposite_game_icecream()[source]

Like test_opposite_game_orthant(), except over the ice-cream cone.

test_opposite_game_orthant()[source]

Test the value of the “opposite” game over the nonnegative orthant.

test_orthogonality_icecream()[source]

Check the orthgonality relationships that hold for a solution over the ice-cream cone.

test_orthogonality_orthant()[source]

Check the orthgonality relationships that hold for a solution over the nonnegative orthant.

test_player1_start_valid_icecream()[source]

Ensure that player one’s starting point is feasible over the ice-cream cone.

test_player1_start_valid_orthant()[source]

Ensure that player one’s starting point is feasible over the nonnegative orthant.

test_player2_start_valid_icecream()[source]

Ensure that player two’s starting point is feasible over the ice-cream cone.

test_player2_start_valid_orthant()[source]

Ensure that player two’s starting point is feasible over the nonnegative orthant.

test_positive_operator_value()[source]

Test that a positive operator on the nonnegative orthant gives rise to a a game with a nonnegative value.

This test theoretically applies to the ice-cream cone as well, but we don’t know how to make positive operators on that cone.

test_scaling_icecream()[source]

The same test as test_nonnegative_scaling_orthant(), except over the ice cream cone.

test_scaling_orthant()[source]

Test that scaling L by a nonnegative number scales the value of the game by the same number over the nonnegative orthant.

test_solutions_dont_change_icecream()[source]

If we solve the same game twice over the ice-cream cone, then we should get the same solution both times. The solution to a game is not unique, but the process we use is (as far as we know) deterministic.

test_solutions_dont_change_orthant()[source]

If we solve the same game twice over the nonnegative orthant, then we should get the same solution both times. The solution to a game is not unique, but the process we use is (as far as we know) deterministic.

test_translation_icecream()[source]

The same as test_translation_orthant(), except over the ice cream cone.

test_translation_orthant()[source]

Test that translation works over the nonnegative orthant.