forked from rballester/tthistograms
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtthistogram.py
executable file
·159 lines (139 loc) · 6.71 KB
/
tthistogram.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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""
The most important class: the TTHistogram. Includes code compression and look-up over a variety of ROIs
"""
import numpy as np
import tt
import scipy.signal
import time
import sys
import ttrecipes as tr
class TTHistogram(object):
def __init__(self, X, B, eps, verbose=False):
"""
Build (incrementally) a compressed integral histogram that can be later queried
:param X: an ndarray, *already quantized* with values in 0, ..., B-1
:param B: number of bins
:param eps: relative error for the hierarchical sum-and-compress
"""
N = X.ndim
if not (X.min() >= 0 and X.max() < B):
raise ValueError('Please provide an array that is already quantized')
start = time.time()
def create_generator():
for b in range(B):
if verbose:
print(b)
sys.stdout.flush()
sl = (X == b)
for n in range(N):
sl = np.cumsum(sl, axis=n)
sys.stdout.flush()
sl_t = tr.core.tt_svd(sl, eps, verbose=False)
sys.stdout.flush()
cores = tt.vector.to_list(sl_t)
onehot = np.zeros([1, B, 1])
onehot[0, b, 0] = 1
cores.append(onehot)
sl_t = tt.vector.from_list(cores)
if verbose:
print('Done')
yield sl_t
generator = create_generator()
self.tensor = tr.core.sum_and_compress(generator, rounding=eps/np.log2(B), verbose=verbose)
cores = tt.vector.to_list(self.tensor)
cores[:-1] = [np.concatenate([np.zeros([c.shape[0], 1, c.shape[2]]), c], axis=1) for c in cores[:-1]]
self.tensor = tt.vector.from_list(cores)
self.total_time = time.time() - start
def box(self, corners):
"""
:param corners: a list of pairs [[i0, i1], [j0, j1], ...] encoding the query box
:return: a vector with B elements
"""
cores = tt.vector.to_list(self.tensor)
start = time.time()
reco = np.array([[1]])
for corner, core in zip(corners, cores[:-1]):
reco = reco.dot(core[:, corner[1], :] - core[:, corner[0], :])
reco = np.squeeze(reco.dot(cores[-1][:, :, 0]))
elapsed = time.time() - start
return reco, elapsed
def separable(self, corners, pat):
"""
:param corners: a list of pairs [[i0, i1], [j0, j1], ...] containing all window positions to compute
:param pat: a rank-1 TT encoding the separable region (must fit in `corners`)
:return: an array of dimension N+1 and size (i1-i0+1) x (j1-j0+1) x ... x B
"""
assert np.all(corners[:, 1] - corners[:, 0] == pat.n)
cores = tt.vector.to_list(self.tensor)
coresp = [-np.diff(np.concatenate([np.zeros([c.shape[0], 1, c.shape[2]]), c, np.zeros([c.shape[0], 1, c.shape[2]])], axis=1), axis=1) for c in tt.vector.to_list(pat)]
start = time.time()
reco = np.array([[1]])
for corner, core, corep, in zip(corners, cores[:-1], coresp):
comb = np.einsum('ijk,ljm->ilkm', core[:, corner[0]:corner[1]+1, :], corep)
comb = np.reshape(comb, [comb.shape[0]*comb.shape[1], comb.shape[-2]*comb.shape[-1]])
reco = reco.dot(comb)
reco = np.squeeze(reco.dot(cores[-1][:, :, 0]))
elapsed = time.time() - start
return reco, elapsed
def nonseparable(self, corners, pat):
"""
As `separable`, but `pat` does not need have rank 1
"""
assert np.all(corners[:, 1] - corners[:, 0] == pat.n)
cores = tt.vector.to_list(self.tensor)
coresp = [-np.diff(np.concatenate([np.zeros([c.shape[0], 1, c.shape[2]]), c, np.zeros([c.shape[0], 1, c.shape[2]])], axis=1), axis=1) for c in tt.vector.to_list(pat)]
start = time.time()
cores[:-1] = [core[:, corner[0]:corner[1] + 1, :] for corner, core in zip(corners, cores[:-1])]
Rprod = np.array([[1]])
def partial_dot_right(cores1, cores2, mu, Rprod):
Ucore = np.einsum('ij,ikl->jkl', Rprod, cores1[mu])
Vcore = cores2[mu]
return np.dot(tr.core.left_unfolding(Ucore).T, tr.core.left_unfolding(Vcore))
d = len(coresp)
for mu in range(d):
Rprod = partial_dot_right(coresp, cores, mu, Rprod)
Rprod = Rprod.dot(cores[-1][:, :, 0])
elapsed = time.time() - start
return np.squeeze(Rprod), elapsed
def box_field(self, corners, shape):
"""
:param corners: a list of pairs [[i0, i1], [j0, j1], ...] containing all window positions to compute
:param shape: a list of N integers (all must be odd) encoding the shape of each window
:return: an array of dimension N+1 and size (i1-i0+1) x (j1-j0+1) x ... x B
"""
assert all(np.mod(shape, 2) == 0)
shape = shape // 2
assert all(corners[:, 0] - shape >= 0)
assert all(corners[:, 1] + shape <= self.tensor.n[:-1])
cores = tt.vector.to_list(self.tensor)
start = time.time()
reco = np.ones([1, 1, 1])
for corner, sh, core in zip(corners, shape, cores[:-1]):
chunk = core[:, corner[0]+sh:corner[1]+sh, :] - core[:, corner[0]-sh:corner[1]-sh, :] # r1 x corner[1]-corner[0] x r2
reco = np.tensordot(reco, chunk, axes=[2, 0])
reco = np.reshape(reco, [reco.shape[0], -1, reco.shape[-1]])
reco = np.squeeze(np.tensordot(reco, cores[-1], axes=[2, 0]))
reco = np.reshape(reco, list(corners[:, 1] - corners[:, 0]) + [self.tensor.n[-1]])
elapsed = time.time() - start
return reco, elapsed
def separable_field(self, corners, pat):
"""
As `box_field`, but for non-rectangular separable regions.
:param pat: a rank-1 TT
"""
shape = pat.n
assert all(np.mod(shape, 2) == 0)
shape = shape // 2
cores = tt.vector.to_list(self.tensor)
coresp = [np.diff(np.concatenate([np.zeros([c.shape[0], 1, c.shape[2]]), c, np.zeros([c.shape[0], 1, c.shape[2]])], axis=1), axis=1) for c in tt.vector.to_list(pat)]
start = time.time()
reco = np.ones([1, 1])
for corner, sh, core, corep in zip(corners, shape, cores[:-1], coresp):
chunk = core[:, corner[0]-sh:corner[1]+sh, :]
convolution = scipy.signal.convolve(chunk, corep, mode='valid')
reco = np.einsum('jk,klm', reco, convolution)
reco = np.reshape(reco, [-1, convolution.shape[-1]])
reco = np.squeeze(np.tensordot(reco, cores[-1], axes=[1, 0]))
reco = np.reshape(reco, list(corners[:, 1] - corners[:, 0]) + [self.tensor.n[-1]])
elapsed = time.time() - start
return reco, elapsed