]>
gitweb.michael.orlitzky.com - dunshire.git/blob - randomgen.py
395408c6626ec4ee48dcb95e3b2e684daea88f4a
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.
32 A random real number between ``-RANDOM_MAX`` and ``RANDOM_MAX``,
38 >>> abs(random_scalar()) <= RANDOM_MAX
42 return uniform(-RANDOM_MAX
, RANDOM_MAX
)
45 def random_nn_scalar():
47 Generate a random nonnegative scalar.
53 A random nonnegative real number between zero and ``RANDOM_MAX``,
59 >>> 0 <= random_nn_scalar() <= RANDOM_MAX
63 return abs(random_scalar())
68 Generate a random natural number.
74 A random natural number between ``1`` and ``RANDOM_MAX`` inclusive.
79 >>> 1 <= random_natural() <= RANDOM_MAX
83 return randint(1, RANDOM_MAX
)
86 def random_matrix(row_count
, column_count
=None):
88 Generate a random matrix.
94 The number of rows you want in the returned matrix.
97 The number of columns you want in the returned matrix (default:
98 the same as ``row_count``).
104 A new matrix whose entries are random floats chosen uniformly from
105 the interval ``[-RANDOM_MAX, RANDOM_MAX]``.
110 >>> A = random_matrix(3)
114 >>> A = random_matrix(3,2)
119 if column_count
is None:
120 column_count
= row_count
122 entries
= [random_scalar() for _
in range(row_count
*column_count
)]
123 return matrix(entries
, (row_count
, column_count
))
126 def random_nonnegative_matrix(row_count
, column_count
=None):
128 Generate a random matrix with nonnegative entries.
134 The number of rows you want in the returned matrix.
137 The number of columns you want in the returned matrix (default:
138 the same as ``row_count``).
144 A new matrix whose entries are chosen by :func:`random_nn_scalar`.
149 >>> A = random_nonnegative_matrix(3)
152 >>> all([entry >= 0 for entry in A])
155 >>> A = random_nonnegative_matrix(3,2)
158 >>> all([entry >= 0 for entry in A])
162 if column_count
is None:
163 column_count
= row_count
165 entries
= [random_nn_scalar() for _
in range(row_count
*column_count
)]
166 return matrix(entries
, (row_count
, column_count
))
169 def random_diagonal_matrix(dims
):
171 Generate a random square matrix with zero off-diagonal entries.
173 These matrices are Lyapunov-like on the nonnegative orthant, as is
180 The number of rows/columns you want in the returned matrix.
186 A new matrix whose diagonal entries are random floats chosen
187 using func:`random_scalar` and whose off-diagonal entries are
193 >>> A = random_diagonal_matrix(3)
196 >>> A[0,1] == A[0,2] == A[1,0] == A[2,0] == A[1,2] == A[2,1] == 0
200 return matrix([[random_scalar()*int(i
== j
)
201 for i
in range(dims
)]
202 for j
in range(dims
)])
205 def random_skew_symmetric_matrix(dims
):
207 Generate a random skew-symmetrix matrix.
213 The number of rows/columns you want in the returned matrix.
219 A new skew-matrix whose strictly above-diagonal entries are
220 random floats chosen with :func:`random_scalar`.
225 >>> A = random_skew_symmetric_matrix(3)
229 >>> from dunshire.options import ABS_TOL
230 >>> from dunshire.matrices import norm
231 >>> A = random_skew_symmetric_matrix(random_natural())
232 >>> norm(A + A.trans()) < ABS_TOL
236 strict_ut
= [[random_scalar()*int(i
< j
)
237 for i
in range(dims
)]
238 for j
in range(dims
)]
240 strict_ut
= matrix(strict_ut
, (dims
, dims
))
241 return strict_ut
- strict_ut
.trans()
244 def random_lyapunov_like_icecream(dims
):
246 Generate a random matrix Lyapunov-like on the ice-cream cone.
248 The form of these matrices is cited in Gowda and Tao
249 [GowdaTao]_. The scalar ``a`` and the vector ``b`` (using their
250 notation) are easy to generate. The submatrix ``D`` is a little
251 trickier, but it can be found noticing that :math:`C + C^{T} = 0`
252 for a skew-symmetric matrix :math:`C` implying that :math:`C + C^{T}
253 + \left(2a\right)I = \left(2a\right)I`. Thus we can stick an
254 :math:`aI` with each of :math:`C,C^{T}` and let those be our
261 The dimension of the ice-cream cone (not of the matrix you want!)
262 on which the returned matrix should be Lyapunov-like.
268 A new matrix, Lyapunov-like on the ice-cream cone in ``dims``
269 dimensions, whose free entries are random floats chosen uniformly
270 from the interval ``[-RANDOM_MAX, RANDOM_MAX]``.
275 .. [GowdaTao] M. S. Gowda and J. Tao. On the bilinearity rank of a
276 proper cone and Lyapunov-like transformations. Mathematical
277 Programming, 147:155-170, 2014.
282 >>> L = random_lyapunov_like_icecream(3)
286 >>> from dunshire.options import ABS_TOL
287 >>> from dunshire.matrices import inner_product
288 >>> x = matrix([1,1,0])
289 >>> s = matrix([1,-1,0])
290 >>> abs(inner_product(L*x, s)) < ABS_TOL
294 a
= matrix([random_scalar()], (1, 1))
295 b
= matrix([random_scalar() for _
in range(dims
-1)], (dims
-1, 1))
296 D
= random_skew_symmetric_matrix(dims
-1) + a
*identity(dims
-1)
297 row1
= append_col(a
, b
.trans())
298 row2
= append_col(b
, D
)
299 return append_row(row1
, row2
)
302 def random_orthant_game():
304 Generate a random game over the nonnegative orthant.
306 We generate each of ``L``, ``K``, ``e1``, and ``e2`` randomly within
307 the constraints of the nonnegative orthant, and then construct a
308 game from them. The process is repeated until we generate a game with
309 a condition number under ``MAX_COND``.
315 A random game over some nonnegative orthant.
318 ambient_dim
= random_natural() + 1
319 K
= NonnegativeOrthant(ambient_dim
)
320 e1
= [random_nn_scalar() for _
in range(K
.dimension())]
321 e2
= [random_nn_scalar() for _
in range(K
.dimension())]
322 L
= random_matrix(K
.dimension())
323 G
= SymmetricLinearGame(L
, K
, e1
, e2
)
325 if G
.condition() <= MAX_COND
:
328 return random_orthant_game()
331 def random_icecream_game():
333 Generate a random game over the ice-cream cone.
335 We generate each of ``L``, ``K``, ``e1``, and ``e2`` randomly within
336 the constraints of the ice-cream cone, and then construct a game
337 from them. The process is repeated until we generate a game with a
338 condition number under ``MAX_COND``.
344 A random game over some ice-cream cone.
347 # Use a minimum dimension of two to avoid divide-by-zero in
348 # the fudge factor we make up later.
349 ambient_dim
= random_natural() + 1
350 K
= IceCream(ambient_dim
)
351 e1
= [1] # Set the "height" of e1 to one
352 e2
= [1] # And the same for e2
354 # If we choose the rest of the components of e1,e2 randomly
355 # between 0 and 1, then the largest the squared norm of the
356 # non-height part of e1,e2 could be is the 1*(dim(K) - 1). We
357 # need to make it less than one (the height of the cone) so
358 # that the whole thing is in the cone. The norm of the
359 # non-height part is sqrt(dim(K) - 1), and we can divide by
361 fudge_factor
= 1.0 / (2.0*sqrt(K
.dimension() - 1.0))
362 e1
+= [fudge_factor
*uniform(0, 1) for _
in range(K
.dimension() - 1)]
363 e2
+= [fudge_factor
*uniform(0, 1) for _
in range(K
.dimension() - 1)]
364 L
= random_matrix(K
.dimension())
365 G
= SymmetricLinearGame(L
, K
, e1
, e2
)
367 if G
.condition() <= MAX_COND
:
370 return random_icecream_game()
373 def random_ll_orthant_game():
375 Return a random Lyapunov game over some nonnegative orthant.
377 We first construct a :func:`random_orthant_game` and then modify it
378 to have a :func:`random_diagonal_matrix` as its operator. Such
379 things are Lyapunov-like on the nonnegative orthant. That process is
380 repeated until the condition number of the resulting game is within
387 A random game over some nonnegative orthant whose ``payoff`` method
388 is based on a Lyapunov-like ``L`` operator.
391 G
= random_orthant_game()
392 L
= random_diagonal_matrix(G
._K
.dimension())
394 # Replace the totally-random ``L`` with random Lyapunov-like one.
395 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
397 while G
.condition() > MAX_COND
:
398 # Try again until the condition number is satisfactory.
399 G
= random_orthant_game()
400 L
= random_diagonal_matrix(G
._K
.dimension())
401 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
406 def random_ll_icecream_game():
408 Return a random Lyapunov game over some ice-cream cone.
410 We first construct a :func:`random_icecream_game` and then modify it
411 to have a :func:`random_lyapunov_like_icecream` operator. That
412 process is repeated until the condition number of the resulting game
413 is within ``MAX_COND``.
419 A random game over some ice-cream cone whose ``payoff`` method
420 is based on a Lyapunov-like ``L`` operator.
423 G
= random_icecream_game()
424 L
= random_lyapunov_like_icecream(G
._K
.dimension())
426 # Replace the totally-random ``L`` with random Lyapunov-like one.
427 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
429 while G
.condition() > MAX_COND
:
430 # Try again until the condition number is satisfactory.
431 G
= random_icecream_game()
432 L
= random_lyapunov_like_icecream(G
._K
.dimension())
433 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
438 def random_positive_orthant_game():
440 Return a random game over the nonnegative orthant with a positive
443 We first construct a :func:`random_orthant_game` and then modify it
444 to have a :func:`random_nonnegative_matrix` as its operator. That
445 process is repeated until the condition number of the resulting game
446 is within ``MAX_COND``.
452 A random game over some nonnegative orthant whose ``payoff`` method
453 is based on a positive ``L`` operator.
457 G
= random_orthant_game()
458 L
= random_nonnegative_matrix(G
._K
.dimension())
460 # Replace the totally-random ``L`` with the random nonnegative one.
461 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
463 while G
.condition() > MAX_COND
:
464 # Try again until the condition number is satisfactory.
465 G
= random_orthant_game()
466 L
= random_nonnegative_matrix(G
._K
.dimension())
467 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
472 def random_nn_scaling(G
):
474 Scale the given game by a random nonnegative amount.
476 We re-attempt the scaling with a new random number until the
477 resulting scaled game has an acceptable condition number.
482 G : SymmetricLinearGame
483 The game that you would like to scale.
487 (float, SymmetricLinearGame)
488 A pair containing the both the scaling factor and the new scaled game.
491 alpha
= random_nn_scalar()
492 H
= SymmetricLinearGame(alpha
*G
._L.trans(), G
._K
, G
._e
1, G
._e
2)
494 while H
.condition() > MAX_COND
:
495 # Loop until the condition number of H doesn't suck.
496 alpha
= random_nn_scalar()
497 H
= SymmetricLinearGame(alpha
*G
._L.trans(), G
._K
, G
._e
1, G
._e
2)
502 def random_translation(G
):
504 Translate the given game by a random amount.
506 We re-attempt the translation with new random scalars until the
507 resulting translated game has an acceptable condition number.
512 G : SymmetricLinearGame
513 The game that you would like to translate.
517 (float, SymmetricLinearGame)
518 A pair containing the both the translation distance and the new
522 alpha
= random_scalar()
523 tensor_prod
= G
._e
1 * G
._e
2.trans()
524 M
= G
._L + alpha
*tensor_prod
526 H
= SymmetricLinearGame(M
.trans(), G
._K
, G
._e
1, G
._e
2)
527 while H
.condition() > MAX_COND
:
528 # Loop until the condition number of H doesn't suck.
529 alpha
= random_scalar()
530 M
= G
._L + alpha
*tensor_prod
531 H
= SymmetricLinearGame(M
.trans(), G
._K
, G
._e
1, G
._e
2)