$\newcommand{\Vt}[1]{\mathbf{#1}}$ $\newcommand{\Mt}[1]{\mathbf{#1}}$ $\newcommand{\vtA}{\Vt{A}}$ $\newcommand{\vtB}{\Vt{B}}$ $\newcommand{\vtC}{\Vt{C}}$ $\newcommand{\vtD}{\Vt{D}}$ $\newcommand{\vtE}{\Vt{E}}$ $\newcommand{\vtF}{\Vt{F}}$ $\newcommand{\vtG}{\Vt{G}}$ $\newcommand{\vtH}{\Vt{H}}$ $\newcommand{\vtI}{\Vt{I}}$ $\newcommand{\vtJ}{\Vt{J}}$ $\newcommand{\vtK}{\Vt{K}}$ $\newcommand{\vtL}{\Vt{L}}$ $\newcommand{\vtM}{\Vt{M}}$ $\newcommand{\vtN}{\Vt{N}}$ $\newcommand{\vtO}{\Vt{P}}$ $\newcommand{\vtP}{\Vt{P}}$ $\newcommand{\vtQ}{\Vt{Q}}$ $\newcommand{\vtR}{\Vt{R}}$ $\newcommand{\vtS}{\Vt{S}}$ $\newcommand{\vtT}{\Vt{T}}$ $\newcommand{\vtU}{\Vt{U}}$ $\newcommand{\vtV}{\Vt{V}}$ $\newcommand{\vtW}{\Vt{W}}$ $\newcommand{\vtX}{\Vt{X}}$ $\newcommand{\vtY}{\Vt{Y}}$ $\newcommand{\vtZ}{\Vt{Z}}$ $\newcommand{\mtA}{\Mt{A}}$ $\newcommand{\mtB}{\Mt{B}}$ $\newcommand{\mtC}{\Mt{C}}$ $\newcommand{\mtD}{\Mt{D}}$ $\newcommand{\mtE}{\Mt{E}}$ $\newcommand{\mtF}{\Mt{F}}$ $\newcommand{\mtG}{\Mt{G}}$ $\newcommand{\mtH}{\Mt{H}}$ $\newcommand{\mtI}{\Mt{I}}$ $\newcommand{\mtJ}{\Mt{J}}$ $\newcommand{\mtK}{\Mt{K}}$ $\newcommand{\mtL}{\Mt{L}}$ $\newcommand{\mtM}{\Mt{M}}$ $\newcommand{\mtN}{\Mt{N}}$ $\newcommand{\mtO}{\Mt{P}}$ $\newcommand{\mtP}{\Mt{P}}$ $\newcommand{\mtQ}{\Mt{Q}}$ $\newcommand{\mtR}{\Mt{R}}$ $\newcommand{\mtS}{\Mt{S}}$ $\newcommand{\mtT}{\Mt{T}}$ $\newcommand{\mtU}{\Mt{U}}$ $\newcommand{\mtV}{\Mt{V}}$ $\newcommand{\mtW}{\Mt{W}}$ $\newcommand{\mtX}{\Mt{X}}$ $\newcommand{\mtY}{\Mt{Y}}$ $\newcommand{\mtZ}{\Mt{Z}}$
This notebook illustrates some Interference Alignment algorithms.
The channel model used is the $K$-user MIMO IC channel, where we have $K$ pairs of transmitters and receivers and each transmitter sends useful information only to its intended receiver, while interfering with the other receivers. This is shown in the figure below
The received signal at the $i$-th receiver is given by
$$\vtY_i = \mtH_{ii}\mtP_i\vtX_i + \sum_{j=1,i \neq j}^{K}\mtH_{ij}\mtP_j \vtX_j + \vtN_i$$
In [1]:
# xxxxxxxxxx Add the parent folder to the python path. xxxxxxxxxxxxxxxxxxxx
import sys
import os
parent_dir = os.path.abspath("../")
sys.path.append(parent_dir)
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Imports
import numpy as np
from pyphysim.ia.ia import MaxSinrIASolver
from pyphysim.comm.channels import MultiUserChannelMatrix
In [2]:
# Parameters
K = 3
Nt = np.ones(K, dtype=int) * 2
Nr = np.ones(K, dtype=int) * 2
Ns = np.ones(K, dtype=int) * 1
# Transmit power of all users
#P = np.array([1.2, 1.5, 0.9])
P = np.array([1.0, 1.0, 1.0])
multiUserChannel = MultiUserChannelMatrix()
In [3]:
iasolver = MaxSinrIASolver(multiUserChannel)
# xxxxx Debug xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
np.random.seed(42) # Used in the generation of the random precoder
iasolver._multiUserChannel.set_channel_seed(324)
# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In [4]:
multiUserChannel.randomize(Nr, Nt, K)
iasolver.randomizeF(Ns, P)
iasolver._W = iasolver._calc_Uk_all_k()
In [5]:
H00 = multiUserChannel.H[0,0]
H01 = multiUserChannel.H[0,1]
H02 = multiUserChannel.H[0,2]
H10 = multiUserChannel.H[1,0]
H11 = multiUserChannel.H[1,1]
H12 = multiUserChannel.H[1,2]
H20 = multiUserChannel.H[2,0]
H21 = multiUserChannel.H[2,1]
H22 = multiUserChannel.H[2,2]
print "H00:\n{0}".format(H00)
print "H01:\n{0}".format(H01)
print "H02:\n{0}".format(H02)
print "H10:\n{0}".format(H10)
print "H11:\n{0}".format(H11)
print "H12:\n{0}".format(H12)
print "H20:\n{0}".format(H20)
print "H21:\n{0}".format(H21)
print "H22:\n{0}".format(H22)
In [6]:
# Different precoders: Fkl is column vector
F00 = iasolver.F[0][:, 0:1]
F10 = iasolver.F[1][:, 0:1]
F20 = iasolver.F[2][:, 0:1]
print "F00:\n{0}".format(F00)
print "F10:\n{0}".format(F10)
print "F20:\n{0}".format(F20)
Lets calculate the covariance matrices $\mtB^{[kl]}$
In [7]:
# Bkl for the different k and l
# k=0, l=0
second_part00 = np.dot(np.dot(np.dot(H00, F00), F00.transpose().conjugate()), H00.transpose().conjugate())
first_part00 = np.dot(np.dot(np.dot(H00, F00), F00.transpose().conjugate()), H00.transpose().conjugate()) + \
np.dot(np.dot(np.dot(H01, F10), F10.transpose().conjugate()), H01.transpose().conjugate()) + \
np.dot(np.dot(np.dot(H02, F20), F20.transpose().conjugate()), H02.transpose().conjugate())
B00 = first_part00 - second_part00 + np.eye(Nr[0])
print "B00:\n{0}".format(B00)
print
# k=1, l=0
second_part10 = np.dot(np.dot(np.dot(H11, F10), F10.transpose().conjugate()), H11.transpose().conjugate())
first_part10 = np.dot(np.dot(np.dot(H10, F00), F00.transpose().conjugate()), H10.transpose().conjugate()) + \
np.dot(np.dot(np.dot(H11, F10), F10.transpose().conjugate()), H11.transpose().conjugate()) + \
np.dot(np.dot(np.dot(H12, F20), F20.transpose().conjugate()), H12.transpose().conjugate())
B10 = first_part10 - second_part10 + np.eye(Nr[0])
print "B10:\n{0}".format(B10)
print
# k=2, l=0
second_part20 = np.dot(np.dot(np.dot(H22, F20), F20.transpose().conjugate()), H22.transpose().conjugate())
first_part20 = np.dot(np.dot(np.dot(H20, F00), F00.transpose().conjugate()), H20.transpose().conjugate()) + \
np.dot(np.dot(np.dot(H21, F10), F10.transpose().conjugate()), H21.transpose().conjugate()) + \
np.dot(np.dot(np.dot(H22, F20), F20.transpose().conjugate()), H22.transpose().conjugate())
B20 = first_part20 - second_part20 + np.eye(Nr[0])
print "B20:\n{0}".format(B20)
Lets calculate the combining vectors $\mtU^{[kl]}_{\star l}$
In [8]:
Uk_all_K = iasolver._calc_Uk_all_k()
# k = 0, l=0
U00 = np.dot(np.dot(np.linalg.inv(B00), H00), F00)
U00 = U00/np.linalg.norm(U00, 'fro')
print "U00:\n{0}".format(U00)
print "Uk_all_K[0]:\n{0}".format(Uk_all_K[0])
print
U10 = np.dot(np.dot(np.linalg.inv(B10), H11), F10)
U10 = U10/np.linalg.norm(U10, 'fro')
print "U10:\n{0}".format(U10)
print "Uk_all_K[1]:\n{0}".format(Uk_all_K[1])
print
U20 = np.dot(np.dot(np.linalg.inv(B20), H22), F20)
U20 = U20/np.linalg.norm(U20, 'fro')
print "U20:\n{0}".format(U20)
print "Uk_all_K[2]:\n{0}".format(Uk_all_K[2])
print
Now lets reverse the comunication
In [9]:
# The precoders correspond to the combining vectors
Fr00 = U00
Fr10 = U10
Fr20 = U20
# The channel Hkl correspods to Hlk^H
Hr00 = H00.transpose().conjugate()
Hr01 = H10.transpose().conjugate()
Hr02 = H20.transpose().conjugate()
Hr10 = H01.transpose().conjugate()
Hr11 = H11.transpose().conjugate()
Hr12 = H21.transpose().conjugate()
Hr20 = H02.transpose().conjugate()
Hr21 = H12.transpose().conjugate()
Hr22 = H22.transpose().conjugate()
Lets calculate the covariance matrices in the reversed network
In [10]:
# Bkl for the different k and l
# k=0, l=0
second_part_r_00 = np.dot(np.dot(np.dot(Hr00, Fr00), Fr00.transpose().conjugate()), Hr00.transpose().conjugate())
first_part_r_00 = np.dot(np.dot(np.dot(Hr00, Fr00), Fr00.transpose().conjugate()), Hr00.transpose().conjugate()) + \
np.dot(np.dot(np.dot(Hr01, Fr10), Fr10.transpose().conjugate()), Hr01.transpose().conjugate()) + \
np.dot(np.dot(np.dot(Hr02, Fr20), Fr20.transpose().conjugate()), Hr02.transpose().conjugate())
Br00 = first_part_r_00 - second_part_r_00 + np.eye(Nr[0])
print "Br00:\n{0}".format(Br00)
print
# k=1, l=0
second_part_r_10 = np.dot(np.dot(np.dot(Hr11, Fr10), Fr10.transpose().conjugate()), Hr11.transpose().conjugate())
first_part_r_10 = np.dot(np.dot(np.dot(Hr10, Fr00), Fr00.transpose().conjugate()), Hr10.transpose().conjugate()) + \
np.dot(np.dot(np.dot(Hr11, Fr10), Fr10.transpose().conjugate()), Hr11.transpose().conjugate()) + \
np.dot(np.dot(np.dot(Hr12, Fr20), Fr20.transpose().conjugate()), Hr12.transpose().conjugate())
Br10 = first_part_r_10 - second_part_r_10 + np.eye(Nr[0])
print "Br10:\n{0}".format(Br10)
print
# k=2, l=0
second_part_r_20 = np.dot(np.dot(np.dot(Hr22, Fr20), Fr20.transpose().conjugate()), Hr22.transpose().conjugate())
first_part_r_20 = np.dot(np.dot(np.dot(Hr20, Fr00), Fr00.transpose().conjugate()), Hr20.transpose().conjugate()) + \
np.dot(np.dot(np.dot(Hr21, Fr10), Fr10.transpose().conjugate()), Hr21.transpose().conjugate()) + \
np.dot(np.dot(np.dot(Hr22, Fr20), Fr20.transpose().conjugate()), Hr22.transpose().conjugate())
Br20 = first_part_r_20 - second_part_r_20 + np.eye(Nr[0])
print "Br20:\n{0}".format(Br20)
Calculate the combining vectors in the reverse network
In [11]:
Urk_all_K = iasolver._calc_Uk_all_k_rev()
# k = 0, l=0
Ur00 = np.dot(np.dot(np.linalg.inv(Br00), Hr00), Fr00)
Ur00 = Ur00/np.linalg.norm(Ur00, 'fro')
print "Ur00:\n{0}".format(Ur00)
print "Urk_all_K[0]:\n{0}".format(Urk_all_K[0])
print
Ur10 = np.dot(np.dot(np.linalg.inv(Br10), Hr11), Fr10)
Ur10 = Ur10/np.linalg.norm(Ur10, 'fro')
print "Ur10:\n{0}".format(Ur10)
print "Urk_all_K[1]:\n{0}".format(Urk_all_K[1])
print
Ur20 = np.dot(np.dot(np.linalg.inv(Br20), Hr22), Fr20)
Ur20 = Ur20/np.linalg.norm(Ur20, 'fro')
print "Ur20:\n{0}".format(Ur20)
print "Urk_all_K[2]:\n{0}".format(Urk_all_K[2])
print
Reverse the comunication again
In [12]:
iasolver._F = Urk_all_K
print "F00:\n{0}".format(iasolver.F[0])
print "F10:\n{0}".format(iasolver.F[1])
print "F20:\n{0}".format(iasolver.F[2])