-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcp_proto.py
82 lines (72 loc) · 2.98 KB
/
cp_proto.py
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
import lin_alg_proto as la
import numpy as np
def fr_norm_tensor( tensor, approx_tensor ):
"""
returns the frobenius norm of the difference of two tensors
:param tensor: item 1
:param approx_tensor: item 2
:return : frobenius norm of the difference of the two
"""
return np.linalg.norm( tensor - approx_tensor )
def recomp( factor_matrices, lambdas, orig_shape ):
"""
function that recomposes a tensor from the factor matrices given
:param factor_matrices: list of factor matrices
"""
# Each factor matrix has exactly r column vectors, where r is the number of unique rank one components
num_components = len( factor_matrices[0] )
num_factors = len( factor_matrices )
t_r = np.zeros( orig_shape )
for i in range( 0 , num_components ):
cur = lambdas[0] * factor_matrices[0][ : , i ]
for j in range( 1 , num_factors ):
cur = np.multiply.outer( cur , lambdas[j] * factor_matrices[j][ : , i ] )
t_r = t_r + cur
return t_r
def cp_decomp( tensor, num_factors, epochs, threshold ):
"""
function to carry out a CP decomposition for a given tensor
:param tensor: input tensor to carry out decomposition for
:param num_factors: number of rank-one factors to fit for
:param epochs: maximum number iterations
:param threshold: maximum acceptable error
:return : error rate, weight vector, factor matrices
"""
shape = tensor.shape
N = len( shape )
factor_matrices = [None] * N
total_elements = 1
# total number of scalar elements in tensor
for i in range(0, N):
total_elements *= shape[i]
# initialize factors to random values
for i in range( 0 , N ):
factor_matrices[i] = np.full( (shape[i],num_factors), 1 )
ep_passed = 0
lambdas = np.zeros( num_factors )
while True:
for n in range( 0 , N ):
v = np.full( (num_factors, num_factors) , 1 )
for i in [x for x in range( 0 , N ) if x != n]:
v = la.hadamard(
v ,
np.transpose( factor_matrices[i] ) @ factor_matrices[i]
)
v_inv = np.linalg.pinv( v )
k_temp = np.full( (1) , 1 )
for i in [x for x in range( N - 1 , -1 , -1 ) if x != n]:
k_temp = la.khatri_rao( k_temp , factor_matrices[i] )
factor_matrices[n] = np.reshape(
np.copy(tensor) ,
(shape[n], int (total_elements / shape[n])) ) @ k_temp @ v_inv
lambdas[n] = np.linalg.norm( factor_matrices[n] )
factor_matrices[n] = ( 1 / lambdas[n] ) * factor_matrices[n]
est = recomp( factor_matrices , lambdas , shape )
cost = fr_norm_tensor( tensor, est )
ep_passed += 1
if not cost > threshold or not ep_passed < epochs:
break
return lambdas, factor_matrices
t = np.full( (2,2,2), 1 )
c = cp_decomp(t, 3, 1000, .01)
print( recomp( c[1], c[0], t.shape))