mathbinarymathematical-optimization

Selecting a 3-dimensional point within a binary array / number given arbitrary dimensions / position


Im trying to abstract a 3D point system using binary to speed things up.

Lets say I have an arbitrary 3D "volume", say, 8x8x8 units. this volume is defined by a 512-bit binary number (1-dimensional line in essence).

How could I select a 0 or 1 within that number given a 3D position? Im wondering if there is a simple set of binary operations to do something like this, because Ive seen similar implementations, but no description of how they actually work.

Like, lets say this volume is defined left to right (0-7 on the x axis), then forward to back (0-7 on the y axis) and then bottom to top (0-7 on the z axis), making up an 8x8 cube.

basically with these instructions, a point at 1, 1, 1 in a 4x4x4 box would look like this in the array:

                          V - point 1,1,1
0000 0000 0000 0000 0000 0100 0000 0000 ...

Is there some way in binary / bitwise math to count along the x > y > z axis and separate out a binary value from the array given a 3D point?

Inherently, I need to both be able to read that binary value, but also write to it... I assume there would be a way to do this either way

Realistically, I could just convert this binary number to a 1D array and use basic modulos to select the proper position, but if I can speed this up with binary it would really help me out in the long run.


Solution

  • Just do a pack and reshape:

    from typing import Any
    
    import numpy as np
    
    type ByteArray = np.ndarray[Any, np.dtype[np.uint8]]
    
    
    def codes_to_cube(
        codes: np.ndarray,
    ) -> ByteArray:
        bits = np.unpackbits(codes)
        n = int(round(bits.size**(1/3)))
        return bits.reshape((n, n, n))
    
    
    def cube_to_codes(
        cube: ByteArray,
    ) -> ByteArray:
        return np.packbits(cube.ravel())
    
    
    def demo() -> None:
        rand = np.random.default_rng(seed=0)
    
        print('Encoding random bytes:')
        codes = rand.integers(
            size=512//8, low=0, high=256, dtype=np.uint8,
        )
        cube = codes_to_cube(codes)
        print(cube)
        print()
    
        decoded = cube_to_codes(cube)
        assert np.array_equal(decoded, codes)
    
        print('Decoding cube with selected non-zeros:')
        cube = np.zeros(shape=(8, 8, 8), dtype=np.uint8)
        cube[0, 0, 5] = 1
        cube[4, 1, 6] = 1
        cube[2, 7, 1] = 1
        codes = cube_to_codes(cube)
        print(codes)
        encoded = codes_to_cube(codes)
        assert np.array_equal(encoded, cube)
    
    
    if __name__ == '__main__':
        demo()
    
    Encoding random bytes:
    [[[0 1 0 1 1 1 1 1]
      [1 0 0 0 0 0 1 0]
      [1 1 0 0 0 0 1 0]
      [1 1 0 1 1 0 0 1]
      [1 1 0 0 1 1 1 1]
      [1 1 1 0 1 0 1 1]
      [0 0 0 0 1 1 1 1]
      [1 0 1 0 0 0 1 1]]
    
     [[0 0 1 0 0 0 0 1]
      [1 1 0 1 0 1 1 1]
      [1 1 0 1 1 0 0 1]
      [1 0 0 0 0 0 1 0]
      [1 1 1 1 1 0 0 0]
      [1 0 1 1 1 1 0 1]
      [0 0 0 1 0 0 0 0]
      [0 1 0 0 0 1 0 1]]
    
     [[1 0 1 1 1 0 0 0]
      [1 1 1 0 1 0 0 0]
      [1 1 0 0 1 1 0 1]
      [0 1 0 0 1 1 1 0]
      [1 0 1 0 1 0 0 1]
      [0 0 1 1 1 1 0 1]
      [0 1 1 1 1 1 0 1]
      [0 0 0 0 1 0 1 0]]
    
     [[0 0 0 1 1 1 0 1]
      [1 1 1 1 0 0 0 0]
      [0 1 0 0 0 0 1 0]
      [0 0 0 1 0 0 1 1]
      [1 0 1 1 0 1 1 0]
      [0 0 1 0 0 1 1 1]
      [0 0 1 1 1 0 1 1]
      [0 0 0 0 0 1 0 0]]
    
     [[0 0 1 1 1 0 1 1]
      [0 1 0 1 0 0 0 1]
      [1 1 0 1 1 1 1 0]
      [0 0 1 0 1 1 0 0]
      [0 1 1 1 1 0 0 0]
      [0 1 1 1 1 0 1 0]
      [0 0 1 1 0 0 1 0]
      [1 1 0 1 0 0 0 0]]
    
     [[0 1 0 0 1 1 1 0]
      [0 0 0 1 1 1 0 0]
      [0 1 0 0 0 0 0 0]
      [1 0 1 0 0 1 1 0]
      [0 1 1 1 1 0 0 1]
      [0 1 0 1 1 0 0 1]
      [1 0 1 0 1 0 1 0]
      [1 1 1 0 1 0 0 1]]
    
     [[0 1 1 1 1 1 1 1]
      [1 0 1 1 0 0 1 0]
      [1 1 1 0 1 1 0 1]
      [1 0 0 0 0 0 0 0]
      [0 1 1 1 0 0 0 1]
      [0 1 1 1 1 0 1 1]
      [0 1 0 0 1 1 0 0]
      [1 0 0 1 1 0 1 1]]
    
     [[0 1 0 0 0 1 0 1]
      [1 0 0 1 1 0 1 0]
      [1 0 0 0 0 0 1 0]
      [1 1 1 1 1 0 0 0]
      [0 1 0 1 1 1 1 1]
      [0 1 0 0 1 0 0 1]
      [1 1 0 0 0 0 0 0]
      [1 0 1 1 1 0 1 0]]]
    
    Decoding cube with selected non-zeros:
    [ 4  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 64
      0  0  0  0  0  0  0  0  0  2  0  0  0  0  0  0  0  0  0  0  0  0  0  0
      0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]