def calc_post_processing_SINRs( channel: np.ndarray, W: np.ndarray, G_H: np.ndarray, noise_var: Optional[float] = None) -> np.ndarray: """ Calculate the post processing SINRs (in dB) of all streams for a given MIMO scheme. Parameters ---------- channel : np.ndarray The MIMO channel. This should be a 2D numpy array. W : np.ndarray The precoder for the MIMO scheme. This should be a 2D numpy array. G_H : np.ndarray The receive filter for the MIMO scheme. This should be a 2D numpy array. noise_var : float The noise variance Returns ------- np.ndarray The SINR of all streams (in linear scale). """ return linear2dB( calc_post_processing_linear_SINRs(channel, W, G_H, noise_var))
def simulate_for_a_given_ap_assoc(pl, ap_assoc, wall_losses_dB, Pt, noise_var): """ Simulate and return the SINR for a given path loss and AP associations. This is an internal function called inside `perform_simulation_SINR_heatmap` Parameters ---------- pl : 5D numpy float array The path loss (in LINEAR SCALE) from each discrete position in each room to each access point. Dimension: (n, n, d, d, a) where 'n' is the number of rooms per dimension, 'd' is the number of discrete positions in one room (per dimension) and 'a' is the number of access points. ap_assoc : 4D numpy int array The index of the access point that each discrete point in each room is associated with. Dimension: (n, n, d, d) wall_losses_dB : 5D numpy int array The wall losses (in dB) from each discrete user in each room to each access point. Dimension: (n, n, d, d, a) Pt : float Transmit power. noise_var : float Noise variance (power) Returns ------- sinr_array_dB : 4D numpy array The SINR (in dB) of each discrete point of each room. """ wall_losses = dB2Linear(-wall_losses_dB) # Number of APs is the last dimension in the path loss array num_aps = pl.shape[-1] # Output variable sinr_array = np.empty(ap_assoc.shape, dtype=float) for ap_idx in range(num_aps): # Mask of the users associated with the current access point mask = (ap_assoc == ap_idx) # # Mask of the users NOT associated with the current access point # mask_n = np.logical_not(mask) # Mask with all APs except the current one (that is, the # interfering APs) mask_i_aps = np.arange(num_aps) != ap_idx # Each element in desired_power is the desired power of one user # associated with the current access point desired_power = Pt * wall_losses[mask, ap_idx] * pl[mask, ap_idx] undesired_power = np.sum(Pt * wall_losses[mask][:, mask_i_aps] * pl[mask][:, mask_i_aps], axis=-1) sinr_array[mask] = (desired_power / (undesired_power + noise_var)) return linear2dB(sinr_array)
def simulate_for_a_given_ap_assoc(pl, ap_assoc, wall_losses_dB, Pt, noise_var): """ Simulate and return the SINR for a given path loss and AP associations. This is an internal function called inside `perform_simulation_SINR_heatmap` Parameters ---------- pl : 5D numpy float array The path loss (in LINEAR SCALE) from each discrete position in each room to each access point. Dimension: (n, n, d, d, a) where 'n' is the number of rooms per dimension, 'd' is the number of discrete positions in one room (per dimension) and 'a' is the number of access points. ap_assoc : 4D numpy int array The index of the access point that each discrete point in each room is associated with. Dimension: (n, n, d, d) wall_losses_dB : 5D numpy int array The wall losses (in dB) from each discrete user in each room to each access point. Dimension: (n, n, d, d, a) Pt : float Transmit power. noise_var : float Noise variance (power) Returns ------- sinr_array_dB : 4D numpy array The SINR (in dB) of each discrete point of each room. """ wall_losses = dB2Linear(-wall_losses_dB) # Number of APs is the last dimension in the path loss array num_aps = pl.shape[-1] # Output variable sinr_array = np.empty(ap_assoc.shape, dtype=float) for ap_idx in range(num_aps): # Mask of the users associated with the current access point mask = ap_assoc == ap_idx # # Mask of the users NOT associated with the current access point # mask_n = np.logical_not(mask) # Mask with all APs except the current one (that is, the # interfering APs) mask_i_aps = np.arange(num_aps) != ap_idx # Each element in desired_power is the desired power of one user # associated with the current access point desired_power = Pt * wall_losses[mask, ap_idx] * pl[mask, ap_idx] undesired_power = np.sum(Pt * wall_losses[mask][:, mask_i_aps] * pl[mask][:, mask_i_aps], axis=-1) sinr_array[mask] = desired_power / (undesired_power + noise_var) return linear2dB(sinr_array)
def simulate_for_a_given_ap_assoc(pl_plus_wl_tx_aps, ap_assoc, transmitting_aps, Pt, noise_var): """ Perform the simulation for a given AP association. Parameters ---------- pl_plus_wl_tx_aps : np.ndarray ap_assoc : np.ndarray transmitting_aps : np.ndarray Pt : float | np.ndarray noise_var : float The noise variance. Returns ------- (np.ndarray, np.ndarray) A tuple with the SINRs and the capacity. """ # Output variables sinr_array = np.empty(ap_assoc.shape, dtype=float) capacity = np.empty(ap_assoc.shape, dtype=float) num_users, = ap_assoc.shape # For each transmitting AP for index, ap in enumerate(transmitting_aps): # 'ap' is the index of the current AP in the list of all APs # (including the non transmitting APs), while 'index' is the index # or the current AP in transmitting_aps # Index of the users associated with the current AP current_ap_users_idx = np.arange(num_users)[ap_assoc == ap] # Mask to get the interfering APs mask_i_aps = np.ones(len(transmitting_aps), dtype=bool) mask_i_aps[index] = False # Desired power of these users desired_power = (Pt * pl_plus_wl_tx_aps[current_ap_users_idx, index]) undesired_power = np.sum( Pt * pl_plus_wl_tx_aps[current_ap_users_idx][:, mask_i_aps], axis=-1) sinr_array[current_ap_users_idx] = (desired_power / (undesired_power + noise_var)) # The capacity (actually, the spectral efficiency since we didn't # multiply by the bandwidth) is calculated from the SINR. However, # if there is more then one user associated with the current AP we # assume bandwidth will be equally divided among all of them. capacity[current_ap_users_idx] = ( np.log2(1 + sinr_array[current_ap_users_idx]) / len(current_ap_users_idx)) return linear2dB(sinr_array), capacity
def plot_true_and_estimated_channel_with_bokeh(true_channel, estimated_channel, title='', antenna=0): true_channel_time = np.fft.ifft(true_channel[:, antenna], axis=0) num_subcarriers = true_channel.shape[0] data = { 'index': np.r_[0:num_subcarriers], 'channel_time': np.abs(true_channel_time), 'channel': np.abs(true_channel[:, antenna]), 'estimated_channel': np.abs(estimated_channel[:, antenna]), 'error': linear2dB( np.abs(true_channel[:, antenna] - estimated_channel[:, antenna]) / \ np.abs(true_channel[:, antenna]))} source00 = bp.ColumnDataSource(data=data) # Specify the tools by name. This does not allow us to set parameters # for the tools TOOLS = "pan,wheel_zoom,box_zoom,reset,resize,crosshair" # Create a hover tool and tell it to only show for a curve with name # 'estimated' hover_tool = HoverTool(names=['estimated'], tooltips=[('True Channel', ''), ('Error (in dB)', '@error')]) # p1 = bp.figure(tools=TOOLS, width=600, height=200) # p1.circle('index', 'channel_time', source=source00) # p1.title = title p2 = bp.figure(tools=TOOLS, plot_width=400, plot_height=300) p2.add_tools(hover_tool) p2.line('index', 'channel', source=source00, legend='True Channel') # We specify the 'name' attribute so that we can specify the 'names' # attribute for the hover tool and tell it to only show for the # estimated channel curve. p2.line('index', 'estimated_channel', source=source00, color='red', name='estimated', legend='Estimated Channel') p2.title = title # p = bp.vplot(p1, p2) # return p return p2
def calc_SINRs(self, noise_var): """ Calculate the SINRs (in dB) of the multiple streams. Parameters ---------- noise_var : float The noise variance. Returns ------- SINRs : np.ndarray The SINRs (in dB) of the multiple streams. """ return linear2dB(self.calc_linear_SINRs(noise_var))
def calc_SINRs(self, noise_var): """ Calculate the SINRs (in dB) of the multiple streams. Parameters ---------- noise_var : float The noise variance. Returns ------- SINRs : 1D numpy array The SINRs (in dB) of the multiple streams. """ return linear2dB(self.calc_linear_SINRs(noise_var))
def which_distance(self, pl): # pragma: no cover """ Calculates the required distance (in meters) to achieve the given path loss. It is the inverse of the calc_path_loss function. Parameters ---------- pl : float or numpy array Path loss (in linear scale). Returns ------- d : float or numpy array Distance(s) that will yield the path loss `pl`. """ d = self.which_distance_dB(-conversion.linear2dB(pl)) return d
def test_calc_post_processing_SINRs(self): Nr = 1 Nt = 2 noise_var = 0.01 channel = randn_c(Nr, Nt) self.alamouti_object.set_channel_matrix(channel) # W = self.alamouti_object._calc_precoder(channel) # G_H = self.alamouti_object._calc_receive_filter(channel, noise_var) expected_sinrs = linear2dB( (np.linalg.norm(channel, 'fro')**2)/noise_var) # Calculate the SINR using method in the Alamouti class. Note that # we only need to pass the noise variance, since the mimo object # knows the channel. sinrs = self.alamouti_object.calc_SINRs(noise_var) np.testing.assert_array_almost_equal(sinrs, expected_sinrs, 2)
def plot_true_and_estimated_channel_with_bokeh( true_channel, estimated_channel, title='', antenna=0): true_channel_time = np.fft.ifft(true_channel[:, antenna], axis=0) num_subcarriers = true_channel.shape[0] data = { 'index': np.r_[0:num_subcarriers], 'channel_time': np.abs(true_channel_time), 'channel': np.abs(true_channel[:,antenna]), 'estimated_channel':np.abs(estimated_channel[:,antenna]), 'error':linear2dB( np.abs(true_channel[:,antenna] - estimated_channel[:,antenna]) / \ np.abs(true_channel[:,antenna]))} source00 = bp.ColumnDataSource(data=data) # Specify the tools by name. This does not allow us to set parameters # for the tools TOOLS = "pan,wheel_zoom,box_zoom,reset,resize,crosshair" # Create a hover tool and tell it to only show for a curve with name # 'estimated' hover_tool = HoverTool( names=['estimated'], tooltips = [('True Channel', ''), ('Error (in dB)', '@error')]) # p1 = bp.figure(tools=TOOLS, width=600, height=200) # p1.circle('index', 'channel_time', source=source00) # p1.title = title p2 = bp.figure(tools=TOOLS, plot_width=400, plot_height=300) p2.add_tools(hover_tool) p2.line('index', 'channel', source=source00, legend='True Channel') # We specify the 'name' attribute so that we can specify the 'names' # attribute for the hover tool and tell it to only show for the # estimated channel curve. p2.line('index', 'estimated_channel', source=source00, color='red', name='estimated', legend='Estimated Channel') p2.title = title # p = bp.vplot(p1, p2) # return p return p2
def test_calc_post_processing_SINRs(self): Nr = 3 Nt = 3 noise_var = 0.01 channel = randn_c(Nr, Nt) self.svdmimo_object.set_channel_matrix(channel) W = self.svdmimo_object._calc_precoder(channel) G_H = self.svdmimo_object._calc_receive_filter(channel, noise_var) expected_sinrs = linear2dB(calc_SINRs(channel, W, G_H, noise_var)) # Calculate the SINR using the function in the mimo module. Note # that we need to pass the channel, the precoder, the receive # filter and the noise variance. sinrs = calc_post_processing_SINRs(channel, W, G_H, noise_var) np.testing.assert_array_almost_equal(sinrs, expected_sinrs, 2) # Calculate the SINR using method in the MIMO class. Note that we # only need to pass the noise variance, since the mimo object knows # the channel and it can calculate the precoder and receive filter. sinrs_other = self.svdmimo_object.calc_linear_SINRs(noise_var) np.testing.assert_array_almost_equal(sinrs_other, expected_sinrs, 2)
def calc_post_processing_SINRs(channel, W, G_H, noise_var=None): """ Calculate the post processing SINRs (in dB) of all streams for a given MIMO scheme. Parameters ---------- channel : 2D numpy array The MIMO channel. W : 2D numpy array The precoder for the MIMO scheme. G_H : 2D numpy array The receive filter for the MIMO scheme. noise_var : float The noise variance Returns ------- sinrs : 1D numpy array The SINR of all streams (in linear scale). """ return linear2dB( calc_post_processing_linear_SINRs(channel, W, G_H, noise_var))
def get_ebn0_vec(results): """ Get the Eb/N0 vector suitable for the plot. Parameters ---------- results : A SimulationResults object The results from a simulation. """ SNR = np.array(results.params['SNR']) modulator = results.params['modulator'] if modulator == 'BPSK': K = 1 elif modulator == 'QPSK': K = 2 else: M = results.params['M'] K = np.round(np.log2(M)) ebn0 = SNR + linear2dB(1./K) return ebn0
def simulate_for_a_given_ap_assoc(pl_plus_wl_tx_aps, ap_assoc, transmitting_aps, Pt, noise_var): """ Perform the simulation for a given AP association. """ # Output variables sinr_array = np.empty(ap_assoc.shape, dtype=float) capacity = np.empty(ap_assoc.shape, dtype=float) num_users, = ap_assoc.shape # For each transmitting AP for index, ap in enumerate(transmitting_aps): # 'ap' is the index of the current AP in the list of all APs # (including the non transmitting APs), while 'index' is the index # or the current AP in transmitting_aps # Index of the users associated with the current AP current_ap_users_idx = np.arange(num_users)[ap_assoc == ap] # Mask to get the interfering APs mask_i_aps = np.ones(len(transmitting_aps), dtype=bool) mask_i_aps[index] = False # Desired power of these users desired_power = Pt * pl_plus_wl_tx_aps[current_ap_users_idx, index] undesired_power = np.sum(Pt * pl_plus_wl_tx_aps[current_ap_users_idx][:, mask_i_aps], axis=-1) sinr_array[current_ap_users_idx] = desired_power / (undesired_power + noise_var) # The capacity (actually, the spectral efficiency since we didn't # multiply by the bandwidth) is calculated from the SINR. However, # if there is more then one user associated with the current AP we # assume bandwidth will be equally divided among all of them. capacity[current_ap_users_idx] = np.log2(1 + sinr_array[current_ap_users_idx]) / len(current_ap_users_idx) return linear2dB(sinr_array), capacity
def test_linear2dB(self): self.assertAlmostEqual(conversion.linear2dB(1000), 30.0)
def compute_channel_estimation_error_dB(H, Hest): return np.mean(linear2dB(np.abs(Hest - H) / np.abs(H)))
def test_block_diagonalize_no_waterfilling(self): Nr = np.array([2, 2]) Nt = np.array([2, 2]) K = Nt.size Nti = 1 iPu = 1e-1 # Power for each user (linear scale) pe = 1e-3 # External interference power (in linear scale) noise_var = 1e-1 # The modulator and packet_length are required in the # effective_throughput metric case psk_obj = fundamental.PSK(4) packet_length = 120 multiUserChannel = multiuser.MultiUserChannelMatrixExtInt() multiUserChannel.randomize(Nr, Nt, K, Nti) multiUserChannel.noise_var = noise_var # Channel from all transmitters to the first receiver H1 = multiUserChannel.get_Hk_without_ext_int(0) # Channel from all transmitters to the second receiver H2 = multiUserChannel.get_Hk_without_ext_int(1) # Create the enhancedBD object enhancedBD_obj = blockdiagonalization.EnhancedBD(K, iPu, noise_var, pe) noise_plus_int_cov_matrix \ = multiUserChannel.calc_cov_matrix_extint_plus_noise(pe) # xxxxx First we test without ext. int. handling xxxxxxxxxxxxxxxxxx enhancedBD_obj.set_ext_int_handling_metric(None) (Ms_all, Wk_all, Ns_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) Ms1 = Ms_all[0] Ms2 = Ms_all[1] self.assertEqual(Ms1.shape[1], Ns_all[0]) self.assertEqual(Ms2.shape[1], Ns_all[1]) # Most likelly only one base station (the one with the worst # channel) will employ a precoder with total power of `Pu`, # while the other base stations will use less power. tol = 1e-10 self.assertGreaterEqual(iPu + tol, np.linalg.norm(Ms1, 'fro') ** 2) # 1e-12 is included to avoid false test fails due to small # precision errors self.assertGreaterEqual(iPu + tol, np.linalg.norm(Ms2, 'fro') ** 2) # Test if the precoder block diagonalizes the channel self.assertNotAlmostEqual(np.linalg.norm(np.dot(H1, Ms1), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H1, Ms2), 'fro'), 0) self.assertNotAlmostEqual(np.linalg.norm(np.dot(H2, Ms2), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H2, Ms1), 'fro'), 0) # Equivalent sinrs (in linear scale) sinrs = np.empty(K, dtype=np.ndarray) sinrs[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, Ms1), Wk_all[0], noise_plus_int_cov_matrix[0]) sinrs[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, Ms2), Wk_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency se = ( np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs[0]), packet_length)) + np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Now with the Naive Stream Reduction xxxxxxxxxxxxxxxxxxxxxxx num_streams = 1 enhancedBD_obj.set_ext_int_handling_metric( 'naive', {'num_streams': num_streams}) (MsPk_naive_all, Wk_naive_all, Ns_naive_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_naive_1 = MsPk_naive_all[0] MsPk_naive_2 = MsPk_naive_all[1] self.assertEqual(MsPk_naive_1.shape[1], Ns_naive_all[0]) self.assertEqual(MsPk_naive_2.shape[1], Ns_naive_all[1]) self.assertEqual(Ns_naive_all[0], num_streams) self.assertEqual(Ns_naive_all[1], num_streams) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_naive_1, 'fro') ** 2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_naive_2, 'fro') ** 2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_naive_1), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_naive_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_naive_2), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_naive_1), 'fro'), 0) sinrs4 = np.empty(K, dtype=np.ndarray) sinrs4[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_naive_1), Wk_naive_all[0], noise_plus_int_cov_matrix[0]) sinrs4[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_naive_2), Wk_naive_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency se4 = ( np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs4[0]), packet_length)) + np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs4[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Now with the Fixed Stream Reduction xxxxxxxxxxxxxxxxxxxxxxx # The 'fixed' metric requires that metric_func_extra_args_dict is # provided and has the 'num_streams' key. If this is not the case # an exception is raised with self.assertRaises(AttributeError): enhancedBD_obj.set_ext_int_handling_metric('fixed') # Now let's test the fixed metric num_streams = 1 enhancedBD_obj.set_ext_int_handling_metric( 'fixed', {'num_streams': num_streams}) (MsPk_fixed_all, Wk_fixed_all, Ns_fixed_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_fixed_1 = MsPk_fixed_all[0] MsPk_fixed_2 = MsPk_fixed_all[1] self.assertEqual(MsPk_fixed_1.shape[1], Ns_fixed_all[0]) self.assertEqual(MsPk_fixed_2.shape[1], Ns_fixed_all[1]) self.assertEqual(Ns_fixed_all[0], num_streams) self.assertEqual(Ns_fixed_all[1], num_streams) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_fixed_1, 'fro') ** 2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_fixed_2, 'fro') ** 2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_fixed_1), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_fixed_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_fixed_2), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_fixed_1), 'fro'), 0) sinrs5 = np.empty(K, dtype=np.ndarray) sinrs5[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_fixed_1), Wk_fixed_all[0], noise_plus_int_cov_matrix[0]) sinrs5[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_fixed_2), Wk_fixed_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency se5 = ( np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs5[0]), packet_length)) + np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs5[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Handling external interference xxxxxxxxxxxxxxxxxxxxxxxxxxxx # Handling external interference using the capacity metric enhancedBD_obj.set_ext_int_handling_metric('capacity') (MsPk_all, Wk_cap_all, Ns_cap_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_cap_1 = MsPk_all[0] MsPk_cap_2 = MsPk_all[1] self.assertEqual(MsPk_cap_1.shape[1], Ns_cap_all[0]) self.assertEqual(MsPk_cap_2.shape[1], Ns_cap_all[1]) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_cap_1, 'fro') ** 2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_cap_2, 'fro') ** 2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_cap_1), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_cap_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_cap_2), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_cap_1), 'fro'), 0) sinrs2 = np.empty(K, dtype=np.ndarray) sinrs2[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_cap_1), Wk_cap_all[0], noise_plus_int_cov_matrix[0]) sinrs2[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_cap_2), Wk_cap_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency se2 = ( np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs2[0]), packet_length)) + np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs2[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Handling external interference xxxxxxxxxxxxxxxxxxxxxxxxxxxx # Handling external interference using the effective_throughput metric enhancedBD_obj.set_ext_int_handling_metric( 'effective_throughput', {'modulator': psk_obj, 'packet_length': packet_length}) (MsPk_effec_all, Wk_effec_all, Ns_effec_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_effec_1 = MsPk_effec_all[0] MsPk_effec_2 = MsPk_effec_all[1] self.assertEqual(MsPk_effec_1.shape[1], Ns_effec_all[0]) self.assertEqual(MsPk_effec_2.shape[1], Ns_effec_all[1]) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_effec_1, 'fro') ** 2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_effec_2, 'fro') ** 2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_effec_1), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_effec_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_effec_2), 'fro'), 0) self.assertAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_effec_1), 'fro'), 0) sinrs3 = np.empty(K, dtype=np.ndarray) sinrs3[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_effec_1), Wk_effec_all[0], noise_plus_int_cov_matrix[0]) sinrs3[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_effec_2), Wk_effec_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency se3 = ( np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs3[0]), packet_length)) + np.sum(psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs3[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Test if the effective_throughput obtains a better spectral # efficiency then the capacity and not handling interference. self.assertGreater(se3 + tol, se2) self.assertGreater(se3 + tol, se)
def test_block_diagonalize_no_waterfilling(self): Nr = np.array([2, 2]) Nt = np.array([2, 2]) K = Nt.size Nti = 1 iPu = 1e-1 # Power for each user (linear scale) pe = 1e-3 # External interference power (in linear scale) noise_var = 1e-1 # The modulator and packet_length are required in the # effective_throughput metric case psk_obj = fundamental.PSK(4) packet_length = 120 multiUserChannel = multiuser.MultiUserChannelMatrixExtInt() multiUserChannel.randomize(Nr, Nt, K, Nti) multiUserChannel.noise_var = noise_var # Channel from all transmitters to the first receiver H1 = multiUserChannel.get_Hk_without_ext_int(0) # Channel from all transmitters to the second receiver H2 = multiUserChannel.get_Hk_without_ext_int(1) # Create the enhancedBD object enhancedBD_obj = blockdiagonalization.EnhancedBD(K, iPu, noise_var, pe) noise_plus_int_cov_matrix \ = multiUserChannel.calc_cov_matrix_extint_plus_noise(pe) # xxxxx First we test without ext. int. handling xxxxxxxxxxxxxxxxxx enhancedBD_obj.set_ext_int_handling_metric(None) (Ms_all, Wk_all, Ns_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) Ms1 = Ms_all[0] Ms2 = Ms_all[1] self.assertEqual(Ms1.shape[1], Ns_all[0]) self.assertEqual(Ms2.shape[1], Ns_all[1]) # Most likely only one base station (the one with the worst # channel) will employ a precoder with total power of `Pu`, # while the other base stations will use less power. tol = 1e-10 self.assertGreaterEqual(iPu + tol, np.linalg.norm(Ms1, 'fro')**2) # 1e-12 is included to avoid false test fails due to small # precision errors self.assertGreaterEqual(iPu + tol, np.linalg.norm(Ms2, 'fro')**2) # Test if the precoder block diagonalizes the channel self.assertNotAlmostEqual(np.linalg.norm(np.dot(H1, Ms1), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H1, Ms2), 'fro'), 0) self.assertNotAlmostEqual(np.linalg.norm(np.dot(H2, Ms2), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H2, Ms1), 'fro'), 0) # Equivalent sinrs (in linear scale) sinrs = np.empty(K, dtype=np.ndarray) sinrs[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, Ms1), Wk_all[0], noise_plus_int_cov_matrix[0]) sinrs[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, Ms2), Wk_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency # noinspection PyPep8 se = (np.sum( psk_obj.calcTheoreticalSpectralEfficiency(linear2dB( sinrs[0]), packet_length)) + np.sum( psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Now with the Naive Stream Reduction xxxxxxxxxxxxxxxxxxxxxxx num_streams = 1 enhancedBD_obj.set_ext_int_handling_metric( 'naive', {'num_streams': num_streams}) (MsPk_naive_all, Wk_naive_all, Ns_naive_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_naive_1 = MsPk_naive_all[0] MsPk_naive_2 = MsPk_naive_all[1] self.assertEqual(MsPk_naive_1.shape[1], Ns_naive_all[0]) self.assertEqual(MsPk_naive_2.shape[1], Ns_naive_all[1]) self.assertEqual(Ns_naive_all[0], num_streams) self.assertEqual(Ns_naive_all[1], num_streams) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_naive_1, 'fro')**2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_naive_2, 'fro')**2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_naive_1), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H1, MsPk_naive_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_naive_2), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H2, MsPk_naive_1), 'fro'), 0) sinrs4 = np.empty(K, dtype=np.ndarray) sinrs4[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_naive_1), Wk_naive_all[0], noise_plus_int_cov_matrix[0]) sinrs4[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_naive_2), Wk_naive_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency # se4 = ( # np.sum(psk_obj.calcTheoreticalSpectralEfficiency( # linear2dB(sinrs4[0]), # packet_length)) # + # np.sum(psk_obj.calcTheoreticalSpectralEfficiency( # linear2dB(sinrs4[1]), # packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Now with the Fixed Stream Reduction xxxxxxxxxxxxxxxxxxxxxxx # The 'fixed' metric requires that metric_func_extra_args_dict is # provided and has the 'num_streams' key. If this is not the case # an exception is raised with self.assertRaises(AttributeError): enhancedBD_obj.set_ext_int_handling_metric('fixed') # Now let's test the fixed metric num_streams = 1 enhancedBD_obj.set_ext_int_handling_metric( 'fixed', {'num_streams': num_streams}) (MsPk_fixed_all, Wk_fixed_all, Ns_fixed_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_fixed_1 = MsPk_fixed_all[0] MsPk_fixed_2 = MsPk_fixed_all[1] self.assertEqual(MsPk_fixed_1.shape[1], Ns_fixed_all[0]) self.assertEqual(MsPk_fixed_2.shape[1], Ns_fixed_all[1]) self.assertEqual(Ns_fixed_all[0], num_streams) self.assertEqual(Ns_fixed_all[1], num_streams) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_fixed_1, 'fro')**2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_fixed_2, 'fro')**2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_fixed_1), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H1, MsPk_fixed_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_fixed_2), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H2, MsPk_fixed_1), 'fro'), 0) sinrs5 = np.empty(K, dtype=np.ndarray) sinrs5[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_fixed_1), Wk_fixed_all[0], noise_plus_int_cov_matrix[0]) sinrs5[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_fixed_2), Wk_fixed_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency # se5 = ( # np.sum(psk_obj.calcTheoreticalSpectralEfficiency( # linear2dB(sinrs5[0]), # packet_length)) # + # np.sum(psk_obj.calcTheoreticalSpectralEfficiency( # linear2dB(sinrs5[1]), # packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Handling external interference xxxxxxxxxxxxxxxxxxxxxxxxxxxx # Handling external interference using the capacity metric enhancedBD_obj.set_ext_int_handling_metric('capacity') (MsPk_all, Wk_cap_all, Ns_cap_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_cap_1 = MsPk_all[0] MsPk_cap_2 = MsPk_all[1] self.assertEqual(MsPk_cap_1.shape[1], Ns_cap_all[0]) self.assertEqual(MsPk_cap_2.shape[1], Ns_cap_all[1]) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_cap_1, 'fro')**2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_cap_2, 'fro')**2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_cap_1), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H1, MsPk_cap_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_cap_2), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H2, MsPk_cap_1), 'fro'), 0) sinrs2 = np.empty(K, dtype=np.ndarray) sinrs2[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_cap_1), Wk_cap_all[0], noise_plus_int_cov_matrix[0]) sinrs2[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_cap_2), Wk_cap_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency # noinspection PyPep8 se2 = (np.sum( psk_obj.calcTheoreticalSpectralEfficiency(linear2dB( sinrs2[0]), packet_length)) + np.sum( psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs2[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # xxxxx Handling external interference xxxxxxxxxxxxxxxxxxxxxxxxxxxx # Handling external interference using the effective_throughput metric enhancedBD_obj.set_ext_int_handling_metric( 'effective_throughput', { 'modulator': psk_obj, 'packet_length': packet_length }) (MsPk_effec_all, Wk_effec_all, Ns_effec_all) \ = enhancedBD_obj.block_diagonalize_no_waterfilling( multiUserChannel) MsPk_effec_1 = MsPk_effec_all[0] MsPk_effec_2 = MsPk_effec_all[1] self.assertEqual(MsPk_effec_1.shape[1], Ns_effec_all[0]) self.assertEqual(MsPk_effec_2.shape[1], Ns_effec_all[1]) # Test if the square of the Frobenius norm of the precoder of each # user is equal to the power available to that user. self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_effec_1, 'fro')**2) self.assertAlmostEqual(iPu, np.linalg.norm(MsPk_effec_2, 'fro')**2) # Test if MsPk really block diagonalizes the channel self.assertNotAlmostEqual( np.linalg.norm(np.dot(H1, MsPk_effec_1), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H1, MsPk_effec_2), 'fro'), 0) self.assertNotAlmostEqual( np.linalg.norm(np.dot(H2, MsPk_effec_2), 'fro'), 0) self.assertAlmostEqual(np.linalg.norm(np.dot(H2, MsPk_effec_1), 'fro'), 0) sinrs3 = np.empty(K, dtype=np.ndarray) sinrs3[0] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H1, MsPk_effec_1), Wk_effec_all[0], noise_plus_int_cov_matrix[0]) sinrs3[1] = blockdiagonalization.EnhancedBD._calc_linear_SINRs( np.dot(H2, MsPk_effec_2), Wk_effec_all[1], noise_plus_int_cov_matrix[1]) # Spectral efficiency # noinspection PyPep8 se3 = (np.sum( psk_obj.calcTheoreticalSpectralEfficiency(linear2dB( sinrs3[0]), packet_length)) + np.sum( psk_obj.calcTheoreticalSpectralEfficiency( linear2dB(sinrs3[1]), packet_length))) # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Test if the effective_throughput obtains a better spectral # efficiency then the capacity and not handling interference. self.assertGreater(se3 + tol, se2) self.assertGreater(se3 + tol, se)