-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhomology.py
More file actions
132 lines (116 loc) · 4.32 KB
/
Copy pathhomology.py
File metadata and controls
132 lines (116 loc) · 4.32 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#SCRIPT
import numpy as np
from numba import njit
import itertools
from itertools import combinations
from functools import reduce
from copy import deepcopy
###FUNCTIONS
#function for merging tuples
def tup_comb(el_prev, el):
return el_prev + el
@njit
def self_distance_matrix(X):
n = X.shape[0]
dist_matrix = np.zeros((X.shape[0], X.shape[0]), dtype=np.float32)
for i in range(n):
for j in range(i, n):
d = np.linalg.norm(X[i,:] - X[j,:])
dist_matrix[i,j] = d
dist_matrix[j,i] = d
return dist_matrix
def filtered_complex(D, max_dimension):
unique_distances = np.unique(D)
n = D.shape[0]
simplices = [{(s,):0.0 for s in range(n)}]
for d in range(max_dimension):
simplices.append({})
#Create simplicial complexes
for step in unique_distances:
for d in range(max_dimension):
if d == 0:
for i in range(0, n):
for j in range(i+1, n):
if step >= D[i,j]:
simplices[1][(i,j)] = D[i,j]
else:
combos = list(combinations(list(simplices[d].keys()), d+2))
for c in combos:
border_set = np.unique(reduce(tup_comb, c))
if (len(border_set) == d+2) and (tuple(border_set) not in simplices[d+1]):
simplices[d+1][tuple(border_set)] = step
#Sorting
simplices_list = list(itertools.chain.from_iterable([list(d.items()) for d in simplices]))
sorted_simplices_list = sorted(simplices_list, key = lambda x: (x[1], len(x[0]), x[0]))
simplices_indices = {s[0]:i for i,s in enumerate(sorted_simplices_list)}
return sorted_simplices_list, simplices_indices
def construct_boundary_matrix(sorted_simplices_list, simplices_indices):
boundary_matrix_d = len(sorted_simplices_list)
boundary_matrix = np.zeros((boundary_matrix_d, boundary_matrix_d), dtype=np.int)
for j, simp in enumerate(sorted_simplices_list):
if len(simp[0]) == 1:
continue
else:
d = len(simp[0])
combos = list(combinations(simp[0], d-1))
for c in combos:
boundary_matrix[simplices_indices[c], j] = 1
return boundary_matrix
@njit
def low(vec, ind):
while ind != -1:
if vec[ind] == 1:
return ind
else:
ind -= 1
return None
@njit
def reduce_matrix(R):
for j in range(R.shape[1]):
j_low = low(R[:,j], j)
if j_low is not None:
all_k = np.ones(j)
while all_k.any() and (j_low is not None):
for k in range(j):
k_low = low(R[:,k], k)
if (k_low is None) or (j_low is None):
all_k[k] = False
else:
all_k[k] = j_low == k_low
if (k_low is not None) and (j_low is not None) and (k_low == j_low):
R[:,j] = np.logical_xor(R[:,j], R[:,k])
j_low = low(R[:,j], j)
return R
@njit
def interpretation(R):
#Persistence pairs and Homology classes
P, E = [], []
for i in range(R.shape[0]):
flag = np.count_nonzero(R[:,i]) == 0
if flag:
check = []
for j in range(R.shape[1]):
if R[:,j].any() and (i == low(R[:,j], j)):
P.append((i,j))
if low(R[:,j], j) is None:
continue
if (i!=low(R[:,j], j)) and flag:
check.append(True)
else:
check.append(False)
if np.array(check).all():
E.append(i)
return P, E
#MAIN
def run(X, max_dimension=2):
assert X.shape[0] > max_dimension, 'Number of points must be greater than maximum simplex dimensionality'
D = self_distance_matrix(X)
sorted_simplices_list, simplices_indices = filtered_complex(D, max_dimension)
boundary_matrix = construct_boundary_matrix(sorted_simplices_list, simplices_indices)
##deepcopy matrix
R = deepcopy(boundary_matrix)
R = reduce_matrix(R)
P, E = interpretation(R)
homologies = [[sorted_simplices_list[p[0]], sorted_simplices_list[p[1]]] for p in P]
homologies = homologies + [[sorted_simplices_list[e], (None, D.max())] for e in E]
return homologies