Skip to content

Commit fc76754

Browse files
Chipe1norvig
authored andcommitted
Added truncated SVD (aimacode#587)
1 parent e3f5657 commit fc76754

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

tests/test_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ def test_normalize():
139139
assert normalize([1, 2, 1]) == [0.25, 0.5, 0.25]
140140

141141

142+
def test_norm():
143+
assert isclose(norm([1, 2, 1], 1), 4)
144+
assert isclose(norm([3, 4], 2), 5)
145+
assert isclose(norm([-1, 1, 2], 4), 18**0.25)
146+
147+
142148
def test_clip():
143149
assert [clip(x, 0, 1) for x in [-1, 0.5, 10]] == [0, 0.5, 1]
144150

@@ -155,6 +161,25 @@ def test_gaussian():
155161
assert gaussian(3,1,3) == 0.3989422804014327
156162

157163

164+
def test_truncated_svd():
165+
test_mat = [[17, 0],
166+
[0, 11]]
167+
_, _, eival = truncated_svd(test_mat)
168+
assert isclose(eival, 17)
169+
170+
test_mat = [[17, 0],
171+
[0, -34]]
172+
_, _, eival = truncated_svd(test_mat)
173+
assert isclose(eival, -34)
174+
175+
test_mat = [[1, 0, 0, 0, 2],
176+
[0, 0, 3, 0, 0],
177+
[0, 0, 0, 0, 0],
178+
[0, 2, 0, 0, 0]]
179+
_, _, eival = truncated_svd(test_mat)
180+
assert isclose(eival, 3)
181+
182+
158183
def test_sigmoid_derivative():
159184
value = 1
160185
assert sigmoid_derivative(value) == 0

utils.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,11 @@ def normalize(dist):
246246
return [(n / total) for n in dist]
247247

248248

249+
def norm(X, n=2):
250+
"""Return the n-norm of vector X"""
251+
return sum([x**n for x in X])**(1/n)
252+
253+
249254
def clip(x, lowest, highest):
250255
"""Return x clipped to the range [lowest..highest]."""
251256
return max(lowest, min(x, highest))
@@ -270,6 +275,41 @@ def gaussian(mean, st_dev, x):
270275
return 1/(math.sqrt(2*math.pi)*st_dev)*math.e**(-0.5*(float(x-mean)/st_dev)**2)
271276

272277

278+
def truncated_svd(X, max_iter=1000):
279+
"""Computes the first component of SVD"""
280+
281+
def normalize_vec(X, n = 2):
282+
"""Returns normalized vector"""
283+
norm_X = norm(X, n)
284+
Y = [x/norm_X for x in X]
285+
return Y
286+
287+
m, n = len(X), len(X[0])
288+
A = [[0 for _ in range(n + m)] for _ in range(n + m)]
289+
for i in range(m):
290+
for j in range(n):
291+
A[i][j] = A[m + j][n + i] = X[i][j]
292+
293+
X = [random.random() for _ in range(n + m)]
294+
X = normalize_vec(X)
295+
for _ in range(max_iter):
296+
old_X = X
297+
X = matrix_multiplication(A, [[x] for x in X])
298+
X = [x[0] for x in X]
299+
X = normalize_vec(X)
300+
# check for convergence
301+
if norm([x1 - x2 for x1, x2 in zip(old_X, X)]) <= 1e-10:
302+
break
303+
304+
projected_X = matrix_multiplication(A, [[x] for x in X])
305+
projected_X = [x[0] for x in projected_X]
306+
eival = norm(projected_X, 1)/norm(X, 1)
307+
eivec_n = normalize_vec(X[:n])
308+
eivec_m = normalize_vec(X[n:])
309+
310+
return (eivec_m, eivec_n, eival)
311+
312+
273313
try: # math.isclose was added in Python 3.5; but we might be in 3.4
274314
from math import isclose
275315
except ImportError:

0 commit comments

Comments
 (0)