Skip to content
This repository was archived by the owner on Jun 27, 2025. It is now read-only.

Commit a386b0c

Browse files
committed
Merge branch 'master' of github.com:static-frame/arraymap
2 parents 700ba46 + 0af6e98 commit a386b0c

3 files changed

Lines changed: 195 additions & 46 deletions

File tree

arraymap.c

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,15 @@ char_get_end_p(char* p, Py_ssize_t dt_size) {
145145
}
146146

147147

148+
// This masks the input with INT64_MAX, which removes the MSB; we then cast to an int64; the range is now between 0 and INT64_MAX. We then use the MSB of the original value; if set, we negate the number, producing negative values for the upper half of the uint64 range. Note that we only need to check for hash -1 in this branch.
148149
static inline Py_hash_t
149150
uint_to_hash(npy_uint64 v) {
150-
Py_hash_t hash = (Py_hash_t)(v >> 1); // half unsigned fits in signed
151-
if (hash == -1) {
152-
return -2;
151+
Py_hash_t hash = (Py_hash_t)(v & INT64_MAX);
152+
if (v >> 63) {
153+
hash = -hash;
154+
if (hash == -1) {
155+
return -2;
156+
}
153157
}
154158
return hash;
155159
}
@@ -1239,7 +1243,12 @@ insert_int(
12391243
return -1;
12401244
}
12411245
if (self->table[table_pos].hash != -1) {
1242-
PyErr_SetObject(NonUniqueError, PyLong_FromSsize_t(key));
1246+
PyObject* er = PyLong_FromLongLong(key); // for error reporting
1247+
if (er == NULL) {
1248+
return -1;
1249+
}
1250+
PyErr_SetObject(NonUniqueError, er);
1251+
Py_DECREF(er);
12431252
return -1;
12441253
}
12451254
self->table[table_pos].keys_pos = keys_pos;
@@ -1264,7 +1273,12 @@ insert_uint(
12641273
return -1;
12651274
}
12661275
if (self->table[table_pos].hash != -1) {
1267-
PyErr_SetObject(NonUniqueError, PyLong_FromSsize_t(key));
1276+
PyObject* er = PyLong_FromUnsignedLongLong(key);
1277+
if (er == NULL) {
1278+
return -1;
1279+
}
1280+
PyErr_SetObject(NonUniqueError, er);
1281+
Py_DECREF(er);
12681282
return -1;
12691283
}
12701284
self->table[table_pos].keys_pos = keys_pos;
@@ -1290,7 +1304,12 @@ insert_double(
12901304
return -1;
12911305
}
12921306
if (self->table[table_pos].hash != -1) {
1293-
PyErr_SetObject(NonUniqueError, PyFloat_FromDouble(key));
1307+
PyObject* er = PyFloat_FromDouble(key);
1308+
if (er == NULL) {
1309+
return -1;
1310+
}
1311+
PyErr_SetObject(NonUniqueError, er);
1312+
Py_DECREF(er);
12941313
return -1;
12951314
}
12961315
self->table[table_pos].keys_pos = keys_pos;
@@ -1316,8 +1335,12 @@ insert_unicode(
13161335
return -1;
13171336
}
13181337
if (self->table[table_pos].hash != -1) {
1319-
PyErr_SetObject(NonUniqueError,
1320-
PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, key, key_size));
1338+
PyObject* er = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, key, key_size);
1339+
if (er == NULL) {
1340+
return -1;
1341+
}
1342+
PyErr_SetObject(NonUniqueError, er);
1343+
Py_DECREF(er);
13211344
return -1;
13221345
}
13231346
self->table[table_pos].keys_pos = keys_pos;
@@ -1343,8 +1366,12 @@ insert_string(
13431366
return -1;
13441367
}
13451368
if (self->table[table_pos].hash != -1) {
1346-
PyErr_SetObject(NonUniqueError,
1347-
PyBytes_FromStringAndSize(key, key_size));
1369+
PyObject* er = PyBytes_FromStringAndSize(key, key_size);
1370+
if (er == NULL) {
1371+
return -1;
1372+
}
1373+
PyErr_SetObject(NonUniqueError, er);
1374+
Py_DECREF(er);
13481375
return -1;
13491376
}
13501377
self->table[table_pos].keys_pos = keys_pos;
@@ -1947,6 +1974,7 @@ fam_init(PyObject *self, PyObject *args, PyObject *kwargs)
19471974
}
19481975
return 0;
19491976
error:
1977+
// assume all dynamic memory assigned to struct attrs that will be cleaned
19501978
return -1;
19511979
}
19521980

