I want to create an 8x8 matrix which provides the error probabilities in bit communication. The matrix looks as follows:
The columns amount to the observed quantities and the rows to the measured quantities. An element p[i,j]
amounts to the conditional probability p(j|i)
. For example, the element
p[0,1]
gives the probability to observe the string 001
when the actual value is 000
, i.e., it measures p(001|000)
.
Question: How can I create such a matrix in Python such that
p(100|000)<p(110|000)
?p(001|000)< p(000|001)
. That is, having bias that favors with higher probabilities transitions 1 to 0 than transitions 0 to 1.Of course, the sum of probabilities in each row must equal to 1.
All in all, I want to create function in Python that takes as input an integer n
(the size of the matrix, or equivalently where 2^n
is the length of the bit string) and outputs a probability transition matrix with the above specified rules.
The difficulty is how to implement a probability distribution to fill the cells.
It is trivial to create an 8x8 array and fill diagonals:
P = np.zeros((8,8))
for i in range(8):
for j in range(8):
if i==j:
P[i,j]=1
Similarly, its trivial to fill a given row or a given column by a fixed number. However, I cannot figure out (how to even begin) to fill such a matrix following the rules above, or even how exactly to define the distribution the elements must follow.
It turns out you can do this simply without numpy
or scipy
. I use pandas
for nice printing.
The logic is that for each bit, you have a probability of flipping (p01
or p10
) or remaining the same (p00
or p11
). Transforming one bit string to another requires multiplying the appropriate probability for each of n
bits.
For example: P(010|001) = P(0->0) * P(1->0) * P(0->1) = p00 * p10 * p01
This process is repeated for every sent
and observed
combination.
You can further reduce the two level if
statement below to one line using nested ternary assignment, but I think this is a nice balance of being concise and readable:
import pandas as pd
def p(sent, observed, p01, p10):
"""Return the probability of 'sent' being received as 'observed'
given p01 (the probability a bit flips from a 0->1) and p10 (the
probability a bit flips from 1->0).
"""
p00 = 1 - p01
p11 = 1 - p10
r = 1
for i, _ in enumerate(sent):
if sent[i] == "0":
r *= p00 if observed[i] == "0" else p01
else:
r *= p10 if observed[i] == "0" else p11
return r
def generate_error_matrix(n, p01, p10):
"""Print a matrix of the transitions of all permutations of bit
errors for a given bit length.
Parameters:
n - the number of bits
p01 - probability of a bit flipping from 0 to 1
p10 - probability of a bit flipping from 1 to 0
"""
labels = [f"{i:0{n}b}" for i in range(0, 2**n)]
result = pd.DataFrame(index=labels, columns=labels)
for rowIndex, row in result.iterrows():
for columnIndex, _ in row.items():
result.at[rowIndex, columnIndex] = p(rowIndex, columnIndex, p01, p10)
return result
print(generate_error_matrix(n=3, p01=0.2, p10=0.1))
000 001 010 011 100 101 110 111
000 0.512 0.128 0.128 0.032 0.128 0.032 0.032 0.008
001 0.064 0.576 0.016 0.144 0.016 0.144 0.004 0.036
010 0.064 0.016 0.576 0.144 0.016 0.004 0.144 0.036
011 0.008 0.072 0.072 0.648 0.002 0.018 0.018 0.162
100 0.064 0.016 0.016 0.004 0.576 0.144 0.144 0.036
101 0.008 0.072 0.002 0.018 0.072 0.648 0.018 0.162
110 0.008 0.002 0.072 0.018 0.072 0.018 0.648 0.162
111 0.001 0.009 0.009 0.081 0.009 0.081 0.081 0.729
print(generate_error_matrix(n=3, p01=1, p10=0))
000 001 010 011 100 101 110 111
000 0 0 0 0 0 0 0 1
001 0 0 0 0 0 0 0 1
010 0 0 0 0 0 0 0 1
011 0 0 0 0 0 0 0 1
100 0 0 0 0 0 0 0 1
101 0 0 0 0 0 0 0 1
110 0 0 0 0 0 0 0 1
111 0 0 0 0 0 0 0 1
print(generate_error_matrix(n=3, p01=0, p10=1))
000 001 010 011 100 101 110 111
000 1 0 0 0 0 0 0 0
001 1 0 0 0 0 0 0 0
010 1 0 0 0 0 0 0 0
011 1 0 0 0 0 0 0 0
100 1 0 0 0 0 0 0 0
101 1 0 0 0 0 0 0 0
110 1 0 0 0 0 0 0 0
111 1 0 0 0 0 0 0 0
print(generate_error_matrix(n=3, p01=1, p10=1))
000 001 010 011 100 101 110 111
000 0 0 0 0 0 0 0 1
001 0 0 0 0 0 0 1 0
010 0 0 0 0 0 1 0 0
011 0 0 0 0 1 0 0 0
100 0 0 0 1 0 0 0 0
101 0 0 1 0 0 0 0 0
110 0 1 0 0 0 0 0 0
111 1 0 0 0 0 0 0 0
print(generate_error_matrix(n=3, p01=0.5, p10=0.5))
000 001 010 011 100 101 110 111
000 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
001 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
010 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
011 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
100 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
101 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
110 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
111 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125