@@ -2132,6 +2132,169 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse
2132
2132
verbose(" done computing right kernel matrix over integers mod 2 for %s x%s matrix" % (self .nrows(), self .ncols()),level = 1 , t = tm)
2133
2133
return ' computed-pluq' , M
2134
2134
2135
+ def doubly_lexical_ordering (self , inplace = False ):
2136
+ r """
2137
+ Return a doubly lexical ordering of the matrix.
2138
+
2139
+ A doubly lexical ordering of a matrix is an ordering of the rows
2140
+ and of the columns of the matrix so that both the rows and the
2141
+ columns, as vectors, are lexically increasing. See [Anna1987 ]_.
2142
+ A lexical ordering of vectors is the standard dictionary ordering,
2143
+ except that vectors will be read from highest to lowest coordinate.
2144
+ Thus row vectors will be compared from right to left, and column
2145
+ vectors from bottom to top.
2146
+
2147
+ INPUT:
2148
+
2149
+ - ``inplace`` -- boolean ( default: ``False``) ; using ``inplace=True``
2150
+ will permute the rows and columns of the current matrix
2151
+ according to a doubly lexical ordering. This will modify the matrix.
2152
+
2153
+ OUTPUT:
2154
+
2155
+ Returns a pair ( ``row_ordering``, ``col_ordering``) . Each item
2156
+ is a ``PermutationGroupElement`` that represents a doubly lexical
2157
+ ordering of the rows or columns.
2158
+
2159
+ .. SEEALSO::
2160
+
2161
+ - :meth:`~sage. matrix. matrix2. Matrix. permutation_normal_form` --
2162
+ a similar matrix normal form
2163
+
2164
+ ALGORITHM:
2165
+
2166
+ The algorithm is adapted from section 3 of [Hoffman1985 ]_. The time
2167
+ complexity of this algorithm is `O( n \c dot m^ 2) ` for a `n \t imes m`
2168
+ matrix.
2169
+
2170
+ EXAMPLES:
2171
+
2172
+ sage: A = Matrix( GF( 2) , [
2173
+ ....: [0, 1 ],
2174
+ .... : [1, 0 ]])
2175
+ sage: r, c = A. doubly_lexical_ordering( )
2176
+ sage: r
2177
+ ( 1,2)
2178
+ sage: c
2179
+ ( )
2180
+ sage: A. permute_rows_and_columns( r, c) ; A
2181
+ [1 0 ]
2182
+ [0 1 ]
2183
+
2184
+ ::
2185
+
2186
+ sage: A = Matrix( GF( 2) , [
2187
+ ....: [0, 1 ],
2188
+ .... : [1, 0 ]])
2189
+ sage: r, c = A. doubly_lexical_ordering( inplace=True) ; A
2190
+ [1 0 ]
2191
+ [0 1 ]
2192
+
2193
+ TESTS:
2194
+
2195
+ sage: A = Matrix( GF( 2) , [
2196
+ ....: [1, 1, 0, 0, 0, 0, 0 ],
2197
+ .... : [1, 1, 0, 0, 0, 0, 0 ],
2198
+ .... : [1, 1, 0, 1, 0, 0, 0 ],
2199
+ .... : [0, 0, 1, 1, 0, 0, 0 ],
2200
+ .... : [0, 1, 1, 1, 1, 0, 0 ],
2201
+ .... : [0, 0, 0, 0, 0, 1, 1 ],
2202
+ .... : [0, 0, 0, 0, 0, 1, 1 ],
2203
+ .... : [0, 0, 0, 0, 1, 1, 1 ],
2204
+ .... : [0, 0, 0, 1, 1, 1, 0 ]])
2205
+ sage: r, c = A. doubly_lexical_ordering( )
2206
+ sage: B = A. with_permuted_rows_and_columns( r, c)
2207
+ sage: flag = True
2208
+ sage: for i in range( B. ncols( )) :
2209
+ .... : for j in range( i) :
2210
+ .... : for k in reversed( range( B. nrows( ))) :
2211
+ .... : if B[k ][j ] > B[k ][i ]:
2212
+ .... : flag = False
2213
+ .... : break
2214
+ .... : if B[k ][j ] < B[k ][i ]:
2215
+ .... : break
2216
+ .... :
2217
+ sage: for i in range( B. nrows( )) :
2218
+ .... : for j in range( i) :
2219
+ .... : for k in reversed( range( B. ncols( ))) :
2220
+ .... : if B[j ][k ] > B[i ][k ]:
2221
+ .... : flag = False
2222
+ .... : break
2223
+ .... : if B[j ][k ] < B[i ][k ]:
2224
+ .... : break
2225
+ .... :
2226
+ sage: flag
2227
+ True
2228
+ sage: r, c = A. doubly_lexical_ordering( inplace=True)
2229
+ sage: A == B
2230
+ True
2231
+
2232
+ """
2233
+
2234
+ partition_rows = [False for _ in range (self ._nrows - 1 )]
2235
+ partition_num = 1
2236
+ row_swapped = list (range (1 , self ._nrows + 1 ))
2237
+ col_swapped = list (range (1 , self ._ncols + 1 ))
2238
+
2239
+ cdef Matrix_mod2_dense A = self if inplace else self .__copy__()
2240
+
2241
+ for i in reversed (range (1 , A._ncols + 1 )):
2242
+
2243
+ # count 1 for each partition and column
2244
+ count1 = [[0 for _ in range (partition_num)] for _ in range (i)]
2245
+ for col in range (i):
2246
+ parition_i = 0
2247
+ for row in reversed (range (A._nrows)):
2248
+ count1[col][parition_i] += 1 if mzd_read_bit(A._entries, row, col) else 0
2249
+ if row > 0 and partition_rows[row - 1 ]:
2250
+ parition_i += 1
2251
+
2252
+ # calculate largest_col = col s.t. count1[col] is lexicographically largest (0 <= col < i)
2253
+ largest_col = 0
2254
+ largest_count1 = count1[0 ]
2255
+ for col in range (1 , i):
2256
+ if count1[col] >= largest_count1:
2257
+ largest_col = col
2258
+ largest_count1 = count1[col]
2259
+
2260
+ # We refine each partition of rows according to the value of A[:][largest_col].
2261
+ # and also move down rows that satisfy A[row][largest_col] = 1 in each partition.
2262
+ partition_start = 0
2263
+ for _ in range (partition_num):
2264
+ partition_end = partition_start
2265
+ while partition_end < A._nrows - 1 and not partition_rows[partition_end]:
2266
+ partition_end += 1
2267
+ row_start = partition_start
2268
+ row_end = partition_end
2269
+ while row_start < row_end:
2270
+ while row_start < row_end and not mzd_read_bit(A._entries, row_start, largest_col):
2271
+ row_start += 1
2272
+ while row_start < row_end and mzd_read_bit(A._entries, row_end, largest_col):
2273
+ row_end -= 1
2274
+ if row_start < row_end: # swap row
2275
+ A.swap_rows_c(row_start, row_end)
2276
+ row_swapped[row_start], row_swapped[row_end] = row_swapped[row_end], row_swapped[row_start]
2277
+ partition_start = partition_end + 1 # for next partition
2278
+
2279
+ for row in range (A._nrows - 1 ):
2280
+ if mzd_read_bit(A._entries, row, largest_col) != mzd_read_bit(A._entries, row + 1 , largest_col):
2281
+ if not partition_rows[row]:
2282
+ partition_rows[row] = True
2283
+ partition_num += 1
2284
+
2285
+ # swap column
2286
+ A.swap_columns_c(largest_col, i - 1 )
2287
+ col_swapped[largest_col], col_swapped[i - 1 ] = col_swapped[i - 1 ], col_swapped[largest_col]
2288
+
2289
+ from sage.groups.perm_gps.permgroup_named import SymmetricGroup
2290
+ from sage.groups.perm_gps.permgroup_element import make_permgroup_element_v2
2291
+ symmetric_group_nrows = SymmetricGroup(self ._nrows)
2292
+ symmetric_group_ncols = SymmetricGroup(self ._ncols)
2293
+ row_ordering = make_permgroup_element_v2(symmetric_group_nrows, row_swapped, symmetric_group_nrows.domain())
2294
+ col_ordering = make_permgroup_element_v2(symmetric_group_ncols, col_swapped, symmetric_group_ncols.domain())
2295
+
2296
+ return row_ordering, col_ordering
2297
+
2135
2298
# Used for hashing
2136
2299
cdef int i, k
2137
2300
cdef unsigned long parity_table[256 ]
0 commit comments