| | 1035 | Random matrices with predictable subspaces. The ``algorithm='subspaces'`` |
| | 1036 | keyword, along with an optional nullity (``nullity``) will return |
| | 1037 | a matrix whose natural basis vectors for its four fundamental subspaces, if computed as |
| | 1038 | described in the documentation of the :func:`~sage.matrix.constructor.random_subspaces_matrix` |
| | 1039 | contain only integer entries. If ``nullity``, is not set, the |
| | 1040 | nullity of the matrix will be generated randomly. :: |
| | 1041 | |
| | 1042 | sage: B=random_matrix(QQ, 5, 6, algorithm='subspaces', nullity=2); B #random |
| | 1043 | sage: B_expanded=B.augment(identity_matrix(5)).rref() |
| | 1044 | sage: all([x in ZZ for x in B_expanded.list()]) |
| | 1045 | True |
| | 1046 | sage: C=B_expanded.submatrix(0,0,B.nrows()-B.nullity(),B.ncols()) |
| | 1047 | sage: L=B_expanded.submatrix(B.nrows()-B.nullity(),B.ncols()) |
| | 1048 | sage: B.right_kernel()==C.right_kernel() |
| | 1049 | True |
| | 1050 | sage: B.row_space()==C.row_space() |
| | 1051 | True |
| | 1052 | sage: B.column_space()==L.right_kernel() |
| | 1053 | True |
| | 1054 | sage: B.left_kernel()==L.row_space() |
| | 1055 | True |
| | 1056 | |
| | 1057 | For more, see the documentation of the :func:`~sage.matrix.constructor.random_subspaces_matrix` |
| | 1058 | function. In the notebook or at the Sage command-line, first execute the following to make |
| | 1059 | this further documentation available:: |
| | 1060 | |
| | 1061 | from sage.matrix.constructor import random_subspaces_matrix |
| | 1062 | |
| | 1063 | Random unimodular matrices. The ``algorithm='unimodular'`` |
| | 1064 | keyword, along with an optional entry size control (``upper_bound``) |
| | 1065 | will return a matrix of determinant 1. When the base ring is ``ZZ`` |
| | 1066 | or ``QQ`` the result has integer entries, whose magnitudes |
| | 1067 | can be limited by the value of ``upper_bound``. :: |
| | 1068 | |
| | 1069 | sage: C=random_matrix(QQ, 5, algorithm='unimodular', upper_bound=70); C # random |
| | 1070 | sage: det(C) |
| | 1071 | 1 |
| | 1072 | sage: C.base_ring() |
| | 1073 | Rational Field |
| | 1074 | sage: (C.nrows(), C.ncols()) |
| | 1075 | (5, 5) |
| | 1076 | sage: all([abs(x)<70 for x in C.list()]) |
| | 1077 | True |
| | 1078 | |
| | 1079 | For more, see the documentation of the :func:`~sage.matrix.constructor.random_unimodular_matrix` |
| | 1080 | function. In the notebook or at the Sage command-line, first execute the following to make |
| | 1081 | this further documentation available:: |
| | 1082 | |
| | 1083 | from sage.matrix.constructor import random_unimodular_matrix |
| | 1084 | |
| | 1875 | |
| | 1876 | def random_subspaces_matrix(parent, nullity=None): |
| | 1877 | """ |
| | 1878 | Create a matrix of the designated size and nullity whose right and |
| | 1879 | left null spaces, column space, and row space have desirable |
| | 1880 | properties that simplify the subspaces. |
| | 1881 | |
| | 1882 | INPUT: |
| | 1883 | |
| | 1884 | - ``parent`` - A matrix space specifying the base ring, dimensions, and |
| | 1885 | representation (dense/sparse) for the result. The base ring must be exact. |
| | 1886 | |
| | 1887 | - ``nullity`` - The desired nullity of the return matrix. (Default: None) |
| | 1888 | |
| | 1889 | OUTPUT: |
| | 1890 | |
| | 1891 | A matrix whose natrual basis vectors for its four subspaces, when |
| | 1892 | computed, have reasonably sized, integral valued, entries. |
| | 1893 | |
| | 1894 | .. note:: |
| | 1895 | |
| | 1896 | It is easiest to use this function via a call to the |
| | 1897 | :func:`~sage.matrix.constructor.random_matrix` |
| | 1898 | function with the ``algorithm='subspaces'`` keyword. We provide |
| | 1899 | one example accessing this function directly, while the remainder will |
| | 1900 | use this more general function. |
| | 1901 | |
| | 1902 | EXAMPLES: |
| | 1903 | |
| | 1904 | A 6x8 matrix with designated nullity of 3. The four subspaces are |
| | 1905 | determined using one simple routine in which we augment the |
| | 1906 | original matrix with the equal row dimension identity matrix. The |
| | 1907 | resulting matrix is then put in reduced row-echelon form and the |
| | 1908 | subspaces can then be determined by analyzing subdivisions of this |
| | 1909 | matrix. See the four subspaces routine in [BEEZER]_ for more. :: |
| | 1910 | |
| | 1911 | sage: from sage.matrix.constructor import random_subspaces_matrix |
| | 1912 | sage: matrix_space = sage.matrix.matrix_space.MatrixSpace(QQ, 6, 8) |
| | 1913 | sage: B=random_subspaces_matrix(matrix_space, nullity=3); B # random |
| | 1914 | [ 113 339 -46 218 -243 641 -269 -306] |
| | 1915 | [ -33 -99 13 -63 69 -185 77 90] |
| | 1916 | [ 35 105 -14 67 -74 197 -82 -95] |
| | 1917 | [ -18 -54 7 -34 37 -100 41 49] |
| | 1918 | [ -26 -78 10 -49 53 -144 59 71] |
| | 1919 | [ -8 -24 3 -15 16 -44 18 22] |
| | 1920 | sage: B.nullity() |
| | 1921 | 3 |
| | 1922 | sage: B_expanded=B.augment(identity_matrix(6)).rref() |
| | 1923 | sage: B_expanded # random |
| | 1924 | [ 1 3 0 0 1 1 3 -2 0 0 -3 0 -9 16] |
| | 1925 | [ 0 0 1 0 3 -2 -1 -3 0 0 2 0 11 -27] |
| | 1926 | [ 0 0 0 1 -1 2 -3 -1 0 0 2 0 7 -14] |
| | 1927 | [ 0 0 0 0 0 0 0 0 1 0 -5 0 -3 2] |
| | 1928 | [ 0 0 0 0 0 0 0 0 0 1 1 0 1 -3] |
| | 1929 | [ 0 0 0 0 0 0 0 0 0 0 0 1 -1 1] |
| | 1930 | sage: B_expanded.subdivide(B.nrows()-B.nullity(),B.ncols());B_expanded # random |
| | 1931 | [ 1 3 0 0 1 1 3 -2| 0 0 -3 0 -9 16] |
| | 1932 | [ 0 0 1 0 3 -2 -1 -3| 0 0 2 0 11 -27] |
| | 1933 | [ 0 0 0 1 -1 2 -3 -1| 0 0 2 0 7 -14] |
| | 1934 | [-------------------------------+-----------------------] |
| | 1935 | [ 0 0 0 0 0 0 0 0| 1 0 -5 0 -3 2] |
| | 1936 | [ 0 0 0 0 0 0 0 0| 0 1 1 0 1 -3] |
| | 1937 | [ 0 0 0 0 0 0 0 0| 0 0 0 1 -1 1] |
| | 1938 | sage: C=B_expanded.subdivision(0,0) |
| | 1939 | sage: C # random |
| | 1940 | [ 1 3 0 0 1 1 3 -2] |
| | 1941 | [ 0 0 1 0 3 -2 -1 -3] |
| | 1942 | [ 0 0 0 1 -1 2 -3 -1] |
| | 1943 | sage: L=B_expanded.subdivision(1,1) |
| | 1944 | sage: L # random |
| | 1945 | [ 1 0 -5 0 -3 2] |
| | 1946 | [ 0 1 1 0 1 -3] |
| | 1947 | [ 0 0 0 1 -1 1] |
| | 1948 | sage: B.right_kernel()==C.right_kernel() |
| | 1949 | True |
| | 1950 | sage: B.row_space()==C.row_space() |
| | 1951 | True |
| | 1952 | sage: B.column_space()==L.right_kernel() |
| | 1953 | True |
| | 1954 | sage: B.left_kernel()==L.row_space() |
| | 1955 | True |
| | 1956 | |
| | 1957 | A matrix to show that the null space of the L matrix is the column space of the starting matrix. :: |
| | 1958 | |
| | 1959 | sage: A=random_matrix(QQ, 5, 7, algorithm='subspaces', nullity=None); A # random |
| | 1960 | [-31 12 -9 -27 21 2 -15] |
| | 1961 | [105 -24 6 103 -30 -34 79] |
| | 1962 | [ 29 -9 5 26 -14 -5 17] |
| | 1963 | [233 -55 16 228 -71 -73 173] |
| | 1964 | [-42 10 -3 -41 13 13 -31] |
| | 1965 | sage: A.nullity() # random |
| | 1966 | 1 |
| | 1967 | sage: A_expanded=A.augment(identity_matrix(5)).rref() |
| | 1968 | sage: A_expanded # random |
| | 1969 | [ 1 0 0 0 0 1 0 0 3 7 25 151] |
| | 1970 | [ 0 1 0 0 1 2 1 0 5 21 84 493] |
| | 1971 | [ 0 0 1 0 -1 2 0 0 2 13 53 308] |
| | 1972 | [ 0 0 0 1 0 -1 1 0 -2 -3 -9 -57] |
| | 1973 | [ 0 0 0 0 0 0 0 1 -3 1 1 -2] |
| | 1974 | sage: C=A_expanded.submatrix(0,0,A.nrows()-A.nullity(),A.ncols()) |
| | 1975 | sage: L=A_expanded.submatrix(A.nrows()-A.nullity(),A.ncols()) |
| | 1976 | sage: A.right_kernel()==C.right_kernel() |
| | 1977 | True |
| | 1978 | sage: A.row_space()==C.row_space() |
| | 1979 | True |
| | 1980 | sage: A.column_space()==L.right_kernel() |
| | 1981 | True |
| | 1982 | sage: A.left_kernel()==L.row_space() |
| | 1983 | True |
| | 1984 | |
| | 1985 | TESTS: |
| | 1986 | |
| | 1987 | The designated rank of the L matrix cannot be greater than the number of desired rows. :: |
| | 1988 | |
| | 1989 | sage: random_matrix(QQ, 19, 20, algorithm='subspaces', nullity=21) |
| | 1990 | Traceback (most recent call last): |
| | 1991 | ... |
| | 1992 | ValueError: nullity cannot exceed the number of rows or columns. |
| | 1993 | |
| | 1994 | REFERENCES: |
| | 1995 | |
| | 1996 | .. [BEEZER] `A First Course in Linear Algebra <http://linear.ups.edu/>`_. |
| | 1997 | Robert A. Beezer, accessed 15 July 2010. |
| | 1998 | |
| | 1999 | AUTHOR: |
| | 2000 | |
| | 2001 | Billy Wonderly (2010-07) |
| | 2002 | """ |
| | 2003 | |
| | 2004 | import sage.gsl.probability_distribution as pd |
| | 2005 | |
| | 2006 | ring = parent.base_ring() |
| | 2007 | rows = parent.nrows() |
| | 2008 | columns = parent.nrows() |
| | 2009 | |
| | 2010 | if nullity>rows: |
| | 2011 | raise ValueError("nullity cannot exceed the number of rows or columns.") |
| | 2012 | # If nullity is not designated, generate using probability distribution skewing to smaller numbers, always at least 1. |
| | 2013 | if nullity==None: |
| | 2014 | nullity_generator=pd.RealDistribution("beta",[1.4,5.5]) |
| | 2015 | nullity=int(nullity_generator.get_random_element()*(rows-1)+1) |
| | 2016 | else: |
| | 2017 | nullity=nullity |
| | 2018 | rank=rows-nullity |
| | 2019 | B=random_matrix(ring, rows, columns, algorithm='echelon_form', num_pivots=rank) |
| | 2020 | # Create a nonsingular matrix whose columns will be used to stack a matrix over the L matrix, forming a nonsingular matrix. |
| | 2021 | K_nonzero_columns=random_matrix(ring, rank, rank, algorithm='echelonizable', rank=rank) |
| | 2022 | K=matrix(QQ,rank,rows) |
| | 2023 | L=random_matrix(ring, nullity, rows, algorithm='echelon_form', num_pivots=nullity) |
| | 2024 | for column in range(len(L.nonpivots())): |
| | 2025 | for entry in range(rank): |
| | 2026 | K[entry,L.nonpivots()[column]]=K_nonzero_columns[entry,column] |
| | 2027 | J=K.stack(L) |
| | 2028 | J_inverse=J.inverse() |
| | 2029 | N=B.augment(J) |
| | 2030 | M=J_inverse*N # Entries remain whole values as J is unimodular. |
| | 2031 | return_matrix=M.submatrix(0,0,M.nrows(),M.ncols()-M.nrows()) |
| | 2032 | return return_matrix |
| | 2033 | |
| | 2034 | def random_unimodular_matrix(parent, upper_bound=None): |
| | 2035 | """ |
| | 2036 | Generate a random unimodular (determinant 1) matrix of a desired size over a desired ring. |
| | 2037 | |
| | 2038 | INPUT: |
| | 2039 | |
| | 2040 | - ``parent`` - A matrix space specifying the base ring, dimensions |
| | 2041 | and representation (dense/sparse) for the result. The base ring |
| | 2042 | must be exact. |
| | 2043 | |
| | 2044 | - ``upper_bound`` - For large matrices over QQ or ZZ, |
| | 2045 | ``upper_bound`` is the largest value matrix entries can achieve. |
| | 2046 | |
| | 2047 | OUTPUT: |
| | 2048 | |
| | 2049 | An invertible matrix with the desired properties and determinant 1. |
| | 2050 | |
| | 2051 | .. note:: |
| | 2052 | |
| | 2053 | It is easiest to use this function via a call to the |
| | 2054 | :func:`~sage.matrix.constructor.random_matrix` |
| | 2055 | function with the ``algorithm='unimodular'`` keyword. We provide |
| | 2056 | one example accessing this function directly, while the remainder will |
| | 2057 | use this more general function. |
| | 2058 | |
| | 2059 | EXAMPLES: |
| | 2060 | |
| | 2061 | A matrix size 5 over QQ. :: |
| | 2062 | |
| | 2063 | sage: from sage.matrix.constructor import random_unimodular_matrix |
| | 2064 | sage: matrix_space = sage.matrix.matrix_space.MatrixSpace(QQ, 5) |
| | 2065 | sage: A=random_unimodular_matrix(matrix_space); A # random |
| | 2066 | [ -8 31 85 148 -419] |
| | 2067 | [ 2 -9 -25 -45 127] |
| | 2068 | [ -1 10 30 65 -176] |
| | 2069 | [ -3 12 33 58 -164] |
| | 2070 | [ 5 -21 -59 -109 304] |
| | 2071 | sage: det(A) |
| | 2072 | 1 |
| | 2073 | |
| | 2074 | A matrix size 6 with entries no larger than 50. :: |
| | 2075 | |
| | 2076 | sage: B=random_matrix(ZZ, 7, algorithm='unimodular', upper_bound=50);B # random |
| | 2077 | [ -1 0 3 1 -2 2 9] |
| | 2078 | [ 1 2 -5 0 14 19 -49] |
| | 2079 | [ -3 -2 12 5 -6 -4 24] |
| | 2080 | [ 1 2 -9 -3 3 4 -7] |
| | 2081 | [ -2 -1 7 2 -8 -5 31] |
| | 2082 | [ 2 2 -6 -3 8 16 -32] |
| | 2083 | [ 1 2 -9 -2 5 6 -12] |
| | 2084 | sage: det(B) |
| | 2085 | 1 |
| | 2086 | |
| | 2087 | A matrix over the number Field in `y` with defining polynomial `y^2-2y-2`. :: |
| | 2088 | |
| | 2089 | sage: y = var('y') |
| | 2090 | sage: K=NumberField(y^2-2*y-2,'y') |
| | 2091 | sage: C=random_matrix(K, 3, algorithm='unimodular');C # random |
| | 2092 | [ 2*y - 33 681*y - 787 31*y - 37] |
| | 2093 | [ y + 6 -155*y + 83 -7*y + 4] |
| | 2094 | [ -y 24*y + 51 y + 3] |
| | 2095 | sage: det(C) |
| | 2096 | 1 |
| | 2097 | |
| | 2098 | TESTS: |
| | 2099 | |
| | 2100 | Unimodular matrices are square. :: |
| | 2101 | |
| | 2102 | sage: random_matrix(QQ, 5, 6, algorithm='unimodular') |
| | 2103 | Traceback (most recent call last): |
| | 2104 | ... |
| | 2105 | TypeError: a unimodular matrix must be square. |
| | 2106 | |
| | 2107 | Only matrices over ZZ and QQ can have size control. :: |
| | 2108 | |
| | 2109 | sage: F.<a>=GF(5^7) |
| | 2110 | sage: random_matrix(F, 5, algorithm='unimodular', upper_bound=20) |
| | 2111 | Traceback (most recent call last): |
| | 2112 | ... |
| | 2113 | TypeError: only matrices over ZZ or QQ can have size control. |
| | 2114 | |
| | 2115 | AUTHOR: |
| | 2116 | |
| | 2117 | Billy Wonderly (2010-07) |
| | 2118 | """ |
| | 2119 | |
| | 2120 | ring=parent.base_ring() |
| | 2121 | size=parent.nrows() |
| | 2122 | if parent.nrows()!=parent.ncols(): |
| | 2123 | raise TypeError("a unimodular matrix must be square.") |
| | 2124 | if upper_bound!=None and (ring!=ZZ and ring!=QQ): |
| | 2125 | raise TypeError("only matrices over ZZ or QQ can have size control.") |
| | 2126 | if upper_bound==None: |
| | 2127 | # random_echelonizable_matrix() always returns a determinant one matrix if given full rank. |
| | 2128 | return random_matrix(ring, size, algorithm='echelonizable', rank=size) |
| | 2129 | elif upper_bound!=None and (ring==ZZ or ring==QQ): |
| | 2130 | return random_matrix(ring, size,algorithm='echelonizable',rank=size, upper_bound=upper_bound) |
| | 2131 | |