forked from sbu-python-class/python-science
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgauss.py
More file actions
117 lines (84 loc) · 3.11 KB
/
gauss.py
File metadata and controls
117 lines (84 loc) · 3.11 KB
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
# gaussian elimination with scaled pivoting
#
# M. Zingale (2013-02-25)
from __future__ import print_function
import numpy as np
def gauss_elim(A, b, return_det=0, quiet=0):
""" perform gaussian elimination with pivoting, solving A x = b.
A is an NxN matrix, x and b are an N-element vectors. Note: A
and b are changed upon exit to be in upper triangular (row
echelon) form """
# b is a vector
if not b.ndim == 1:
print("ERROR: b should be a vector")
return None
N = len(b)
# A is square, with each dimension of length N
if not A.shape == (N, N):
print("ERROR: A should be square with each dim of same length as b")
return None
# allocation the solution array
x = np.zeros((N), dtype=A.dtype)
# find the scale factors for each row -- this is used when pivoting
scales = np.max(np.abs(A), 1)
# keep track of the number of times we swapped rows
num_row_swap = 0
if not quiet: print_Ab(A, b)
# main loop over rows
for k in range(N):
# find the pivot row based on the size of column k -- only consider
# the rows beyond the current row
row_max = np.argmax(A[k:, k]/scales[k:])
if k > 0: row_max += k # we sliced A from k:, correct for total rows
# swap the row with the largest scaled element in the current column
# with the current row (pivot) -- do this with b too!
if not row_max == k:
A[[k, row_max],:] = A[[row_max, k],:]
b[[k, row_max]] = b[[row_max, k]]
if not quiet: print("pivoted")
num_row_swap += 1
# do the forward-elimination for all rows below the current
for i in range(k+1, N):
coeff = A[i,k]/A[k,k]
for j in range(k+1, N):
A[i,j] += -A[k,j]*coeff
A[i,k] = 0.0
b[i] += -coeff*b[k]
if not quiet: print_Ab(A, b)
# back-substitution
# last solution is easy
x[N-1] = b[N-1]/A[N-1,N-1]
for i in reversed(range(N-1)):
sum = b[i]
for j in range(i+1,N):
sum += -A[i,j]*x[j]
x[i] = sum/A[i,i]
# determinant
det = np.prod(np.diagonal(A))*(-1.0)**num_row_swap
if not return_det:
return x
else:
return x, det
# for debugging:
# output: np.savetxt("test.out", a, fmt="%5.2f", delimiter=" ")
# convert -font Courier-New-Regular -pointsize 20 text:test.out test.png
def print_Ab(A, b):
""" printout the matrix A and vector b in a pretty fashion. We
don't use the numpy print here, because we want to make them
side by side"""
N = len(b)
open_t = "/"
close_t = "\\"
open_b = "\\"
close_b = "/"
# numbers take 6 positions + 2 spaces
a_fmt = " {:6.3f} "
space = 8*" "
line = "|" + N*a_fmt + "|" + space + "|" + a_fmt + "|"
top = open_t + N*space + close_t + space + open_t + space + close_t
bottom = open_b + N*space + close_b + space + open_b + space + close_b + "\n"
print(top)
for i in range(N):
out = tuple(A[i,:]) + (b[i],)
print(line.format(*out))
print(bottom)