Binary matrix multiplication
Question:
I got a matrix A, with the following bytes as rows:
11111110 (0xfe)
11111000 (0xf8)
10000100 (0x84)
10010010 (0x92)
My program reads a byte from stdin with the function sys.stdin.read(1)
. Suppose I receive the byte x 10101010 (0xaa)
. Is there a way using numpy to perform the multiplication:
>>> A.dot(x)
0x06 (00000110)
As A
is a 4×8 matrix, compossed by 4 bytes as rows, and x
is an 8 bit array, I was expecting to receive the (nibble 0110
) byte 0000 0110
as a result of the multiplication A * x
, treating bits as elements of the matrix.
If the elements of the matrix were treated as binary bytes, the result would be:
>>> A = np.array([[1,1,1,1,1,1,1,0],[1,1,1,1,1,0,0,0],[1,0,0,0,0,1,0,0],[1,0,0,1,0,0,1,0]])
>>> x = np.array([1,0,1,0,1,0,1,0])
>>> A.dot(x)%2
array([0, 1, 1, 0])
Answers:
1. Not using dot
You do not need to fully expand your matrix to do bitwise “multiplication” on it. You want to treat A
as a 4×8 matrix of bits and x
as an 8-element vector of bits. A row multiplication yields 1 for the bits that are on in both A
and x
and 0 if either bit is 0. This is equivalent to applying bitwise and (&
):
>>> [hex(n) for n in (A & x)]
['0xaa', '0xa8', '0x80', '0x82']
10101010
10101000
10000000
10000000
Here is a post on counting the bits in a byte. bin(n).count("1")
is probably the easiest one to use, so
>>> [bin(n).count("1") % 2 for n in (A & x)]
[0, 1, 1, 0]
If you want just a number, you can do something like
>>> int(''.join(str(bin(n).count("1") % 2) for n in (A & x)), 2)
6
2. Using dot
To use dot
, you can easily expand A
and x
into their numpy equivalents:
>>> list(list(int(n) for n in list(bin(r)[2:])) for r in A)
[['1', '1', '1', '1', '1', '1', '1', '0'],
['1', '1', '1', '1', '1', '0', '0', '0'],
['1', '0', '0', '0', '0', '1', '0', '0'],
['1', '0', '0', '1', '0', '0', '1', '0']]
>>> list(int(n) for n in bin(x)[2:])
[1, 0, 1, 0, 1, 0, 1, 0]
You can apply dot
to the result:
>>> np.dot(list(list(int(n) for n in list(bin(r)[2:])) for r in A),
list(int(n) for n in bin(x)[2:])) % 2
array([0, 1, 1, 0])
I got a matrix A, with the following bytes as rows:
11111110 (0xfe)
11111000 (0xf8)
10000100 (0x84)
10010010 (0x92)
My program reads a byte from stdin with the function sys.stdin.read(1)
. Suppose I receive the byte x 10101010 (0xaa)
. Is there a way using numpy to perform the multiplication:
>>> A.dot(x)
0x06 (00000110)
As A
is a 4×8 matrix, compossed by 4 bytes as rows, and x
is an 8 bit array, I was expecting to receive the (nibble 0110
) byte 0000 0110
as a result of the multiplication A * x
, treating bits as elements of the matrix.
If the elements of the matrix were treated as binary bytes, the result would be:
>>> A = np.array([[1,1,1,1,1,1,1,0],[1,1,1,1,1,0,0,0],[1,0,0,0,0,1,0,0],[1,0,0,1,0,0,1,0]])
>>> x = np.array([1,0,1,0,1,0,1,0])
>>> A.dot(x)%2
array([0, 1, 1, 0])
1. Not using dot
You do not need to fully expand your matrix to do bitwise “multiplication” on it. You want to treat A
as a 4×8 matrix of bits and x
as an 8-element vector of bits. A row multiplication yields 1 for the bits that are on in both A
and x
and 0 if either bit is 0. This is equivalent to applying bitwise and (&
):
>>> [hex(n) for n in (A & x)]
['0xaa', '0xa8', '0x80', '0x82']
10101010
10101000
10000000
10000000
Here is a post on counting the bits in a byte. bin(n).count("1")
is probably the easiest one to use, so
>>> [bin(n).count("1") % 2 for n in (A & x)]
[0, 1, 1, 0]
If you want just a number, you can do something like
>>> int(''.join(str(bin(n).count("1") % 2) for n in (A & x)), 2)
6
2. Using dot
To use dot
, you can easily expand A
and x
into their numpy equivalents:
>>> list(list(int(n) for n in list(bin(r)[2:])) for r in A)
[['1', '1', '1', '1', '1', '1', '1', '0'],
['1', '1', '1', '1', '1', '0', '0', '0'],
['1', '0', '0', '0', '0', '1', '0', '0'],
['1', '0', '0', '1', '0', '0', '1', '0']]
>>> list(int(n) for n in bin(x)[2:])
[1, 0, 1, 0, 1, 0, 1, 0]
You can apply dot
to the result:
>>> np.dot(list(list(int(n) for n in list(bin(r)[2:])) for r in A),
list(int(n) for n in bin(x)[2:])) % 2
array([0, 1, 1, 0])