40
40
:meth:`~sage.matrix.special.random_rref_matrix`
41
41
:meth:`~sage.matrix.special.random_subspaces_matrix`
42
42
:meth:`~sage.matrix.special.random_unimodular_matrix`
43
+ :meth:`~sage.matrix.special.random_unitary_matrix`
43
44
:meth:`~sage.matrix.special.toeplitz`
44
45
:meth:`~sage.matrix.special.vandermonde`
45
46
:meth:`~sage.matrix.special.vector_on_axis_rotation_matrix`
@@ -241,6 +242,9 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation
241
242
242
243
- ``'unimodular'`` -- creates a matrix of determinant 1
243
244
245
+ - ``'unitary'`` -- creates a (square) unitary matrix over a
246
+ subfield of the complex numbers
247
+
244
248
- ``'diagonalizable'`` -- creates a diagonalizable matrix. if the
245
249
base ring is ``QQ`` creates a diagonalizable matrix whose eigenvectors,
246
250
if computed by hand, will have only integer entries. See the
@@ -303,7 +307,7 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation
303
307
sage: expected(100)
304
308
1/25250
305
309
sage: add_samples(ZZ, 5, 5)
306
- sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic):
310
+ sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): # long time
307
311
....: add_samples(ZZ, 5, 5)
308
312
309
313
The ``distribution`` keyword set to ``uniform`` will limit values
@@ -313,7 +317,7 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation
313
317
sage: total_count = 0
314
318
sage: dic = defaultdict(Integer)
315
319
sage: add_samples(ZZ, 5, 5, distribution='uniform')
316
- sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic):
320
+ sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): # long time
317
321
....: add_samples(ZZ, 5, 5, distribution='uniform')
318
322
319
323
The ``x`` and ``y`` keywords can be used to distribute entries uniformly.
@@ -324,14 +328,14 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation
324
328
sage: total_count = 0
325
329
sage: dic = defaultdict(Integer)
326
330
sage: add_samples(ZZ, 4, 8, x=70, y=100)
327
- sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic):
331
+ sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): # long time
328
332
....: add_samples(ZZ, 4, 8, x=70, y=100)
329
333
330
334
sage: expected = lambda n : 1/10 if n in range(-5, 5) else 0
331
335
sage: total_count = 0
332
336
sage: dic = defaultdict(Integer)
333
337
sage: add_samples(ZZ, 3, 7, x=-5, y=5)
334
- sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic):
338
+ sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): # long time
335
339
....: add_samples(ZZ, 3, 7, x=-5, y=5)
336
340
337
341
If only ``x`` is given, then it is used as the upper bound of a range
@@ -341,7 +345,7 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation
341
345
sage: total_count = 0
342
346
sage: dic = defaultdict(Integer)
343
347
sage: add_samples(ZZ, 5, 5, x=25)
344
- sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic):
348
+ sage: while not all(abs(dic[a]/total_count - expected(a)) < 0.001 for a in dic): # long time
345
349
....: add_samples(ZZ, 5, 5, x=25)
346
350
347
351
To control the number of nonzero entries, use the ``density`` keyword
@@ -665,6 +669,8 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation
665
669
return random_subspaces_matrix (parent , * args , ** kwds )
666
670
elif algorithm == 'unimodular' :
667
671
return random_unimodular_matrix (parent , * args , ** kwds )
672
+ elif algorithm == 'unitary' :
673
+ return random_unitary_matrix (parent , * args , ** kwds )
668
674
else :
669
675
raise ValueError ('random matrix algorithm "%s" is not recognized' % algorithm )
670
676
@@ -3135,6 +3141,171 @@ def random_unimodular_matrix(parent, upper_bound=None, max_tries=100):
3135
3141
return random_matrix (ring , size ,algorithm = 'echelonizable' ,rank = size , upper_bound = upper_bound , max_tries = max_tries )
3136
3142
3137
3143
3144
+ @matrix_method
3145
+ def random_unitary_matrix (parent ):
3146
+ r"""
3147
+ Generate a random (square) unitary matrix of a given size
3148
+ with entries in a subfield of the complex numbers.
3149
+
3150
+ INPUT:
3151
+
3152
+ - ``parent`` -- :class:`sage.matrix.matrix_space.MatrixSpace`; a
3153
+ square matrix space over a subfield of the complex numbers
3154
+ having characteristic zero.
3155
+
3156
+ OUTPUT:
3157
+
3158
+ A random unitary matrix in ``parent``. A :exc:`ValueError` is
3159
+ raised if ``parent`` is not an appropriate matrix space (is not
3160
+ square, or is not over a usable field).
3161
+
3162
+ ALGORITHM:
3163
+
3164
+ The method described by Liebeck and Osborne [LieOs1991]_ is used
3165
+ almost verbatim.
3166
+
3167
+ .. WARNING:
3168
+
3169
+ Inexact rings may violate your expectations; in particular,
3170
+ the rings ``RR`` and ``CC`` are accepted by this method but
3171
+ the resulting matrix will usually fail the ``is_unitary()``
3172
+ check due to numerical issues.
3173
+
3174
+ EXAMPLES:
3175
+
3176
+ This function is not in the global namespace and must be imported
3177
+ before being used::
3178
+
3179
+ sage: from sage.matrix.constructor import random_unitary_matrix
3180
+
3181
+ Matrices with rational entries::
3182
+
3183
+ sage: n = ZZ.random_element(10)
3184
+ sage: MS = MatrixSpace(QQ, n)
3185
+ sage: U = random_unitary_matrix(MS)
3186
+ sage: U.is_unitary() and U in MS
3187
+ True
3188
+
3189
+ Matrices over a quadraric field::
3190
+
3191
+ sage: n = ZZ.random_element(10)
3192
+ sage: K = QuadraticField(-1,'i')
3193
+ sage: MS = MatrixSpace(K, n)
3194
+ sage: U = random_unitary_matrix(MS)
3195
+ sage: U.is_unitary() and U in MS
3196
+ True
3197
+
3198
+ Matrices with entries in the algebraic real field (slow)::
3199
+
3200
+ sage: # long time
3201
+ sage: n = ZZ.random_element(4)
3202
+ sage: MS = MatrixSpace(AA, n)
3203
+ sage: U = random_unitary_matrix(MS)
3204
+ sage: U.is_unitary() and U in MS
3205
+ True
3206
+
3207
+ Matrices with entries in the algebraic field (slower yet)::
3208
+
3209
+ sage: # long time
3210
+ sage: n = ZZ.random_element(2)
3211
+ sage: MS = MatrixSpace(QQbar, n)
3212
+ sage: U = random_unitary_matrix(MS)
3213
+ sage: U.is_unitary() and U in MS
3214
+ True
3215
+
3216
+ Double-precision real/complex matrices can be constructed as
3217
+ well::
3218
+
3219
+ sage: n = ZZ.random_element(10)
3220
+ sage: MS = MatrixSpace(RDF, n)
3221
+ sage: U = random_unitary_matrix(MS)
3222
+ sage: U.is_unitary() and U in MS
3223
+ True
3224
+ sage: MS = MatrixSpace(CDF, n)
3225
+ sage: U = random_unitary_matrix(MS)
3226
+ sage: U.is_unitary() and U in MS
3227
+ True
3228
+
3229
+ TESTS:
3230
+
3231
+ We raise a :exc:`ValueError` if the supplied matrix space is not
3232
+ square::
3233
+
3234
+ sage: MS = MatrixSpace(QQ, 3, 4)
3235
+ sage: random_unitary_matrix(MS)
3236
+ Traceback (most recent call last):
3237
+ ...
3238
+ ValueError: parent must be square
3239
+
3240
+ Likewise if its base ring is not a field::
3241
+
3242
+ sage: MS = MatrixSpace(ZZ, 3)
3243
+ sage: random_unitary_matrix(MS)
3244
+ Traceback (most recent call last):
3245
+ ...
3246
+ ValueError: base ring of parent must be a field
3247
+
3248
+ Likewise if that field is not of characteristic zero::
3249
+
3250
+ sage: MS = MatrixSpace(GF(5), 3)
3251
+ sage: random_unitary_matrix(MS)
3252
+ Traceback (most recent call last):
3253
+ ...
3254
+ ValueError: base ring of parent must have characteristic zero
3255
+
3256
+ Likewise if that field is not a subfield of the complex numbers::
3257
+
3258
+ sage: R = Qp(7)
3259
+ sage: R.characteristic()
3260
+ 0
3261
+ sage: MS = MatrixSpace(R, 3)
3262
+ sage: random_unitary_matrix(MS)
3263
+ Traceback (most recent call last):
3264
+ ...
3265
+ ValueError: base ring of parent must be a subfield of the complex
3266
+ numbers
3267
+ """
3268
+ n = parent .nrows ()
3269
+ if n != parent .ncols ():
3270
+ raise ValueError ("parent must be square" )
3271
+
3272
+ F = parent .base_ring ()
3273
+ if not F .is_field ():
3274
+ raise ValueError ("base ring of parent must be a field" )
3275
+ elif not F .characteristic ().is_zero ():
3276
+ # This is probably covered by checking for subfields of
3277
+ # RLF/CLF, but it's mentioned explicitly in the paper and my
3278
+ # faith in the subfield check is not 100%, so we verify the
3279
+ # characteristic separately.
3280
+ raise ValueError ("base ring of parent must have characteristic zero" )
3281
+
3282
+ from sage .rings .real_lazy import RLF , CLF
3283
+ if not (RLF .has_coerce_map_from (F ) or
3284
+ F .has_coerce_map_from (RLF ) or
3285
+ CLF .has_coerce_map_from (F ) or
3286
+ F .has_coerce_map_from (CLF )):
3287
+ # The implementation of SR.random_element() currently just
3288
+ # returns a random integer coerced into SR, so there is no
3289
+ # benefit to allowing SR here when QQ is available.
3290
+ raise ValueError ("base ring of parent must be a subfield "
3291
+ "of the complex numbers" )
3292
+
3293
+ I = identity_matrix (F ,n )
3294
+ A = random_matrix (F ,n )
3295
+ S = A - A .conjugate_transpose ()
3296
+ U = (S - I ).inverse ()* (S + I )
3297
+
3298
+ # Scale the rows of U by plus/minus one with equal probability.
3299
+ # This generates the equivalence class of U according to the
3300
+ # Liebeck/Osborne paper.
3301
+ from random import random
3302
+ for i in range (n ):
3303
+ if random () < 0.5 :
3304
+ U .set_row_to_multiple_of_row (i , i , - 1 )
3305
+
3306
+ return U
3307
+
3308
+
3138
3309
@matrix_method
3139
3310
def random_diagonalizable_matrix (parent , eigenvalues = None , dimensions = None ):
3140
3311
"""
0 commit comments