-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathsdr_util.py
198 lines (164 loc) · 5.4 KB
/
sdr_util.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
"""
Utilities for sparsely encoded SDRs.
Using numba they-re pretty fast - generators run at 200k/sec for SDRs which are 40 bit solid.
SDR distance measurement runs at over 2M/sec for the same, 40 bit size test
Functions:
near_sdr() - changes a number of bits in the input SDR
near_sdrs() - generates a list of neighboring SDRs
random_sdrs() - generates a list of random SDRs - two consecutive
ones have zero overlap
sdr_overlap() - measures overlap in bits between two SDRs
sdr_distance()- a metric of distance between two SDRs
Beware both sdr_overlap and sdr_distance work on sorted SDRs
Copyright Cezar Totth 2022
Use this as you wish, without any warranties or restrictions
"""
import numpy as np
import numba
def empty_sdr():
return np.array([], dtype = np.uint32)
def linear_encoder(min_val, max_val,sdr_size, sdr_len):
bitrange = sdr_size - sdr_len
vrange = max_val - min_val
def encode(value):
firstbit = int(bitrange * (value - min_val) / vrange + 0.4999)
sdr = [bit for bit in range(firstbit, firstbit + sdr_len)]
return np.array(sdr, dtype = np.uint32)
return encode
# This is a "naive" python implementation which "compiles" well in numba
@numba.njit(fastmath = True)
def sdr_overlap(n1,n2):
"""
returns number of number overlapping bits between two input SDRs
"""
out = 0
i1, i2 = 0, 0
while i1 < n1.size and i2 < n2.size:
v1, v2 = n1[i1], n2[i2]
if v1 == v2:
out += 1
i2 += 1
elif v1 > v2:
i2 += 1
continue
i1 += 1
return out
@numba.njit(fastmath=True)
def sdr_intersection(n1, n2):
"""
returns bits found in both n1 and n2
"""
out = []
i1, i2 = 0, 0
while i1 < n1.size and i2 < n2.size:
v1, v2 = n1[i1], n2[i2]
if v1 == v2:
out.append(v1)
i2 += 1
elif v1 > v2:
i2 += 1
continue
i1 += 1
return np.array(out, dtype = np.uint32)
@numba.njit(fastmath = True)
def sdr_union(n1,n2):
out = []
i1, i2 = 0, 0
while i1 < n1.size and i2 < n2.size:
v1, v2 = n1[i1], n2[i2]
if v1 == v2:
# out.append(v1)
i2 += 1
elif v1 > v2:
out.append(v2)
i2 += 1
continue
out.append(v1)
i1 += 1
return np.array(out, dtype = np.uint32)
@numba.njit(fastmath = True)
def sdr_distance(n1, n2):
"""
A metric distance between two SDRs, consistent for various sizes SDRs
Returns:
0.0 if the two SDRs are identical.
1.0 if the two SDRs have zero overlapping bit
The actual formula is 1 - 2 * (number of overlapped bits)/ (total number of bits)
"""
return 1.0 - 2.0 * sdr_overlap(n1, n2) / (len(n1) + len(n2))
@numba.jit(nopython=True)
def random_sdr(sdr_size, sdr_len):
out = np.zeros(sdr_len, dtype = np.uint32)
r = np.random.randint(0,sdr_size)
for i in range(sdr_len):
while r in out:
r = np.random.randint(0,sdr_size)
out[i] = r
out.sort()
return out
@numba.jit(nopython=True)
def near_sdr(sdr, sdr_size, switch = 3):
"""
returns a sdr close to input sdr by switching switch bits
can be used to generate a random distribution of overlapping sdrs
sdr: Input SDR
sdr_size: total number of available bits
switch: how many bits in input SDR will be changed.
"""
out = sdr.copy()
np.random.shuffle(out)
for i in range(switch):
x = np.random.randint(0,sdr_size)
while x in out:
x = np.random.randint(0,sdr_size)
out[i] = x
out.sort()
return out
def near_sdrs(num_sdrs, sdr_size, on_bits, switch = 3):
"""
generates a list of slowly, randomly changing SDRs
first SDR is generated randomly
each following SDR is produced by randomly changing switch bits in its previous
num_sdrs: how many SDRs to generate
sdr_size: SDR length
on_bits : Solidity
switch : how many bits to switch from previous SDR in the list
"""
tor = [ np.random.permutation(sdr_size).astype(np.uint32)[:on_bits] ]
tor[0].sort()
for _ in range(num_sdrs):
tor.append(near_sdr(tor[-1],sdr_size, switch))
return tor
def random_sdrs(num_sdrs, sdr_size, on_bits):
"""
produces a list of random SDRs
num_sdrs: how many SDRs to generate
sdr_size: SDR length
on_bits : Solidity
"""
return near_sdrs(num_sdrs, sdr_size, on_bits, on_bits)[1:]
if __name__ == "__main__":
from time import time
NUM_SDRS = 100000
SDR_SIZE = 20000
SDR_BITS = 40
dummy = near_sdrs(2, SDR_SIZE, SDR_BITS) # give numba time to compile
print(f"generating {NUM_SDRS} sdrs of solidity/size: {SDR_BITS}/{SDR_SIZE}")
print("Near ....")
t = time()
nsdrs = near_sdrs(NUM_SDRS, SDR_SIZE, SDR_BITS)
t = time() - t
print(f"{NUM_SDRS} sdrs generated in {int(t*1000)} ms")
print("Random ....")
t = time()
rsdrs = random_sdrs(NUM_SDRS, SDR_SIZE, SDR_BITS)
t = time() - t
print(f"{NUM_SDRS} sdrs generated in {int(t*1000)} ms")
######## example for linear_encoder() ################
encode = linear_encoder(-1,1, 100, 10)
zero = encode(0)
minus_one = encode(-1)
plus_one = encode(1)
print(f"zero: {zero}")
print(f"minus one: {minus_one}")
print(f"plus one: {plus_one}")