]>
gitweb.michael.orlitzky.com - dunshire.git/blob - test/randomgen.py
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.matrices import norm
204 >>> A = random_skew_symmetric_matrix(random_natural())
205 >>> norm(A + A.trans()) < options.ABS_TOL
209 strict_ut
= [[random_scalar()*int(i
< j
)
210 for i
in range(dims
)]
211 for j
in range(dims
)]
213 strict_ut
= matrix(strict_ut
, (dims
, dims
))
214 return strict_ut
- strict_ut
.trans()
217 def random_lyapunov_like_icecream(dims
):
219 Generate a random matrix Lyapunov-like on the ice-cream cone.
221 The form of these matrices is cited in Gowda and Tao
222 [GowdaTao]_. The scalar ``a`` and the vector ``b`` (using their
223 notation) are easy to generate. The submatrix ``D`` is a little
224 trickier, but it can be found noticing that :math:`C + C^{T} = 0`
225 for a skew-symmetric matrix :math:`C` implying that :math:`C + C^{T}
226 + \left(2a\right)I = \left(2a\right)I`. Thus we can stick an
227 :math:`aI` with each of :math:`C,C^{T}` and let those be our
234 The dimension of the ice-cream cone (not of the matrix you want!)
235 on which the returned matrix should be Lyapunov-like.
241 A new matrix, Lyapunov-like on the ice-cream cone in ``dims``
242 dimensions, whose free entries are random floats chosen uniformly
243 from the interval [-RANDOM_MAX, RANDOM_MAX].
248 .. [GowdaTao] M. S. Gowda and J. Tao. On the bilinearity rank of a
249 proper cone and Lyapunov-like transformations. Mathematical
250 Programming, 147:155-170, 2014.
255 >>> L = random_lyapunov_like_icecream(3)
258 >>> x = matrix([1,1,0])
259 >>> s = matrix([1,-1,0])
260 >>> abs(inner_product(L*x, s)) < options.ABS_TOL
264 a
= matrix([random_scalar()], (1, 1))
265 b
= matrix([random_scalar() for _
in range(dims
-1)], (dims
-1, 1))
266 D
= random_skew_symmetric_matrix(dims
-1) + a
*identity(dims
-1)
267 row1
= append_col(a
, b
.trans())
268 row2
= append_col(b
, D
)
269 return append_row(row1
, row2
)
272 def random_orthant_game():
274 Generate the ``L``, ``K``, ``e1``, and ``e2`` parameters for a
275 random game over the nonnegative orthant, and return the
276 corresponding :class:`SymmetricLinearGame`.
278 We keep going until we generate a game with a condition number under
281 ambient_dim
= random_natural() + 1
282 K
= NonnegativeOrthant(ambient_dim
)
283 e1
= [random_nn_scalar() for _
in range(K
.dimension())]
284 e2
= [random_nn_scalar() for _
in range(K
.dimension())]
285 L
= random_matrix(K
.dimension())
286 G
= SymmetricLinearGame(L
, K
, e1
, e2
)
288 if G
.condition() <= MAX_COND
:
291 return random_orthant_game()
294 def random_icecream_game():
296 Generate the ``L``, ``K``, ``e1``, and ``e2`` parameters for a
297 random game over the ice-cream cone, and return the corresponding
298 :class:`SymmetricLinearGame`.
300 # Use a minimum dimension of two to avoid divide-by-zero in
301 # the fudge factor we make up later.
302 ambient_dim
= random_natural() + 1
303 K
= IceCream(ambient_dim
)
304 e1
= [1] # Set the "height" of e1 to one
305 e2
= [1] # And the same for e2
307 # If we choose the rest of the components of e1,e2 randomly
308 # between 0 and 1, then the largest the squared norm of the
309 # non-height part of e1,e2 could be is the 1*(dim(K) - 1). We
310 # need to make it less than one (the height of the cone) so
311 # that the whole thing is in the cone. The norm of the
312 # non-height part is sqrt(dim(K) - 1), and we can divide by
314 fudge_factor
= 1.0 / (2.0*sqrt(K
.dimension() - 1.0))
315 e1
+= [fudge_factor
*uniform(0, 1) for _
in range(K
.dimension() - 1)]
316 e2
+= [fudge_factor
*uniform(0, 1) for _
in range(K
.dimension() - 1)]
317 L
= random_matrix(K
.dimension())
318 G
= SymmetricLinearGame(L
, K
, e1
, e2
)
320 if G
.condition() <= MAX_COND
:
323 return random_icecream_game()
326 def random_ll_orthant_game():
328 Return a random Lyapunov game over some nonnegative orthant.
330 G
= random_orthant_game()
331 L
= random_diagonal_matrix(G
._K
.dimension())
333 # Replace the totally-random ``L`` with random Lyapunov-like one.
334 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
336 while G
.condition() > MAX_COND
:
337 # Try again until the condition number is satisfactory.
338 G
= random_orthant_game()
339 L
= random_diagonal_matrix(G
._K
.dimension())
340 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
345 def random_ll_icecream_game():
347 Return a random Lyapunov game over some ice-cream cone.
349 G
= random_icecream_game()
350 L
= random_lyapunov_like_icecream(G
._K
.dimension())
352 # Replace the totally-random ``L`` with random Lyapunov-like one.
353 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
355 while G
.condition() > MAX_COND
:
356 # Try again until the condition number is satisfactory.
357 G
= random_icecream_game()
358 L
= random_lyapunov_like_icecream(G
._K
.dimension())
359 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
364 def random_positive_orthant_game():
365 G
= random_orthant_game()
366 L
= random_nonnegative_matrix(G
._K
.dimension())
368 # Replace the totally-random ``L`` with the random nonnegative one.
369 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
371 while G
.condition() > MAX_COND
:
372 # Try again until the condition number is satisfactory.
373 G
= random_orthant_game()
374 L
= random_nonnegative_matrix(G
._K
.dimension())
375 G
= SymmetricLinearGame(L
, G
._K
, G
._e
1, G
._e
2)
380 def random_nn_scaling(G
):
381 alpha
= random_nn_scalar()
382 H
= SymmetricLinearGame(alpha
*G
._L.trans(), G
._K
, G
._e
1, G
._e
2)
384 while H
.condition() > MAX_COND
:
385 # Loop until the condition number of H doesn't suck.
386 alpha
= random_nn_scalar()
387 H
= SymmetricLinearGame(alpha
*G
._L.trans(), G
._K
, G
._e
1, G
._e
2)
391 def random_translation(G
):
392 alpha
= random_scalar()
393 tensor_prod
= G
._e
1 * G
._e
2.trans()
394 M
= G
._L + alpha
*tensor_prod
396 H
= SymmetricLinearGame(M
.trans(), G
._K
, G
._e
1, G
._e
2)
397 while H
.condition() > MAX_COND
:
398 # Loop until the condition number of H doesn't suck.
399 alpha
= random_scalar()
400 M
= G
._L + alpha
*tensor_prod
401 H
= SymmetricLinearGame(M
.trans(), G
._K
, G
._e
1, G
._e
2)