@@ -162,21 +162,135 @@ swap_strategy is an optional argument that determines how the swapping of rows a
162
162
bareiss_colswap (the default) swaps the columns and rows normally.
163
163
bareiss_virtcolswap pretends to swap the columns which can be faster for sparse matrices.
164
164
"""
165
- function bareiss! (M:: AbstractMatrix , swap_strategy= bareiss_colswap;
166
- find_pivot= find_pivot_any)
167
- swapcols!, swaprows!, update!, zero! = swap_strategy;
165
+ function bareiss! (M:: AbstractMatrix{T} , swap_strategy= bareiss_colswap;
166
+ find_pivot= find_pivot_any, column_pivots = nothing ) where T
167
+ swapcols!, swaprows!, update!, zero! = swap_strategy
168
168
prev = one (eltype (M))
169
169
n = size (M, 1 )
170
+ pivot = one (T)
171
+ column_permuted = false
170
172
for k in 1 : n
171
173
r = find_pivot (M, k)
172
- r === nothing && return k - 1
174
+ r === nothing && return ( k - 1 , pivot, column_permuted)
173
175
(swapto, pivot) = r
176
+ if column_pivots != = nothing && k != swapto[2 ]
177
+ column_pivots[k] = swapto[2 ]
178
+ column_permuted |= true
179
+ end
174
180
if CartesianIndex (k, k) != swapto
175
181
swapcols! (M, k, swapto[2 ])
176
182
swaprows! (M, k, swapto[1 ])
177
183
end
178
184
update! (zero!, M, k, swapto, pivot, prev)
179
185
prev = pivot
180
186
end
181
- return n
187
+ return (n, pivot, column_permuted)
188
+ end
189
+
190
+ function nullspace (A)
191
+ column_pivots = collect (1 : size (A, 2 ))
192
+ B = copy (A)
193
+ (rank, d, column_permuted) = bareiss! (B; column_pivots)
194
+ reduce_echelon! (B, rank, d)
195
+ N = ModelingToolkit. reduced_echelon_nullspace (rank, B)
196
+ apply_inv_pivot_rows! (N, column_pivots)
197
+ end
198
+
199
+ function apply_inv_pivot_rows! (M, ipiv)
200
+ for i in size (M, 1 ): - 1 : 1
201
+ swaprows! (M, i, ipiv[i])
202
+ end
203
+ M
204
+ end
205
+
206
+ # ##
207
+ # ## Modified from AbstractAlgebra.jl
208
+ # ##
209
+ # ## https://github.com/Nemocas/AbstractAlgebra.jl/blob/4803548c7a945f3f7bd8c63f8bb7c79fac92b11a/LICENSE.md
210
+ function reduce_echelon! (A:: AbstractMatrix{T} , rank, d) where T
211
+ m, n = size (A)
212
+ for i = rank + 1 : m
213
+ for j = 1 : n
214
+ A[i, j] = zero (T)
215
+ end
216
+ end
217
+ if rank > 1
218
+ t = zero (T)
219
+ q = zero (T)
220
+ d = - d
221
+ pivots = zeros (Int, n)
222
+ np = rank
223
+ j = k = 1
224
+ for i = 1 : rank
225
+ while iszero (A[i, j])
226
+ pivots[np + k] = j
227
+ j += 1
228
+ k += 1
229
+ end
230
+ pivots[i] = j
231
+ j += 1
232
+ end
233
+ while k <= n - rank
234
+ pivots[np + k] = j
235
+ j += 1
236
+ k += 1
237
+ end
238
+ for k = 1 : n - rank
239
+ for i = rank - 1 : - 1 : 1
240
+ t = A[i, pivots[np + k]] * d
241
+ for j = i + 1 : rank
242
+ t += A[i, pivots[j]] * A[j, pivots[np + k]] + q
243
+ end
244
+ A[i, pivots[np + k]] = exactdiv (- t, A[i, pivots[i]])
245
+ end
246
+ end
247
+ d = - d
248
+ for i = 1 : rank
249
+ for j = 1 : rank
250
+ if i == j
251
+ A[j, pivots[i]] = d
252
+ else
253
+ A[j, pivots[i]] = zero (T)
254
+ end
255
+ end
256
+ end
257
+ end
258
+ return A
259
+ end
260
+
261
+ function reduced_echelon_nullspace (rank, A:: AbstractMatrix{T} ) where T
262
+ n = size (A, 2 )
263
+ nullity = n - rank
264
+ U = zeros (T, n, nullity)
265
+ if rank == 0
266
+ for i = 1 : nullity
267
+ U[i, i] = one (T)
268
+ end
269
+ elseif nullity != 0
270
+ pivots = zeros (Int, rank)
271
+ nonpivots = zeros (Int, nullity)
272
+ j = k = 1
273
+ for i = 1 : rank
274
+ while iszero (A[i, j])
275
+ nonpivots[k] = j
276
+ j += 1
277
+ k += 1
278
+ end
279
+ pivots[i] = j
280
+ j += 1
281
+ end
282
+ while k <= nullity
283
+ nonpivots[k] = j
284
+ j += 1
285
+ k += 1
286
+ end
287
+ d = - A[1 , pivots[1 ]]
288
+ for i = 1 : nullity
289
+ for j = 1 : rank
290
+ U[pivots[j], i] = A[j, nonpivots[i]]
291
+ end
292
+ U[nonpivots[i], i] = d
293
+ end
294
+ end
295
+ return U
182
296
end
0 commit comments