-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathechelon_form.jl
144 lines (122 loc) · 4.01 KB
/
echelon_form.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
Base.@propagate_inbounds function _swap_rows!(A::AbstractMatrix, i, j)
@boundscheck @assert i in axes(A, 1)
@boundscheck @assert j in axes(A, 1)
i == j && return A
@inbounds for col_idx in axes(A, 2)
A[i, col_idx], A[j, col_idx] = A[j, col_idx], A[i, col_idx]
end
return A
end
Base.@propagate_inbounds function _mul_row!(
A::AbstractMatrix,
val,
row_idx;
starting_at = 1,
)
@boundscheck @assert starting_at in axes(A, 2)
@inbounds for col_idx in starting_at:last(axes(A, 2))
A[row_idx, col_idx] *= val
end
return A
end
# Version over exact field:
Base.@propagate_inbounds function _find_pivot(
A::AbstractMatrix,
col_idx;
starting_at = 1,
)
k = findnext(!iszero, @view(A[:, col_idx]), starting_at)
isnothing(k) && return false, col_idx
return true, k
end
Base.@propagate_inbounds function _reduce_column_by_pivot!(
A::AbstractMatrix,
row_idx,
col_idx;
starting_at = 1,
)
@boundscheck checkbounds(A, row_idx, col_idx)
@boundscheck starting_at in axes(A, 2)
for ridx in axes(A, 1)
ridx == row_idx && continue
v = A[ridx, col_idx]
iszero(v) && continue
@inbounds for cidx in starting_at:last(axes(A, 2))
A[ridx, cidx] -= v * A[row_idx, cidx]
end
end
return A
end
_finalize_pivot_reduce!(A::AbstractMatrix, pivot) = A
# version over AbstractFloat
const FloatOrComplex = Union{AbstractFloat,Complex{<:AbstractFloat}}
Base.@propagate_inbounds function _find_pivot(
A::AbstractMatrix{T},
col_idx;
starting_at = 1,
atol = eps(real(eltype(A))) * size(A, 1),
) where {T<:FloatOrComplex}
isempty(starting_at:size(A, 1)) && return false, starting_at
@boundscheck @assert starting_at in axes(A, 1)
# find the largest entry in the column below the last pivot
@boundscheck @assert col_idx in axes(A, 2)
mval, midx = findmax(abs, @view A[starting_at:end, col_idx])
if mval < atol # the largest entry is below threshold so we zero everything in the column
@view(A[starting_at:end, col_idx]) .= zero(T)
return false, starting_at
end
return true, oftype(starting_at, starting_at + midx - 1)
end
function _finalize_pivot_reduce!(
A::AbstractSparseMatrix{T},
pivot,
) where {T<:FloatOrComplex}
m = T <: Complex ? 2abs(eps(T)) : eps(T)
droptol!(A, max(size(A)...) * m)
return A
end
Base.@propagate_inbounds function _reduce_column_by_pivot!(
A::AbstractMatrix{T},
row_idx,
col_idx;
starting_at = 1,
atol = eps(real(eltype(A))) * size(A, 1),
) where {T<:FloatOrComplex}
@boundscheck checkbounds(A, row_idx, col_idx)
@boundscheck starting_at in axes(A, 2)
for ridx in axes(A, 1)
ridx == row_idx && continue
@inbounds v = A[ridx, col_idx]
if abs(v) < atol
@inbounds A[ridx, col_idx] = zero(T)
continue
end
@inbounds for cidx in starting_at:last(axes(A, 2))
A[ridx, cidx] -= v * A[row_idx, cidx]
end
end
return A
end
Base.@propagate_inbounds function row_echelon_form!(A::AbstractMatrix)
pivots = Int[]
row_idx = firstindex(axes(A, 1)) - 1 # also: current_rank
@inbounds for col_idx in axes(A, 2)
found, j = _find_pivot(A, col_idx; starting_at = row_idx + 1)
found || continue
row_idx += 1
push!(pivots, col_idx)
# swap the rows so that A[row_idx, :] is the row with leading nz in
# i-th column
A = _swap_rows!(A, row_idx, j)
w = inv(A[row_idx, col_idx])
# multiply A[row_idx, :] by w
A = _mul_row!(A, w, row_idx; starting_at = col_idx)
# A[current_rank, col_idx] is 1 now
# zero the whole col_idx-th column above and below pivot:
# to the left of col_idx everything is zero
A = _reduce_column_by_pivot!(A, row_idx, col_idx; starting_at = col_idx)
A = _finalize_pivot_reduce!(A, col_idx)
end
return A, pivots
end
row_echelon_form(A::AbstractMatrix) = row_echelon_form!(deepcopy(A))