doc/articles/npy-opt.py

Lines changed: 140 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,20 @@ def get_array(size: int) -> np.ndarray:
309309
return array
310310

311311

312+
class FFUInt32(FixtureFactory):
313+
NAME = "uint32"
314+
SORT = 3
315+
316+
@staticmethod
317+
def get_array(size: int) -> np.ndarray:
318+
array = np.arange(INT_START, INT_START + size, dtype=np.uint32)
319+
array.flags.writeable = False
320+
return array
321+
322+
312323
class FFFloat64(FixtureFactory):
313324
NAME = "float64"
314-
SORT = 3
325+
SORT = 4
315326

316327
@staticmethod
317328
def get_array(size: int) -> np.ndarray:
@@ -322,7 +333,7 @@ def get_array(size: int) -> np.ndarray:
322333

323334
class FFFloat32(FixtureFactory):
324335
NAME = "float32"
325-
SORT = 4
336+
SORT = 5
326337

327338
@staticmethod
328339
def get_array(size: int) -> np.ndarray:
@@ -331,37 +342,112 @@ def get_array(size: int) -> np.ndarray:
331342
return array
332343

333344

334-
class FFString(FixtureFactory):
335-
NAME = "string"
345+
def get_string_array(size: int, char_count: int, kind: str) -> str:
346+
fmt = f'-<{char_count}'
347+
array = np.array(
348+
[f'{hex(e) * (char_count // 8)}'.format(fmt) for e in range(INT_START, INT_START + size)],
349+
dtype=f'{kind}{char_count}')
350+
array.flags.writeable = False
351+
return array
352+
353+
class FFU8(FixtureFactory):
354+
NAME = "U8"
336355
SORT = 6
337356

338357
@staticmethod
339358
def get_array(size: int) -> np.ndarray:
340-
array = np.array([hex(e) for e in range(size)])
341-
array.flags.writeable = False
342-
return array
343-
359+
return get_string_array(size, 8, 'U')
344360

345-
class FFString4x(FixtureFactory):
346-
NAME = "string 4x"
361+
class FFU16(FixtureFactory):
362+
NAME = "U16"
347363
SORT = 7
348364

349365
@staticmethod
350366
def get_array(size: int) -> np.ndarray:
351-
array = np.array([hex(e) * 4 for e in range(size)])
352-
array.flags.writeable = False
353-
return array
367+
return get_string_array(size, 16, 'U')
354368

355369

356-
class FFBytes(FixtureFactory):
357-
NAME = "bytes"
370+
class FFU32(FixtureFactory):
371+
NAME = "U32"
358372
SORT = 8
359373

360374
@staticmethod
361375
def get_array(size: int) -> np.ndarray:
362-
array = np.array([bytes(hex(e), encoding="utf-8") for e in range(size)])
363-
array.flags.writeable = False
364-
return array
376+
return get_string_array(size, 32, 'U')
377+
378+
379+
class FFU64(FixtureFactory):
380+
NAME = "U64"
381+
SORT = 9
382+
383+
@staticmethod
384+
def get_array(size: int) -> np.ndarray:
385+
return get_string_array(size, 64, 'U')
386+
387+
388+
class FFU128(FixtureFactory):
389+
NAME = "U128"
390+
SORT = 10
391+
392+
@staticmethod
393+
def get_array(size: int) -> np.ndarray:
394+
return get_string_array(size, 128, 'U')
395+
396+
397+
class FFS8(FixtureFactory):
398+
NAME = "S8"
399+
SORT = 11
400+
401+
@staticmethod
402+
def get_array(size: int) -> np.ndarray:
403+
return get_string_array(size, 8, 'S')
404+
405+
class FFS16(FixtureFactory):
406+
NAME = "S16"
407+
SORT = 12
408+
409+
@staticmethod
410+
def get_array(size: int) -> np.ndarray:
411+
return get_string_array(size, 16, 'S')
412+
413+
414+
class FFS32(FixtureFactory):
415+
NAME = "S32"
416+
SORT = 13
417+
418+
@staticmethod
419+
def get_array(size: int) -> np.ndarray:
420+
return get_string_array(size, 32, 'S')
421+
422+
423+
class FFS64(FixtureFactory):
424+
NAME = "S64"
425+
SORT = 14
426+
427+
@staticmethod
428+
def get_array(size: int) -> np.ndarray:
429+
return get_string_array(size, 64, 'S')
430+
431+
class FFS128(FixtureFactory):
432+
NAME = "S128"
433+
SORT = 15
434+
435+
@staticmethod
436+
def get_array(size: int) -> np.ndarray:
437+
return get_string_array(size, 128, 'S')
438+
439+
440+
441+
442+
# class FFBytes(FixtureFactory):
443+
# NAME = "bytes"
444+
# SORT = 8
445+
446+
# @staticmethod
447+
# def get_array(size: int) -> np.ndarray:
448+
# array = np.array([bytes(hex(e), encoding="utf-8") for e in range(INT_START, INT_START + size)])
449+
# array.flags.writeable = False
450+
# return array
365451

