ialdev-core 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iad/core/__init__.py +9 -0
- iad/core/array.py +1961 -0
- iad/core/binary.py +377 -0
- iad/core/cache.py +903 -0
- iad/core/codetools.py +203 -0
- iad/core/datatools.py +671 -0
- iad/core/docs/locators.ipynb +754 -0
- iad/core/dotstyle.py +99 -0
- iad/core/env.py +271 -0
- iad/core/events.py +650 -0
- iad/core/filesproc.py +1046 -0
- iad/core/fnctools.py +390 -0
- iad/core/label.py +240 -0
- iad/core/logs.py +182 -0
- iad/core/nptools.py +449 -0
- iad/core/one_dark.puml +881 -0
- iad/core/param/__init__.py +17 -0
- iad/core/param/confargparse.py +55 -0
- iad/core/param/paramaze.py +339 -0
- iad/core/param/tbox.py +277 -0
- iad/core/paths.py +563 -0
- iad/core/pdtools.py +2570 -0
- iad/core/pydantools/__init__.py +5 -0
- iad/core/pydantools/fixed_pydantic_yaml/__init__.py +32 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/__init__.py +0 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/hacks.py +76 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/old_enums.py +37 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/representers.py +92 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/types.py +122 -0
- iad/core/pydantools/fixed_pydantic_yaml/compat/yaml_lib.py +104 -0
- iad/core/pydantools/fixed_pydantic_yaml/ext/__init__.py +1 -0
- iad/core/pydantools/fixed_pydantic_yaml/ext/semver.py +152 -0
- iad/core/pydantools/fixed_pydantic_yaml/ext/versioned_model.py +113 -0
- iad/core/pydantools/fixed_pydantic_yaml/main.py +30 -0
- iad/core/pydantools/fixed_pydantic_yaml/mixin.py +281 -0
- iad/core/pydantools/fixed_pydantic_yaml/model.py +20 -0
- iad/core/pydantools/fixed_pydantic_yaml/py.typed +1 -0
- iad/core/pydantools/fixed_pydantic_yaml/version.py +1 -0
- iad/core/pydantools/models.py +560 -0
- iad/core/regexp.py +348 -0
- iad/core/short.py +308 -0
- iad/core/strings.py +635 -0
- iad/core/unc_panda.py +270 -0
- iad/core/units.py +58 -0
- iad/core/wrap.py +420 -0
- ialdev_core-0.1.0.dist-info/METADATA +73 -0
- ialdev_core-0.1.0.dist-info/RECORD +48 -0
- ialdev_core-0.1.0.dist-info/WHEEL +4 -0
iad/core/binary.py
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import warnings
|
|
3
|
+
from math import *
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
_log = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Bin(int):
|
|
11
|
+
@staticmethod
|
|
12
|
+
def _test_range(val, bits):
|
|
13
|
+
val_bits = int(val).bit_length()
|
|
14
|
+
if bits and bits < val_bits:
|
|
15
|
+
raise ValueError('%d bits of %d exceeds bits limit of %d' % (val_bits, val, bits))
|
|
16
|
+
|
|
17
|
+
def __new__(cls, val, bits=None):
|
|
18
|
+
if hasattr(val, '__len__'):
|
|
19
|
+
return np.array([Bin(v, bits) for v in val], dtype='O')
|
|
20
|
+
Bin._test_range(val, bits)
|
|
21
|
+
return super(Bin, cls).__new__(cls, val)
|
|
22
|
+
|
|
23
|
+
def __init__(self, _, bits: object = None):
|
|
24
|
+
self.bits = bits # type: int
|
|
25
|
+
|
|
26
|
+
def __str__(self):
|
|
27
|
+
return bstr(int(self), bits=self.bits, dec=True)
|
|
28
|
+
|
|
29
|
+
def __repr__(self):
|
|
30
|
+
return self.__str__()
|
|
31
|
+
|
|
32
|
+
def __add__(self, other):
|
|
33
|
+
return Bin(int(self) + int(other),
|
|
34
|
+
max(bits_num(self), bits_num(other)) + 1
|
|
35
|
+
if self.bits or isinstance(other, Bin) and other.bits else None)
|
|
36
|
+
|
|
37
|
+
def __radd__(self, other):
|
|
38
|
+
return self.__add__(other)
|
|
39
|
+
|
|
40
|
+
def __sub__(self, other):
|
|
41
|
+
return Bin(int(self) - int(other),
|
|
42
|
+
max(bits_num(self), bits_num(other)) + 1
|
|
43
|
+
if self.bits or isinstance(other, Bin) and other.bits else None)
|
|
44
|
+
|
|
45
|
+
def __rsub__(self, other):
|
|
46
|
+
return Bin(int(other) - int(self),
|
|
47
|
+
max(bits_num(self), bits_num(other)) + 1
|
|
48
|
+
if self.bits or isinstance(other, Bin) and other.bits else None)
|
|
49
|
+
|
|
50
|
+
def __mul__(self, other):
|
|
51
|
+
return Bin(int(self) * int(other),
|
|
52
|
+
bits_num(self) + bits_num(other)
|
|
53
|
+
if self.bits or isinstance(other, Bin) and other.bits else None)
|
|
54
|
+
|
|
55
|
+
def __rmul__(self, other):
|
|
56
|
+
return self.__mul__(other)
|
|
57
|
+
|
|
58
|
+
def __lshift__(self, n: int):
|
|
59
|
+
return Bin(int(self) << n, self.bits + n if self.bits else None)
|
|
60
|
+
|
|
61
|
+
def __rshift__(self, n):
|
|
62
|
+
return Bin(int(self) >> n, self.bits - n if self.bits else None)
|
|
63
|
+
|
|
64
|
+
def __floordiv__(self, other):
|
|
65
|
+
return Bin(int(self) // int(other),
|
|
66
|
+
(self.bits if isinstance(other, Bin) else (2**self.bits-1) // int(other))
|
|
67
|
+
if self.bits else None)
|
|
68
|
+
|
|
69
|
+
def __rfloordiv__(self, other):
|
|
70
|
+
return Bin(int(other) // int(self), bits_num(other))
|
|
71
|
+
|
|
72
|
+
def __truediv__(self, other):
|
|
73
|
+
raise NotImplementedError('True Division not implemented for Bin')
|
|
74
|
+
|
|
75
|
+
def __pow__(self, n: int):
|
|
76
|
+
return Bin(int(self) ** n, self.bits * n if self.bits else None)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def set_bits(reg_val, field_val, start_bit, bit_len):
|
|
80
|
+
""" Set bit field of bit_len (start_bit - included) to the reg_val
|
|
81
|
+
|
|
82
|
+
:param reg_val: initial value - a number of integer type
|
|
83
|
+
:param field_val: field value to be set
|
|
84
|
+
:param start_bit: the first bit of field
|
|
85
|
+
:param bit_len: number of field bits
|
|
86
|
+
:return: final reg_val
|
|
87
|
+
"""
|
|
88
|
+
# mask = ~(((1 << bit_len) - 1) << start_bit)
|
|
89
|
+
# if field_val < 0:
|
|
90
|
+
# field_val &= 0xffffffff
|
|
91
|
+
# return (reg_val & mask) | (field_val << start_bit)
|
|
92
|
+
|
|
93
|
+
mask_f = (1 << bit_len) - 1
|
|
94
|
+
if field_val < 0:
|
|
95
|
+
field_val &= mask_f
|
|
96
|
+
mask_0 = ~(mask_f << start_bit)
|
|
97
|
+
return (reg_val & mask_0) | (field_val << start_bit)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_bits(data, start_bit, bit_len):
|
|
101
|
+
""" Extract bit field of bit_len (start_bit - included) from the data
|
|
102
|
+
|
|
103
|
+
:param data: a number of integer type
|
|
104
|
+
:param start_bit: the first bit to extract
|
|
105
|
+
:param bit_len: number of bits to extract
|
|
106
|
+
:return: resulted data
|
|
107
|
+
"""
|
|
108
|
+
mask = (1 << bit_len) - 1
|
|
109
|
+
return (data >> start_bit) & mask
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def extract_bits(data, lsb, msb):
|
|
113
|
+
""" Eextract specific range of bits (from lsb to msb - included) from the data
|
|
114
|
+
|
|
115
|
+
:param data: a number of ndarray (integer types)
|
|
116
|
+
:param lsb: the first bit to extract
|
|
117
|
+
:param msb: the last bit to extract
|
|
118
|
+
:return: resulted data
|
|
119
|
+
"""
|
|
120
|
+
return (((1 << (msb - lsb + 1)) - 1) << lsb & data) >> lsb
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def split_bits(data, bits):
|
|
124
|
+
""" Extract data channels encoded in bits into separate arrays
|
|
125
|
+
|
|
126
|
+
:param data: ndarray or number (integer types)
|
|
127
|
+
:param bits: numbers of bits in each array - from low to high
|
|
128
|
+
:return:
|
|
129
|
+
"""
|
|
130
|
+
if isinstance(data, np.ndarray) and data.dtype.kind not in ['i', 'u']:
|
|
131
|
+
raise TypeError('Integer type expected')
|
|
132
|
+
|
|
133
|
+
offsets = None
|
|
134
|
+
if type(bits[0]) == tuple:
|
|
135
|
+
offsets = list(list(zip(*bits))[1])
|
|
136
|
+
bits = list(list(zip(*bits))[0])
|
|
137
|
+
|
|
138
|
+
res, off = [], 0
|
|
139
|
+
for i, b in enumerate(bits):
|
|
140
|
+
if offsets is not None:
|
|
141
|
+
off = offsets[i]
|
|
142
|
+
res.append((((1 << b) - 1) << off & data) >> off)
|
|
143
|
+
off += b
|
|
144
|
+
return res
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def check_valid_offsets(zip_bits):
|
|
148
|
+
""" Check whether the offsets and the bit numbers are valid
|
|
149
|
+
|
|
150
|
+
:param zip_bits: list of tuples in the fio [(stream_bits_size, stream_offset), ...]
|
|
151
|
+
:return:
|
|
152
|
+
"""
|
|
153
|
+
zip_bits = sorted(zip_bits, key=lambda x: x[1])
|
|
154
|
+
print(zip_bits)
|
|
155
|
+
for i in range(len(zip_bits)-1):
|
|
156
|
+
if sum(zip_bits[i]) > zip_bits[i+1][1]:
|
|
157
|
+
raise AssertionError('You are writing twice on the same place..')
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def join_bits(bits, streams, check_clip=False):
|
|
161
|
+
""" Pack together bits of the streams
|
|
162
|
+
|
|
163
|
+
:param bits:
|
|
164
|
+
:param streams:
|
|
165
|
+
:param check_clip:
|
|
166
|
+
:param offsets: pre-known offsets of the bits. same fio like bits
|
|
167
|
+
:return:
|
|
168
|
+
"""
|
|
169
|
+
assert len(bits) == len(streams)
|
|
170
|
+
|
|
171
|
+
offsets = None
|
|
172
|
+
if type(bits[0]) == tuple:
|
|
173
|
+
check_valid_offsets(bits)
|
|
174
|
+
offsets = list(list(zip(*bits))[1])
|
|
175
|
+
bits = list(list(zip(*bits))[0])
|
|
176
|
+
|
|
177
|
+
res = np.zeros_like(streams[0], dtype='uint64')
|
|
178
|
+
shift = 0
|
|
179
|
+
for i,b in enumerate(bits):
|
|
180
|
+
a = streams[i]
|
|
181
|
+
masked_a = a & max_val(b)
|
|
182
|
+
if check_clip and (masked_a != a).any():
|
|
183
|
+
raise OverflowError('bitwise and clipped values')
|
|
184
|
+
if offsets is not None:
|
|
185
|
+
shift = offsets[i]
|
|
186
|
+
res |= (masked_a.astype(res.dtype) << shift)
|
|
187
|
+
shift += b
|
|
188
|
+
|
|
189
|
+
if offsets is not None:
|
|
190
|
+
shift = max(offset + bit for offset, bit in zip(offsets,bits))
|
|
191
|
+
return res.astype(fit_bits_uint(shift))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def max_val(bits):
|
|
195
|
+
return 2**bits-1
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def bits_num(val):
|
|
199
|
+
""" Count the minimal number of bits required to represent the given value
|
|
200
|
+
|
|
201
|
+
:param val:
|
|
202
|
+
:return: number of bits
|
|
203
|
+
"""
|
|
204
|
+
return val.bits if isinstance(val, Bin) and val.bits else val.bit_length()
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def bstr(num: int, bits=0, dec=0, msb=True, template='{dec}<{exc}{cnt}{bin}>'):
|
|
208
|
+
""" String representation of number in binary form.
|
|
209
|
+
|
|
210
|
+
Optionally decimal representation may be added.
|
|
211
|
+
If actual bits exceed declared number exclamation mark is added according to the template
|
|
212
|
+
Format template
|
|
213
|
+
:param num: the number to represent
|
|
214
|
+
:param bits: number of bits filled by 0 from the left
|
|
215
|
+
:param dec: positions for decimal representation. If not - will not appear
|
|
216
|
+
:param msb show position of the most significant bit
|
|
217
|
+
:param template: template for the output
|
|
218
|
+
:return: string representation
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
return template.format(
|
|
222
|
+
dec='{0:%d}' % dec if dec else '',
|
|
223
|
+
bin='{0:0%db}|%d' % (bits, bits) if bits else '{0:b}',
|
|
224
|
+
cnt='%d|' % int(num).bit_length() if msb and not bits else '',
|
|
225
|
+
exc='' if not bits or len(('{0:0%db}' % bits).format(num)) == bits else '!').format(num)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def encode_array(bits, a, range_weight=0.01, steps=500, show=False):
|
|
229
|
+
""" Encodes array of numbers into array of fixed bits elements
|
|
230
|
+
optimizing the bits utilization and encoding accuracy.
|
|
231
|
+
|
|
232
|
+
:param bits: number of bits to allocate for each element
|
|
233
|
+
:param a: array of numbers to encode
|
|
234
|
+
:param range_weight: factor diminishing cost of the range utilization
|
|
235
|
+
:param steps: number of steps (resolution of the search) range is [0 1.5*max_fitted]
|
|
236
|
+
:param show: if true displays some internals of the algorithms
|
|
237
|
+
|
|
238
|
+
Algorithm scans for a factor minimizing the total cost of the accuracy and range lost
|
|
239
|
+
after the original elements are multiplied by this factors and rounded to the required bits.
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
a_double = np.array(a, dtype='double')
|
|
243
|
+
max_rng = 2 ** bits - 1
|
|
244
|
+
|
|
245
|
+
# find index of a minimal non zero element
|
|
246
|
+
nzi = a_double.nonzero()[0]
|
|
247
|
+
mi = nzi[np.argmin(a_double[nzi])]
|
|
248
|
+
assert a[mi]
|
|
249
|
+
|
|
250
|
+
original_ratios = a_double / a_double.mean()
|
|
251
|
+
|
|
252
|
+
def pack(factor):
|
|
253
|
+
new_a = (a_double * factor).astype('uint64')
|
|
254
|
+
new_a[new_a > max_rng] = max_rng
|
|
255
|
+
return new_a
|
|
256
|
+
|
|
257
|
+
def rounding_error(factor):
|
|
258
|
+
a_k = pack(factor).astype('double')
|
|
259
|
+
avr = a_k.mean()
|
|
260
|
+
ratio_error = (((a_k / avr - original_ratios) * max_rng) ** 2).sum() if avr else np.inf
|
|
261
|
+
range_error = ((a_k - max_rng) ** 2).sum()
|
|
262
|
+
return np.array([ratio_error, range_error, ratio_error + range_weight * range_error])
|
|
263
|
+
|
|
264
|
+
k_range = np.arange(1, steps) * (max_rng * 1.5) / max(a_double) / (steps - 1)
|
|
265
|
+
errors = np.stack(np.vectorize(rounding_error, otypes='O')(k_range))
|
|
266
|
+
|
|
267
|
+
if show:
|
|
268
|
+
import matplotlib.pyplot as plt
|
|
269
|
+
plt.figure()
|
|
270
|
+
errors = np.sqrt(errors)
|
|
271
|
+
labels = ['ratio', 'range', 'total']
|
|
272
|
+
|
|
273
|
+
plt.plot(k_range, errors, '-')
|
|
274
|
+
plt.hold(True)
|
|
275
|
+
print('costs factor range total results')
|
|
276
|
+
print('-----------------------------------------------------')
|
|
277
|
+
y_max = 2 * np.nanmean(errors[errors < np.inf])
|
|
278
|
+
for j, label in zip(range(errors.shape[1]), labels):
|
|
279
|
+
i = np.argmin(errors[:, j])
|
|
280
|
+
k = k_range[i]
|
|
281
|
+
astr = ' '.join('{: 4g}'.format(x) for x in pack(k))
|
|
282
|
+
print(
|
|
283
|
+
'{}: {:.3f} {:.3f} {:.3f} [{} ]'.format(label, k, errors[i, 0], errors[i, 2], astr))
|
|
284
|
+
plt.plot(k_range[i], errors[i, j] + y_max * 0.02, 'v' + 'gbr'[j])
|
|
285
|
+
print('-----------------------------------------------------')
|
|
286
|
+
print('original values: [%s ]' % ' '.join('{: 4g}'.format(x) for x in a))
|
|
287
|
+
plt.ylim(0, y_max)
|
|
288
|
+
plt.xlim(k_range[[0, -1]])
|
|
289
|
+
plt.title('Bits Packing Errors')
|
|
290
|
+
plt.legend(labels)
|
|
291
|
+
plt.xlabel('coefficient (k)')
|
|
292
|
+
plt.ylabel('errors')
|
|
293
|
+
plt.show()
|
|
294
|
+
|
|
295
|
+
return Bin(pack(k_range[np.argmin(errors[:, 2])]), bits)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def align_type_bits(num_bits):
|
|
299
|
+
""" Align given bits to the bits of the minimal standard container type (8, 16, 32, 64)"""
|
|
300
|
+
|
|
301
|
+
if num_bits < 8:
|
|
302
|
+
return 8
|
|
303
|
+
elif num_bits > 64:
|
|
304
|
+
raise ValueError('%d exceeds maximal supported bits %d' % (num_bits, 64))
|
|
305
|
+
else:
|
|
306
|
+
return 2 ** ceil(log(num_bits, 2))
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def fit_bits_uint(x):
|
|
310
|
+
""" Return numpy style type name of the minimal uint container for the given bits numbers."""
|
|
311
|
+
return 'uint%s' % align_type_bits(x)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def parse_fxp_bits(fxp_bits):
|
|
315
|
+
""" Parse fix bits string """
|
|
316
|
+
return [int(d) for d in fxp_bits.split('.')] if isinstance(fxp_bits, str) else fxp_bits
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def to_fxp(data: np.ndarray, fxp_bits, inv_flp='max', inv_fxp='max_int'):
|
|
320
|
+
""" Convert data into fixed point fio - that is integer with last bits denoting a fractional part.
|
|
321
|
+
Convert invalid values if inv_fxp is not None.
|
|
322
|
+
|
|
323
|
+
:param data: any ndarray
|
|
324
|
+
:param fxp_bits: may be string ('8.6') or tuple-like (8, 6)
|
|
325
|
+
:param inv_flp: invalid code in the source data:
|
|
326
|
+
'max' - maximal for this data type,
|
|
327
|
+
None - don't bother
|
|
328
|
+
or a specific value
|
|
329
|
+
:param inv_fxp: invalid data code in the output array:
|
|
330
|
+
'max' - maximal integer value
|
|
331
|
+
'max_int' - maximal integer part of the fixed point
|
|
332
|
+
None - don't convert invalids
|
|
333
|
+
or a specific value encoding the invalids
|
|
334
|
+
"""
|
|
335
|
+
i_bits, f_bits = parse_fxp_bits(fxp_bits)
|
|
336
|
+
max_fxp = 2 ** (i_bits + f_bits) - 1
|
|
337
|
+
inv_options = {'max_int': 2 ** i_bits - 1, 'max': max_fxp}
|
|
338
|
+
|
|
339
|
+
if inv_flp == 'max':
|
|
340
|
+
inv_flp = np.finfo(data.dtype) if issubclass(data.dtype.type, np.float) else np.iinfo(data.dtype)
|
|
341
|
+
if inv_fxp in inv_options:
|
|
342
|
+
inv_fxp = inv_options[inv_fxp]
|
|
343
|
+
elif inv_fxp > max_fxp:
|
|
344
|
+
raise ValueError('Invalidation value %s exceeds the dynamic range %d' % (inv_fxp, max_fxp))
|
|
345
|
+
|
|
346
|
+
inv_mask = (data == inv_flp)
|
|
347
|
+
_log.info('Found %d invalids' % inv_mask.sum())
|
|
348
|
+
|
|
349
|
+
data *= 2 ** f_bits
|
|
350
|
+
sat_mask = (~inv_mask) * (data > max_fxp )
|
|
351
|
+
sat_num = sat_mask.sum()
|
|
352
|
+
if sat_num:
|
|
353
|
+
sat_range = (lambda sat: (sat.min(), sat.max()))(data[sat_mask])
|
|
354
|
+
warnings.warn('Conversion into %d.%d fio has saturated %d pixels in range %s.' %
|
|
355
|
+
(i_bits, f_bits, sat_num, sat_range))
|
|
356
|
+
data[sat_mask] = inv_fxp
|
|
357
|
+
if inv_fxp is not None:
|
|
358
|
+
data[inv_mask] = inv_fxp
|
|
359
|
+
return data.astype(fit_bits_uint(i_bits + f_bits))
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
if __name__ == '__main__':
|
|
363
|
+
pass
|
|
364
|
+
|
|
365
|
+
# Consider: to move testing to test_binar or remove
|
|
366
|
+
ba = np.random.randint(0, 4, size=(3, 4))
|
|
367
|
+
bb = np.random.randint(0, 8, size=(3, 4))
|
|
368
|
+
|
|
369
|
+
my_bits = [2, 3]
|
|
370
|
+
|
|
371
|
+
my_offsets = [3, 5]
|
|
372
|
+
my_bits = list(zip(my_bits, my_offsets))
|
|
373
|
+
|
|
374
|
+
joined = join_bits(my_bits, [ba, bb])
|
|
375
|
+
ra, rb = split_bits(joined, my_bits)
|
|
376
|
+
|
|
377
|
+
a=5
|