]>
gitweb.michael.orlitzky.com - dunshire.git/blob - test/randomgen.py
6513440152d3e693618267d6b839faabaf8417f4
2 Random thing generators used in the rest of the test suite.
4 from random
import randint
, uniform
7 from cvxopt
import matrix
8 from dunshire
.cones
import NonnegativeOrthant
, IceCream
9 from dunshire
.games
import SymmetricLinearGame
10 from dunshire
.matrices
import (append_col
, append_row
, identity
)
14 The maximum condition number of a randomly-generated game.
19 When generating random real numbers or integers, this is used as the
20 largest allowed magnitude. It keeps our condition numbers down and other
21 properties within reason.
26 Generate a random scalar in ``[-RANDOM_MAX, RANDOM_MAX]``.
36 >>> abs(random_scalar()) <= RANDOM_MAX
40 return uniform(-RANDOM_MAX
, RANDOM_MAX
)
43 def random_nn_scalar():
45 Generate a random nonnegative scalar in ``[0, RANDOM_MAX]``.
55 >>> 0 <= random_nn_scalar() <= RANDOM_MAX
59 return abs(random_scalar())
64 Generate a random natural number between ``1 and RANDOM_MAX``
75 >>> 1 <= random_natural() <= RANDOM_MAX
79 return randint(1, RANDOM_MAX
)
82 def random_matrix(dims
):
84 Generate a random square matrix.
90 The number of rows/columns you want in the returned matrix.
96 A new matrix whose entries are random floats chosen uniformly from
97 the interval [-RANDOM_MAX, RANDOM_MAX].
102 >>> A = random_matrix(3)
107 return matrix([[random_scalar()
108 for _
in range(dims
)]
109 for _
in range(dims
)])
112 def random_nonnegative_matrix(dims
):
114 Generate a random square matrix with nonnegative entries.
120 The number of rows/columns you want in the returned matrix.
126 A new matrix whose entries are chosen by :func:`random_nn_scalar`.
131 >>> A = random_nonnegative_matrix(3)
134 >>> all([entry >= 0 for entry in A])
138 return matrix([[random_nn_scalar()
139 for _
in range(dims
)]
140 for _
in range(dims
)])
143 def random_diagonal_matrix(dims
):
145 Generate a random square matrix with zero off-diagonal entries.
147 These matrices are Lyapunov-like on the nonnegative orthant, as is
154 The number of rows/columns you want in the returned matrix.
160 A new matrix whose diagonal entries are random floats chosen
161 using func:`random_scalar` and whose off-diagonal entries are
167 >>> A = random_diagonal_matrix(3)
170 >>> A[0,1] == A[0,2] == A[1,0] == A[2,0] == A[1,2] == A[2,1] == 0
174 return matrix([[random_scalar()*int(i
== j
)
175 for i
in range(dims
)]
176 for j
in range(dims
)])
179 def random_skew_symmetric_matrix(dims
):
181 Generate a random skew-symmetrix matrix.
187 The number of rows/columns you want in the returned matrix.
193 A new skew-matrix whose strictly above-diagonal entries are
194 random floats chosen with :func:`random_scalar`.
199 >>> A = random_skew_symmetric_matrix(3)
203 >>> from dunshire.options import ABS_TOL
204 >>> from dunshire.matrices import norm
205 >>> A = random_skew_symmetric_matrix(random_natural())
206 >>> norm(A + A.trans()) < ABS_TOL
210 strict_ut
= [[random_scalar()*int(i
< j
)
211 for i
in range(dims
)]
212 for j
in range(dims
)]
214 strict_ut
= matrix(strict_ut
, (dims
, dims
))
215 return strict_ut
- strict_ut
.trans()
218 def random_lyapunov_like_icecream(dims
):
220 Generate a random matrix Lyapunov-like on the ice-cream cone.
222 The form of these matrices is cited in Gowda and Tao
223 [GowdaTao]_. The scalar ``a`` and the vector ``b`` (using their
224 notation) are easy to generate. The submatrix ``D`` is a little
225 trickier, but it can be found noticing that :math:`C + C^{T} = 0`
226 for a skew-symmetric matrix :math:`C` implying that :math:`C + C^{T}
227 + \left(2a\right)I = \left(2a\right)I`. Thus we can stick an
228 :math:`aI` with each of :math:`C,C^{T}` and let those be our
235 The dimension of the ice-cream cone (not of the matrix you want!)
236 on which the returned matrix should be Lyapunov-like.
242 A new matrix, Lyapunov-like on the ice-cream cone in ``dims``
243 dimensions, whose free entries are random floats chosen uniformly
244 from the interval [-RANDOM_MAX, RANDOM_MAX].
249 .. [GowdaTao] M. S. Gowda and J. Tao. On the bilinearity rank of a
250 proper cone and Lyapunov-like transformations. Mathematical
251 Programming, 147:155-170, 2014.
256 >>> L = random_lyapunov_like_icecream(3)
260 >>> from dunshire.options import ABS_TOL
261 >>> from dunshire.matrices import inner_product
262 >>> x = matrix([1,1,0])
263 >>> s = matrix([1,-1,0])
264 >>> abs(inner_product(L*x, s)) < ABS_TOL
268 a
= matrix([random_scalar()], (1, 1))
269 b
= matrix([random_scalar() for _
in range(dims
-1)], (dims
-1, 1))
270 D
= random_skew_symmetric_matrix(dims
-1) + a
*identity(dims
-1)
271 row1
= append_col(a
, b
.trans())
272 row2
= append_col(b
, D
)
273 return append_row(row1
, row2
)
276 def random_orthant_game():
278 Generate the ``L``, ``K``, ``e1``, and ``e2`` parameters for a
279 random game over the nonnegative orthant, and return the
280 corresponding :class:`SymmetricLinearGame`.
282 We keep going until we generate a game with a condition number under
285 ambient_dim
= random_natural() + 1
286 K
= NonnegativeOrthant(ambient_dim
)
287 e1
= [random_nn_scalar() for _
in range(K
.dimension())]
288 e2
= [random_nn_scalar() for _
in range(K
.dimension())]
289 L
= random_matrix(K
.dimension())
290 G
= SymmetricLinearGame(L
, K
, e1
, e2
)
292 if G
.condition() <= MAX_COND
:
295 return random_orthant_game()
298 def random_icecream_game():
300 Generate the ``L``, ``K``, ``e1``, and ``e2`` parameters for a
301 random game over the ice-cream cone, and return the corresponding
302 :class:`SymmetricLinearGame`.
304 # Use a minimum dimension of two to avoid divide-by-zero in
305 # the fudge factor we make up later.
306 ambient_dim
= random_natural() + 1
307 K
= IceCream(ambient_dim
)
308 e1
= [1] # Set the "height" of e1 to one
309 e2
= [1] # And the same for e2
311 # If we choose the rest of the components of e1,e2 randomly
312 # between 0 and 1, then the largest the squared norm of the
313 # non-height part of e1,e2 could be is the 1*(dim(K) - 1). We
314 # need to make it less than one (the height of the cone) so
315 # that the whole thing is in the cone. The norm of the
316 # non-height part is sqrt(dim(K) - 1), and we can divide by
318 fudge_factor
= 1.0 / (2.0*sqrt(K
.dimension() - 1.0))
319 e1
+= [fudge_factor
*uniform(0, 1) for _
in range(K
.dimension() - 1)]
320 e2
+= [fudge_factor
*uniform(0, 1) for _
in range(K
.dimension() - 1)]
321 L
= random_matrix(K
.dimension())
322 G
= SymmetricLinearGame(L
, K
, e1
, e2
)
324 if G
.condition() <= MAX_COND
:
327 return random_icecream_game()
330 def random_ll_orthant_game():
332 Return a random Lyapunov game over some nonnegative orthant.
334 G
= random_orthant_game()
335 L
= random_diagonal_matrix(G
._K
.dimension())
337 # Replace the totally-random ``L`` with random Lyapunov-like one.
338 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
340 while G
.condition() > MAX_COND
:
341 # Try again until the condition number is satisfactory.
342 G
= random_orthant_game()
343 L
= random_diagonal_matrix(G
._K
.dimension())
344 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
349 def random_ll_icecream_game():
351 Return a random Lyapunov game over some ice-cream cone.
353 G
= random_icecream_game()
354 L
= random_lyapunov_like_icecream(G
._K
.dimension())
356 # Replace the totally-random ``L`` with random Lyapunov-like one.
357 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
359 while G
.condition() > MAX_COND
:
360 # Try again until the condition number is satisfactory.
361 G
= random_icecream_game()
362 L
= random_lyapunov_like_icecream(G
._K
.dimension())
363 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
368 def random_positive_orthant_game():
369 G
= random_orthant_game()
370 L
= random_nonnegative_matrix(G
._K
.dimension())
372 # Replace the totally-random ``L`` with the random nonnegative one.
373 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
375 while G
.condition() > MAX_COND
:
376 # Try again until the condition number is satisfactory.
377 G
= random_orthant_game()
378 L
= random_nonnegative_matrix(G
._K
.dimension())
379 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
384 def random_nn_scaling(G
):
385 alpha
= random_nn_scalar()
386 H
= SymmetricLinearGame(alpha
*G
._L.trans(), G
._K
, G
._e
1, G
._e
2)
388 while H
.condition() > MAX_COND
:
389 # Loop until the condition number of H doesn't suck.
390 alpha
= random_nn_scalar()
391 H
= SymmetricLinearGame(alpha
*G
._L.trans(), G
._K
, G
._e
1, G
._e
2)
395 def random_translation(G
):
396 alpha
= random_scalar()
397 tensor_prod
= G
._e
1 * G
._e
2.trans()
398 M
= G
._L + alpha
*tensor_prod
400 H
= SymmetricLinearGame(M
.trans(), G
._K
, G
._e
1, G
._e
2)
401 while H
.condition() > MAX_COND
:
402 # Loop until the condition number of H doesn't suck.
403 alpha
= random_scalar()
404 M
= G
._L + alpha
*tensor_prod
405 H
= SymmetricLinearGame(M
.trans(), G
._K
, G
._e
1, G
._e
2)