366452

367453
class FFObject(FixtureFactory):
@@ -390,19 +476,28 @@ def get_versions() -> str:
390476

391477

392478
CLS_FF = (
393-
FFInt32,
394-
FFInt64,
395-
FFUInt64,
396-
FFFloat64,
397-
FFString,
398-
FFString4x,
399-
FFBytes,
479+
# FFInt32,
480+
# FFInt64,
481+
# FFUInt32,
482+
# FFUInt64,
483+
# FFFloat64,
484+
FFU8,
485+
FFU16,
486+
FFU32,
487+
FFU64,
488+
FFU128,
489+
490+
FFS8,
491+
FFS16,
492+
FFS32,
493+
FFS64,
494+
FFS128,
400495
# FFObject,
401496
)
402497
FF_ORDER = [f.NAME for f in sorted(CLS_FF, key=lambda ff: ff.SORT)]
403498

404499
# -------------------------------------------------------------------------------
405-
NUMBER = 2
500+
NUMBER = 20
406501

407502
from itertools import product
408503

@@ -452,24 +547,33 @@ def plot_performance(frame, suffix: str = ""):
452547
ax.set_title(title, fontsize=6)
453548
ax.set_box_aspect(0.8)
454549
time_max = fixture["time"].max()
455-
ax.set_yticks([0, time_max * 0.5, time_max])
456-
ax.set_yticklabels(
457-
[
550+
time_min = fixture["time"].min()
551+
y_ticks = [0, time_min, time_max * 0.5, time_max]
552+
y_labels = [
458553
"",
554+
seconds_to_display(time_min),
459555
seconds_to_display(time_max * 0.5),
460556
seconds_to_display(time_max),
461-
],
462-
fontsize=6,
463-
)
557+
]
558+
if time_min > time_max * 0.25:
559+
# remove the min if it is greater than quarter
560+
y_ticks.pop(1)
561+
y_labels.pop(1)
562+
563+
ax.set_yticks(y_ticks)
564+
ax.set_yticklabels(y_labels, fontsize=4)
464565
# ax.set_xticks(x, names_display, rotation='vertical')
465566
ax.tick_params(
466567
axis="x",
467-
which="both",
468568
bottom=False,
469-
top=False,
470569
labelbottom=False,
471570
)
472-
571+
ax.tick_params(
572+
axis="y",
573+
length=2,
574+
width=.5,
575+
pad=1,
576+
)
473577
fig.set_size_inches(9, 3) # width, height
474578
fig.legend(post, names_display, loc="center right", fontsize=6)
475579
# horizontal, vertical
@@ -483,7 +587,7 @@ def plot_performance(frame, suffix: str = ""):
483587
right=0.85,
484588
top=0.80,
485589
wspace=1.0, # width
486-
hspace=0.2,
590+
hspace=0.4,
487591
)
488592
# plt.rcParams.update({'font.size': 22})
489593
plt.savefig(fp, dpi=300)

test/test_unit.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ def test_fam_constructor_array_int_d():
9292
assert k in fam
9393

9494

95+
def test_fam_constructor_array_int_e():
96+
# https://github.com/static-frame/arraymap/issues/12
97+
a1 = np.array((0, 0, 1, 1, 2, 2), dtype=int)
98+
a2 = a1[[0, 2, 4]]
99+
a2.flags.writeable = False
100+
fam1 = FrozenAutoMap(a2)
101+
assert list(fam1) == [0, 1, 2]
102+
103+
d1 = {i: int(i) for i in a2}
104+
fam2 = FrozenAutoMap(d1)
105+
assert list(fam2) == [0, 1, 2]
106+
107+
d2 = {0: 0, 3: 1}
108+
fam3 = FrozenAutoMap(d2)
109+
assert list(fam3) == [0, 3]
110+
111+
95112
# ------------------------------------------------------------------------------
96113

97114

0 commit comments

Comments
 (0)