Using the DeepMIMO Dataset with Sionna

In this example, you will learn how to use the ray-tracing based DeepMIMO dataset.

DeepMIMO is a generic dataset that enables a wide range of machine/deep learning applications for MIMO systems. It takes as input a set of parameters (such as antenna array configurations and time-domain/OFDM parameters) and generates MIMO channel realizations, corresponding locations, angles of arrival/departure, etc., based on these parameters and on a ray-tracing scenario selectedfrom those available in DeepMIMO.

Table of Contents

GPU Configuration and Imports

[1]:
importosifos.getenv("CUDA_VISIBLE_DEVICES")isNone:gpu_num=0# Use "" to use the CPUos.environ["CUDA_VISIBLE_DEVICES"]=f"{gpu_num}"os.environ['TF_CPP_MIN_LOG_LEVEL']='3'# Import Sionnatry:importsionna.phyexceptImportErrorase:importsysif'google.colab'insys.modules:# Install Sionna in Google Colabprint("Installing Sionna and restarting the runtime. Please run the cell again.")os.system("pip install sionna")os.kill(os.getpid(),5)else:raisee# Configure the notebook to use only a single GPU and allocate only as much memory as needed# For more details, see https://www.tensorflow.org/guide/gpuimporttensorflowastfgpus=tf.config.list_physical_devices('GPU')ifgpus:try:tf.config.experimental.set_memory_growth(gpus[0],True)exceptRuntimeErrorase:print(e)# Avoid warnings from TensorFlowtf.get_logger().setLevel('ERROR')sionna.phy.config.seed=42# Set seed for reproducible random number generation
[2]:
%matplotlib inlineimportmatplotlib.pyplotaspltimportnumpyasnp# Load the required Sionna componentsfromsionna.phyimportBlockfromsionna.phy.mimoimportStreamManagementfromsionna.phy.ofdmimportResourceGrid,ResourceGridMapper,LSChannelEstimator, \LMMSEEqualizer,RZFPrecoder,RemoveNulledSubcarriersfromsionna.phy.channelimportsubcarrier_frequencies,ApplyOFDMChannel, \GenerateOFDMChannel,CIRDatasetfromsionna.phy.fec.ldpcimportLDPC5GEncoder,LDPC5GDecoderfromsionna.phy.mappingimportBinarySource,Mapper,Demapperfromsionna.phy.utilsimportebnodb2no,sim_ber

Configuration of DeepMIMO

DeepMIMO provides multiplescenarios that one can select from. In this example, we use the O1 scenario with the carrier frequency set to 60 GHz (O1_60). To run this example, please download the “O1_60” data filesfrom this page. The downloaded zip file should be extracted into a folder, and the parameterDeepMIMO_params['dataset_folder'] should be set to point to this folder, as done below.

To use DeepMIMO with Sionna, the DeepMIMO dataset first needs to be generated. The generated DeepMIMO dataset contains channels for different locations of the users and basestations. The layout of the O1 scenario is shown in the figure below.

O1_top_view

In this example, we generate a dataset that consists of channels for the links from the basestation 6 to the users located on the rows 400 to 450. Each of these rows consists of 181 user locations, resulting in\(51 \times 181 = 9231\) basestation-user channels.

The antenna arrays in the DeepMIMO dataset are defined through the x-y-z axes. In the following example, a single-user MISO downlink is considered. The basestation is equipped with a uniform linear array of 16 elements spread along the x-axis. The users are each equipped with a single antenna. These parameters can be configured using the code below (for more information about the DeepMIMO parameters, please checkthe DeepMIMO configurations).

