Skip to content

Commit 0f3220d

Browse files
- cube_hash is now not int but bytes:
replaced unnecessary `int.from_bytes(data, 'big')` with just `data` - change naming: cube_hash to cube_id - typo correction: get_canoincal_packing - relevant docstring updates
1 parent 2059b9f commit 0f3220d

File tree

2 files changed

+53
-44
lines changed

2 files changed

+53
-44
lines changed

cubes.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,54 +42,58 @@ def generate_polycubes(n: int, use_cache: bool = False) -> list[np.ndarray]:
4242
else:
4343
pollycubes = generate_polycubes(n-1, use_cache)
4444

45-
hashes = set()
45+
known_ids = set()
4646
done = 0
4747
print(f"\nHashing polycubes n={n}")
4848
for base_cube in pollycubes:
4949
for new_cube in expand_cube(base_cube):
50-
cube_hash = get_canoincal_packing(new_cube, hashes)
51-
hashes.add(cube_hash)
50+
cube_id = get_canonical_packing(new_cube, known_ids)
51+
known_ids.add(cube_id)
5252
log_if_needed(done, len(pollycubes))
5353
done += 1
5454
log_if_needed(done, len(pollycubes))
5555

5656
print(f"\nGenerating polycubes from hash n={n}")
5757
results = []
5858
done = 0
59-
for cube_hash in hashes:
60-
results.append(unpack(cube_hash))
61-
log_if_needed(done, len(hashes))
59+
for cube_id in known_ids:
60+
results.append(unpack(cube_id))
61+
log_if_needed(done, len(known_ids))
6262
done += 1
63-
log_if_needed(done, len(hashes))
63+
log_if_needed(done, len(known_ids))
6464

6565
if (use_cache and not cache_exists(n)):
6666
save_cache(n, results)
6767

6868
return results
6969

7070

71-
def get_canoincal_packing(polycube: np.ndarray, known_hashes: set[int]) -> int:
71+
def get_canonical_packing(polycube: np.ndarray,
72+
known_ids: set[bytes]) -> bytes:
7273
"""
7374
Determines if a polycube has already been seen.
7475
75-
Considers all possible rotations of a cube against the existing cubes stored in memory.
76-
Returns True if the cube exists, or False if it is new.
76+
Considers all possible rotations of a polycube against the existing
77+
ones stored in memory. Returns the id if it's found in the set,
78+
or the maximum id of all rotations if the polycube is new.
7779
7880
Parameters:
79-
polycube (np.array): 3D Numpy byte array where 1 values indicate polycube positions
81+
polycube (np.array): 3D Numpy byte array where 1 values indicate
82+
cube positions. Must be of type np.int8
83+
known_ids (set[bytes]): A set of all known polycube ids
8084
8185
Returns:
82-
hash: the hash for this cube
86+
cube_id (bytes): the id for this cube
8387
8488
"""
85-
max_hash = 0
89+
max_id = b'\x00'
8690
for cube_rotation in all_rotations(polycube):
87-
this_hash = pack(cube_rotation)
88-
if(this_hash in known_hashes):
89-
return this_hash
90-
if (this_hash > max_hash):
91-
max_hash = this_hash
92-
return max_hash
91+
this_id = pack(cube_rotation)
92+
if (this_id in known_ids):
93+
return this_id
94+
if (this_id > max_id):
95+
max_id = this_id
96+
return max_id
9397

9498

9599
if __name__ == "__main__":

libraries/packing.py

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@
22
import math
33

44

5-
def pack(polycube: np.ndarray) -> int:
5+
def pack(polycube: np.ndarray) -> bytes:
66
"""
7-
Converts a 3D ndarray into a single unsigned integer for quick hashing and efficient storage
8-
9-
Converts a {0,1} nd array into a single unique large integer
7+
Converts a 3D ndarray into a single bytes object that unique identifies
8+
the polycube, is hashable, comparable, and allows to reconstruct the
9+
original polycube ndarray.
1010
1111
Parameters:
12-
polycube (np.array): 3D Numpy byte array where 1 values indicate polycube positions
12+
polycube (np.array): 3D Numpy byte array where 1 values indicate polycube positions,
13+
and 0 values indicate empty space. Must be of type np.int8.
1314
1415
Returns:
15-
int: a unique integer hash
16+
cube_id (bytes): a bytes representation of the polycube
1617
1718
"""
1819

20+
# # Previous implementation:
1921
# pack_cube = np.packbits(polycube.flatten(), bitorder='big')
2022
# cube_hash = 0
2123
# for index in polycube.shape:
@@ -24,31 +26,34 @@ def pack(polycube: np.ndarray) -> int:
2426
# cube_hash = (cube_hash << 8) + int(part)
2527
# return cube_hash
2628

27-
data = polycube.tobytes() + polycube.shape[0].to_bytes(1, 'big') + polycube.shape[1].to_bytes(1, 'big') + polycube.shape[2].to_bytes(1, 'big')
28-
return int.from_bytes(data, 'big')
29+
# # dtype should be np.int8: (commented out for efficiency)
30+
# if polycube.dtype != np.int8:
31+
# raise TypeError("Polycube must be of type np.int8")
2932

33+
# pack cube
34+
data = polycube.tobytes() + polycube.shape[0].to_bytes(1, 'big') \
35+
+ polycube.shape[1].to_bytes(1, 'big') \
36+
+ polycube.shape[2].to_bytes(1, 'big')
37+
return data
3038

31-
def unpack(cube_hash: int) -> np.ndarray:
32-
"""
33-
Converts a single integer back into a 3D ndarray
3439

40+
def unpack(cube_id: bytes) -> np.ndarray:
41+
"""
42+
Converts a bytes object back into a 3D ndarray
3543
3644
Parameters:
37-
cube_hash (int): a unique integer hash
45+
cube_id (bytes): a unique bytes object
3846
3947
Returns:
40-
np.array: 3D Numpy byte array where 1 values indicate polycube positions
41-
48+
polycube (np.array): 3D Numpy byte array where 1 values indicate
49+
cube positions
50+
4251
"""
52+
# Extract shape information
53+
shape = (cube_id[-3], cube_id[-2], cube_id[-1])
54+
55+
# Create ndarray from byte data
56+
polycube = np.frombuffer(cube_id[:-3], dtype=np.int8)
57+
polycube = polycube.reshape(shape)
58+
return polycube
4359

44-
length = math.ceil(math.log2(cube_hash))
45-
parts = cube_hash.to_bytes(length, byteorder='big')
46-
shape = (
47-
parts[-3],
48-
parts[-2],
49-
parts[-1],
50-
)
51-
size = shape[0] * shape[1] * shape[2]
52-
raw = np.frombuffer(parts[:-3], dtype=np.uint8)
53-
final = raw[(len(raw) - size):len(raw)].reshape(shape)
54-
return final

0 commit comments

Comments
 (0)