[3]:
# Import DeepMIMOtry:importDeepMIMOexceptImportErrorase:# Install DeepMIMO if package is not already installedimportosos.system("pip install DeepMIMO")importDeepMIMO# Channel generationDeepMIMO_params=DeepMIMO.default_params()# Load the default parametersDeepMIMO_params['dataset_folder']=r'./scenarios'# Path to the downloaded scenariosDeepMIMO_params['scenario']='O1_60'# DeepMIMO scenarioDeepMIMO_params['num_paths']=10# Maximum number of pathsDeepMIMO_params['active_BS']=np.array([6])# Basestation indices to be included in the dataset# Selected rows of users, whose channels are to be generated.DeepMIMO_params['user_row_first']=400# First user row to be included in the datasetDeepMIMO_params['user_row_last']=450# Last user row to be included in the dataset# Configuration of the antenna arraysDeepMIMO_params['bs_antenna']['shape']=np.array([16,1,1])# BS antenna shape through [x, y, z] axesDeepMIMO_params['ue_antenna']['shape']=np.array([1,1,1])# UE antenna shape through [x, y, z] axes# The OFDM_channels parameter allows choosing between the generation of channel impulse# responses (if set to 0) or frequency domain channels (if set to 1).# It is set to 0 for this simulation, as the channel responses in frequency domain# will be generated using Sionna.DeepMIMO_params['OFDM_channels']=0# Generates a DeepMIMO datasetDeepMIMO_dataset=DeepMIMO.generate_data(DeepMIMO_params)
Collecting DeepMIMO  Downloading DeepMIMO-1.0-py3-none-any.whl.metadata (421 bytes)Requirement already satisfied: numpy in /usr/local/lib/python3.10/site-packages (from DeepMIMO) (1.26.4)Requirement already satisfied: scipy in /usr/local/lib/python3.10/site-packages (from DeepMIMO) (1.15.2)Collecting tqdm (from DeepMIMO)  Downloading tqdm-4.67.1-py3-none-any.whl.metadata (57 kB)Downloading DeepMIMO-1.0-py3-none-any.whl (21 kB)Downloading tqdm-4.67.1-py3-none-any.whl (78 kB)Installing collected packages: tqdm, DeepMIMOSuccessfully installed DeepMIMO-1.0 tqdm-4.67.1Basestation 6UE-BS ChannelsBS-BS Channels

Visualization of the dataset

To provide a better understanding of the user and basestation locations, we next visualize the locations of the users, highlighting the first active row of users (row 400), and basestation 6.

[4]:
plt.figure(figsize=(12,8))## User locationsactive_bs_idx=0# Select the first active basestation in the datasetplt.scatter(DeepMIMO_dataset[active_bs_idx]['user']['location'][:,1],# y-axis location of the usersDeepMIMO_dataset[active_bs_idx]['user']['location'][:,0],# x-axis location of the userss=1,marker='x',c='C0',label='The users located on the rows%i to%i (R%i to R%i)'%(DeepMIMO_params['user_row_first'],DeepMIMO_params['user_row_last'],DeepMIMO_params['user_row_first'],DeepMIMO_params['user_row_last']))# First 181 users correspond to the first rowplt.scatter(DeepMIMO_dataset[active_bs_idx]['user']['location'][0:181,1],DeepMIMO_dataset[active_bs_idx]['user']['location'][0:181,0],s=1,marker='x',c='C1',label='First row of users (R%i)'%(DeepMIMO_params['user_row_first']))## Basestation locationplt.scatter(DeepMIMO_dataset[active_bs_idx]['location'][1],DeepMIMO_dataset[active_bs_idx]['location'][0],s=50.0,marker='o',c='C2',label='Basestation')plt.gca().invert_xaxis()# Invert the x-axis to align the figure with the figure aboveplt.ylabel('x-axis')plt.xlabel('y-axis')plt.grid()plt.legend();
../../_images/phy_tutorials_DeepMIMO_9_0.png

Using DeepMIMO with Sionna

The DeepMIMO Python package providesa Sionna-compliant channel impulse response generator that adapts the structure of the DeepMIMO dataset to be consistent with Sionna.

An adapter is instantiated for a given DeepMIMO dataset. In addition to the dataset, the adapter takes the indices of the basestations and users, to generate the channels between these basestations and users:

DeepMIMOSionnaAdapter(DeepMIMO_dataset,bs_idx,ue_idx)

Note:bs_idx andue_idx set the links from which the channels are drawn. For instance, ifbs_idx=[0,1] andue_idx=[2,3], the adapter then outputs the 4 channels formed by the combination of the first and second basestations with the third and fourth users.

The default behavior forbs_idx andue_idx are defined as follows:

  • If value forbs_idx is not given, it will be set to[0] (i.e., the first basestation in theDeepMIMO_dataset).

  • If value forue_idx is not given, then channels are provided for the links between thebs_idx and all users (i.e.,ue_idx=range(len(DeepMIMO_dataset[0]['user']['channel'])).

  • If the bothbs_idx andue_idx are not given, the channels between the first basestation and all the users are provided by the adapter. For this example,DeepMIMOSionnaAdapter(DeepMIMO_dataset) returns the channels from the basestation 6 and the 9231 available user locations.

Note: The adapter assumes basestations are transmitters and users are receivers. Uplink channels can be obtained using (transpose) reciprocity.

Random Sampling of Multi-User Channels

When considering multiple basestations,bs_idx can be set to a 2D numpy matrix of shape\((\) # of samples\(\times\) # of basestations per sample\()\). In this case, for each sample of basestations, theDeepMIMOSionnaAdapter returns a set of\((\) # of basestations per sample\(\times\) # of users\()\) channels, which can be provided as a multi-transmitter sample for the Sionna model. For example,bs_idx=np.array([[0,1],[2,3],[4,5]]) provides threesets of\((\) 2 basestations\(\times\) # of users\()\) channels. These three channel sets are from the basestation sets[0,1],[2,3], and[4,5], respectively, to the users.

To use the adapter for multi-user channels,ue_idx can be set to a 2D numpy matrix of shape\((\) # of samples\(\times\) # of users per sample\()\). In this case, for each sample of users, theDeepMIMOSionnaAdapter returns a set of\((\) # of basestations\(\times\) # of users per sample\()\) channels, which can be provided as a multi-receiver sample for the Sionna model. For example,ue_idx=np.array([[0,1,2],[4,5,6]]) provides two sets of\((\)# of basestations\(\times\) 3 users\()\) channels. These two channel sets are from the basestations to the user sets[0,1,2] and[4,5,6], respectively.

In order to randomly sample channels from all the available user locations consideringnum_rx users, one may setue_idx as in the following cell. In this example, the channels will be randomly chosen from the links between the basestation 6 and the 9231 available user locations.

[5]:
fromDeepMIMOimportDeepMIMOSionnaAdapter# Number of receivers for the Sionna model.# MISO is considered here.num_rx=1# The number of UE locations in the generated DeepMIMO datasetnum_ue_locations=len(DeepMIMO_dataset[0]['user']['channel'])# 9231# Pick the largest possible number of user locations that is a multiple of ``num_rx``ue_idx=np.arange(num_rx*(num_ue_locations//num_rx))# Optionally shuffle the dataset to not select only users that are near each othersnp.random.shuffle(ue_idx)# Reshape to fit the requested number of usersue_idx=np.reshape(ue_idx,[-1,num_rx])# In the shape of (floor(9231/num_rx) x num_rx)DeepMIMO_Sionna_adapter=DeepMIMOSionnaAdapter(DeepMIMO_dataset,ue_idx=ue_idx)

Link-level Simulations using Sionna and DeepMIMO

In the following cell, we define a Sionna model implementing the end-to-end link.

Note: The Sionna CIRDataset object shuffles the DeepMIMO channels provided by the adapter. Therefore, channel samples are passed through the model in a random order.

[6]:
classLinkModel(Block):def__init__(self,DeepMIMO_Sionna_adapter,carrier_frequency,cyclic_prefix_length,pilot_ofdm_symbol_indices,subcarrier_spacing=60e3,batch_size=64):super().__init__()self._batch_size=batch_sizeself._cyclic_prefix_length=cyclic_prefix_lengthself._pilot_ofdm_symbol_indices=pilot_ofdm_symbol_indices# CIRDataset to parse the datasetself._CIR=CIRDataset(DeepMIMO_Sionna_adapter,self._batch_size,DeepMIMO_Sionna_adapter.num_rx,DeepMIMO_Sionna_adapter.num_rx_ant,DeepMIMO_Sionna_adapter.num_tx,DeepMIMO_Sionna_adapter.num_tx_ant,DeepMIMO_Sionna_adapter.num_paths,DeepMIMO_Sionna_adapter.num_time_steps)# System parametersself._carrier_frequency=carrier_frequencyself._subcarrier_spacing=subcarrier_spacingself._fft_size=76self._num_ofdm_symbols=14self._num_streams_per_tx=DeepMIMO_Sionna_adapter.num_rxself._dc_null=Falseself._num_guard_carriers=[0,0]self._pilot_pattern="kronecker"self._pilot_ofdm_symbol_indices=pilot_ofdm_symbol_indicesself._num_bits_per_symbol=4self._coderate=0.5# Setup the OFDM resource grid and stream managementself._sm=StreamManagement(np.ones([DeepMIMO_Sionna_adapter.num_rx,1],int),self._num_streams_per_tx)self._rg=ResourceGrid(num_ofdm_symbols=self._num_ofdm_symbols,fft_size=self._fft_size,subcarrier_spacing=self._subcarrier_spacing,num_tx=DeepMIMO_Sionna_adapter.num_tx,num_streams_per_tx=self._num_streams_per_tx,cyclic_prefix_length=self._cyclic_prefix_length,num_guard_carriers=self._num_guard_carriers,dc_null=self._dc_null,pilot_pattern=self._pilot_pattern,pilot_ofdm_symbol_indices=self._pilot_ofdm_symbol_indices)# Components forming the link# Codeword lengthself._n=int(self._rg.num_data_symbols*self._num_bits_per_symbol)# Number of information bits per codewordself._k=int(self._n*self._coderate)# OFDM channelself._frequencies=subcarrier_frequencies(self._rg.fft_size,self._rg.subcarrier_spacing)self._ofdm_channel=GenerateOFDMChannel(self._CIR,self._rg,normalize_channel=True)self._channel_freq=ApplyOFDMChannel(add_awgn=True)# Transmitterself._binary_source=BinarySource()self._encoder=LDPC5GEncoder(self._k,self._n)self._mapper=Mapper("qam",self._num_bits_per_symbol)self._rg_mapper=ResourceGridMapper(self._rg)self._zf_precoder=RZFPrecoder(self._rg,self._sm,return_effective_channel=True)# Receiverself._ls_est=LSChannelEstimator(self._rg,interpolation_type="lin_time_avg")self._lmmse_equ=LMMSEEqualizer(self._rg,self._sm)self._demapper=Demapper("app","qam",self._num_bits_per_symbol)self._decoder=LDPC5GDecoder(self._encoder,hard_out=True)self._remove_nulled_scs=RemoveNulledSubcarriers(self._rg)defcall(self,batch_size,ebno_db):# Transmitterb=self._binary_source([self._batch_size,1,self._num_streams_per_tx,self._k])c=self._encoder(b)x=self._mapper(c)x_rg=self._rg_mapper(x)# Generate the OFDM channelh_freq=self._ofdm_channel()# Precodingx_rg,g=self._zf_precoder(x_rg,h_freq)# Apply OFDM channelno=ebnodb2no(ebno_db,self._num_bits_per_symbol,self._coderate,self._rg)y=self._channel_freq(x_rg,h_freq,no)# Receiverh_hat,err_var=self._ls_est(y,no)x_hat,no_eff=self._lmmse_equ(y,h_hat,err_var,no)llr=self._demapper(x_hat,no_eff)b_hat=self._decoder(llr)returnb,b_hat

We next evaluate the setup with different\(E_b/N_0\) values to obtain BLER curves.

[7]:
sim_params={"ebno_db":np.linspace(-7,-5.25,10),"cyclic_prefix_length":0,"pilot_ofdm_symbol_indices":[2,11],}batch_size=64model=LinkModel(DeepMIMO_Sionna_adapter=DeepMIMO_Sionna_adapter,carrier_frequency=DeepMIMO_params['scenario_params']['carrier_freq'],cyclic_prefix_length=sim_params["cyclic_prefix_length"],pilot_ofdm_symbol_indices=sim_params["pilot_ofdm_symbol_indices"])ber,bler=sim_ber(model,sim_params["ebno_db"],batch_size=batch_size,max_mc_iter=100,num_target_block_errors=100,graph_mode="graph")
EbNo [dB] |        BER |       BLER |  bit errors |    num bits | block errors |  num blocks | runtime [s] |    status---------------------------------------------------------------------------------------------------------------------------------------     -7.0 | 1.2337e-01 | 1.0000e+00 |       28803 |      233472 |          128 |         128 |         7.4 |reached target block errors   -6.806 | 9.1728e-02 | 9.9219e-01 |       21416 |      233472 |          127 |         128 |         0.1 |reached target block errors   -6.611 | 6.0393e-02 | 9.3750e-01 |       14100 |      233472 |          120 |         128 |         0.1 |reached target block errors   -6.417 | 2.7489e-02 | 7.6562e-01 |        9627 |      350208 |          147 |         192 |         0.1 |reached target block errors   -6.222 | 6.6111e-03 | 3.9844e-01 |        3087 |      466944 |          102 |         256 |         0.2 |reached target block errors   -6.028 | 9.2416e-04 | 9.9265e-02 |        1834 |     1984512 |          108 |        1088 |         0.9 |reached target block errors   -5.833 | 7.8896e-05 | 1.3594e-02 |         921 |    11673600 |           87 |        6400 |         5.1 |reached max iterations   -5.639 | 9.8513e-06 | 2.0313e-03 |         115 |    11673600 |           13 |        6400 |         5.2 |reached max iterations   -5.444 | 4.2832e-07 | 1.5625e-04 |           5 |    11673600 |            1 |        6400 |         5.2 |reached max iterations    -5.25 | 0.0000e+00 | 0.0000e+00 |           0 |    11673600 |            0 |        6400 |         5.1 |reached max iterationsSimulation stopped as no error occurred @ EbNo = -5.2 dB.
[8]:
plt.figure(figsize=(12,8))plt.xlabel(r"$E_b/N_0$ (dB)")plt.ylabel("BLER")plt.grid(which="both")plt.semilogy(sim_params["ebno_db"],bler)
[8]:
[<matplotlib.lines.Line2D at 0x7f541c4572e0>]
../../_images/phy_tutorials_DeepMIMO_16_1.png

DeepMIMO License and Citation

  1. Alkhateeb, “DeepMIMO: A Generic Deep Learning Dataset for Millimeter Wave and Massive MIMO Applications,” in Proc. of Information Theory and Applications Workshop (ITA), San Diego, CA, Feb. 2019.

To use the DeepMIMO dataset, please check the license informationhere.