__author__ = ['Hongyi Yang']
__all__ = [
'init',
'plot_delay',
'plot_buffer',
'plot_throughput',
'plot_delay_heatmap',
'plot_packet_heatmap',
'plot_ook_ber',
'plot_scaled_network_delay',
'_compute_scaled_network_transfer_delay',
]
import gc
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import rc
from mpl_toolkits.axes_grid1 import make_axes_locatable
import warnings
import numpy as np
from scipy.special import erfc
from scipy.stats import ncx2
from NetworkSim.architecture.base.network import Network
from NetworkSim.architecture.setup.model import Model
from NetworkSim.simulation.simulator.base import BaseSimulator
from NetworkSim.simulation.simulator.parallel import ParallelSimulator
from NetworkSim.simulation.tools.performance_analysis import \
get_transfer_delay, get_queueing_delay, get_min_transfer_delay, get_service_delay, \
get_final_batch_delay, get_extended_run_delay, get_overall_delay, \
get_final_batch_throughput, get_extended_run_throughput, get_overall_throughput
queueing_delay_keywords = {'queueing delay', 'qd', 'queueing'}
transfer_delay_keywords = {'transfer delay', 'td', 'transfer'}
overall_range_keywords = {'overall', 'all'}
extended_range_keywords = {'extended', 'e'}
final_batch_range_keywords = {'final batch', 'fb'}
load_keywards = {'load', 'l'}
scale_keywords = {'scale', 's'}
mean_keywords = {'mean', 'Mean', 'MEAN'}
min_keywords = {'min', 'Min', 'MIN'}
max_keywords = {'max', 'Max', 'MAX'}
t_min_keywords = {'tmin', 'TMIN'}
colors = [
sns.color_palette('RdBu_r')[0],
sns.color_palette('PRGn_r')[0],
sns.color_palette('PRGn_r')[-1],
sns.color_palette('Paired')[7],
sns.color_palette('RdBu_r')[-1],
]
markers = [
's',
'^',
'o',
'D'
]
FIG_WIDTH = 3
FIG_HEIGHT = 3
MARKERSIZE = 2.5
MARKER_EDGE_WIDTH = .4
DPI = 600
CAX_FONT_SIZE = 6
def _check_for_ParallelSimulator(simulator):
if not isinstance(simulator, ParallelSimulator):
raise ValueError("ParallelSimulator is expected.")
def _check_for_BaseSimulator(simulator):
if not isinstance(simulator, BaseSimulator):
raise ValueError("BaseSimulator is expected.")
def _check_delay_keywords(delay):
if delay not in queueing_delay_keywords and delay not in transfer_delay_keywords:
raise ValueError("Delay type is not recognised.")
def _check_span_keywords(span):
if span not in overall_range_keywords and span not in extended_range_keywords \
and span not in final_batch_range_keywords:
raise ValueError("Time span is not recognised.")
def _check_metric_keywords(metric):
if metric not in mean_keywords and metric not in min_keywords \
and metric not in max_keywords and metric not in t_min_keywords:
raise ValueError("Metric is not recognised.")
def _check_scaled_network_keywords(data_type):
if data_type not in load_keywards and data_type not in scale_keywords:
raise ValueError("Scaled network plot data type is not recognised.")
def _convert_keywords(keyword):
if keyword in queueing_delay_keywords:
return "Queueing Delay"
elif keyword in transfer_delay_keywords:
return "Transfer Delay"
elif keyword in mean_keywords:
return "Average"
elif keyword in min_keywords:
return "Minimum"
elif keyword in max_keywords:
return "Maximum"
elif keyword in t_min_keywords:
return "Theoretical Minimum"
def _get_delay(simulator, delay, span, metric, analytical):
_check_for_BaseSimulator(simulator)
_check_delay_keywords(delay=delay)
_check_span_keywords(span=span)
_check_metric_keywords(metric=metric)
if span in overall_range_keywords:
results = get_overall_delay(simulator=simulator)
elif span in extended_range_keywords:
results = get_extended_run_delay(simulator=simulator)
elif span in final_batch_range_keywords:
results = get_final_batch_delay(simulator=simulator)
analytical_delay = -1
if delay in queueing_delay_keywords:
if metric in min_keywords:
simulation_delay = results['min_queueing_delay']
elif metric in max_keywords:
simulation_delay = results['max_queueing_delay']
elif metric in mean_keywords:
simulation_delay = results['mean_queueing_delay']
if analytical:
analytical_delay = get_queueing_delay(simulator=simulator) + get_service_delay(simulator=simulator)
elif delay in transfer_delay_keywords:
if metric in min_keywords:
simulation_delay = results['min_transfer_delay']
elif metric in max_keywords:
simulation_delay = results['max_transfer_delay']
elif metric in mean_keywords:
simulation_delay = results['mean_transfer_delay']
elif metric in t_min_keywords:
simulation_delay = get_min_transfer_delay(simulator=simulator)
if analytical:
analytical_delay = get_transfer_delay(simulator=simulator)
return analytical_delay, simulation_delay
def _get_throughput(simulator, span):
_check_for_BaseSimulator(simulator=simulator)
_check_span_keywords(span=span)
if span in overall_range_keywords:
mean_throughput = get_overall_throughput(simulator=simulator)
elif span in extended_range_keywords:
mean_throughput = get_extended_run_throughput(simulator=simulator)
elif span in final_batch_range_keywords:
mean_throughput = get_final_batch_throughput(simulator=simulator)
return mean_throughput
def _get_buffer_size(simulator, metric):
_check_for_BaseSimulator(simulator)
_check_metric_keywords(metric=metric)
mean_queue_size, max_queue_size, _ = simulator.summary(summary_type='q')
# Compute buffer size in kB
if metric in mean_keywords:
buffer_queue = mean_queue_size
elif metric in max_keywords:
buffer_queue = max_queue_size
else:
buffer_queue = 0
buffer_size = buffer_queue * 1500 / 1000
return buffer_queue, buffer_size
[docs]def init():
"""
Plot environment initialisation.
"""
gc.collect()
sns.set_theme(style="ticks")
sns.set_context("paper")
rc('font', **{'family': 'sans-serif', 'sans-serif': ['Helvetica']})
rc('text', usetex=True)
matplotlib.rcParams['mathtext.fontset'] = 'stix'
matplotlib.rcParams['font.family'] = 'STIXGeneral'
SMALLER_SIZE = 6
SMALL_SIZE = 7
MEDIUM_SIZE = 8
LARGE_SIZE = 9
BIGGER_SIZE = 10
rc('font', size=SMALL_SIZE) # controls default text sizes
rc('axes', titlesize=LARGE_SIZE) # fontsize of the axes title
rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels
rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
rc('legend', fontsize=SMALLER_SIZE) # legend fontsize
rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
sns.set_palette('RdBu_r')
warnings.filterwarnings("ignore")
[docs]def plot_delay(simulator, delay='td', span='e', metrics=['mean'], analytical=True, semilog=True):
"""Plot delay statistics of a ParallelSimulator.
Parameters
----------
simulator : ParallelSimulator
The simulator used.
delay : str, optional
The type of delay to be plotted, by default ``td``, chosen from the following:
- `qd`, `queueing delay` or `queueing`
The overall queueing delay.
- `td`, `transfer delay` or `transfer`
The transfer delay.
span : str, optional
The time span of the delay statistics in the simulation, by default ``extended``, chosen from the following:
- `overall` or `all`
The entire duration of the simulation.
- `final batch` or `fb`
The statistics from the final batch when simulation converges.
- `extended` or `e`
The extended period of the simulation.
metrics : list, optional
A list of the metric(s) of delay statistics, by default ``mean``, chosen from the following:
- `mean`
Mean delay statistics.
- `min`
Minimum delay statistics.
- `max`
Maximum delay statistics.
analytical : bool, optional
If analytical results are plotted, by default True
semilog : bool, optional
If to plot data on a semi-logarithmic scale
Returns
-------
fig : Figure
The figure plotted.
"""
_check_for_ParallelSimulator(simulator)
# Get plot data
analytical_delays = []
simulation_delays = []
load = []
for metric in metrics:
_analytical_delays = []
_simulation_delays = []
_load = []
for individual_simulator in simulator.simulator:
analytical_delay, simulation_delay = _get_delay(
simulator=individual_simulator,
delay=delay,
span=span,
metric=metric,
analytical=analytical
)
_analytical_delays.append(analytical_delay / 1000)
_simulation_delays.append(simulation_delay / 1000)
_load_percentage = individual_simulator.model.constants['average_bit_rate'] / \
individual_simulator.model.constants['maximum_bit_rate'] * 100
if individual_simulator.bidirectional:
_load.append(_load_percentage / 2)
else:
_load.append(_load_percentage)
if not analytical_delays:
analytical_delays = _analytical_delays
simulation_delays.append(_simulation_delays)
load.append(_load)
# Generate plot
init()
fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH * 1.05, FIG_HEIGHT), dpi=DPI)
xlabel = "Network Traffic Load ($\%$)"
ylabel = _convert_keywords(delay) + " ($\mu$s)"
if analytical:
sns.lineplot(x=load[0], y=analytical_delays, lw=0.75, ls='--', marker='x', markersize=MARKERSIZE,
markerfacecolor='None', markeredgecolor='k', markeredgewidth=MARKER_EDGE_WIDTH, ax=ax,
color=sns.color_palette('RdBu_r')[-1], ci=None, label='Analytical Average')
for i in range(len(metrics)):
if analytical:
label = "Simulation "
else:
label = ""
label += _convert_keywords(metrics[i])
sns.lineplot(x=load[i], y=simulation_delays[i], lw=0.75, ls='-', marker=markers[i], markersize=MARKERSIZE,
markerfacecolor='None', markeredgecolor='k', markeredgewidth=MARKER_EDGE_WIDTH, ax=ax,
color=colors[i], ci=95, label=label)
if analytical or len(metrics) > 1:
ax.legend()
else:
ax.legend().set_visible(False)
ax.set_ylabel(ylabel)
ax.set_xlabel(xlabel)
ax.grid(which='both')
if semilog:
ax.set_yscale('log')
ax.minorticks_on()
else:
ax.get_xaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax.get_yaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax.xaxis.grid(True, lw=.5, which='major')
ax.yaxis.grid(True, lw=.5, which='major')
ax.xaxis.grid(True, lw=.3, which='minor', ls=':')
ax.yaxis.grid(True, lw=.3, which='minor', ls=':')
ax.tick_params(width=.5, which='major')
ax.tick_params(width=.3, which='minor')
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_linewidth(0.5)
ax.set_aspect(1 / ax.get_data_ratio(), adjustable='box')
fig.tight_layout()
return fig
[docs]def plot_throughput(simulator, span='e', semilog=False):
"""Plot mean throughput statistics of a ParallelSimulator.
Parameters
----------
simulator : ParallelSimulator
The simulator used.
span : str, optional
The time span of the delay statistics in the simulation, by default ``extended``, chosen from the following:
- `overall` or `all`
The entire duration of the simulation.
- `final batch` or `fb`
The statistics from the final batch when simulation converges.
- `extended` or `e`
The extended period of the simulation.
semilog : bool, optional
If to plot data on a semi-logarithmic scale, by default ``False``.
Returns
-------
fig : Figure
The figure plotted.
"""
_check_for_ParallelSimulator(simulator)
# Get plot data
throughput = []
load = []
for individual_simulator in simulator.simulator:
mean_throughput = _get_throughput(simulator=individual_simulator, span=span)
throughput.append(mean_throughput / 1000)
load.append(individual_simulator.model.network.num_nodes *
individual_simulator.model.constants['average_bit_rate'] / 1000)
# Generate plot
init()
fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH, FIG_HEIGHT), dpi=DPI)
xlabel = "Network Traffic Load (Tbit/s)"
ylabel = "Average Throughput (Tbit/s)"
sns.lineplot(x=load, y=throughput, lw=0.75, ls='-', marker='s', markersize=MARKERSIZE,
markerfacecolor='None', markeredgecolor='k', markeredgewidth=MARKER_EDGE_WIDTH, ax=ax,
color=sns.color_palette('RdBu_r')[0], ci=95)
ax.set_ylabel(ylabel)
ax.set_xlabel(xlabel)
ax.grid(which='both')
if semilog:
ax.set_yscale('log')
ax.minorticks_on()
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.ticklabel_format(axis='y', style='plain')
else:
ax.get_xaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax.get_yaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax.xaxis.grid(True, lw=.5, which='major')
ax.yaxis.grid(True, lw=.5, which='major')
ax.xaxis.grid(True, lw=.3, which='minor', ls=':')
ax.yaxis.grid(True, lw=.3, which='minor', ls=':')
ax.tick_params(width=.5, which='major')
ax.tick_params(width=.3, which='minor')
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_linewidth(0.5)
ax.set_aspect(1 / ax.get_data_ratio(), adjustable='box')
fig.tight_layout()
return fig
[docs]def plot_buffer(simulator, metrics=['mean', 'max'], semilog=True):
"""Plot buffer size statistics of a ParallelSimulator.
Parameters
----------
simulator : ParallelSimulator
The simulator used.
metric : list, optional
A list of the metric(s) of delay statistics, by default ``['mean', 'max']``, \
chosen from the following:
- `mean`
Mean delay statistics.
- `min`
Minimum delay statistics.
- `max`
Maximum delay statistics.
semilog : bool, optional
If to plot data on a semi-logarithmic scale, by default ``True``.
Returns
-------
fig : Figure
The figure plotted.
"""
_check_for_ParallelSimulator(simulator)
# Get plot data
buffer = []
buffer_packet = []
load = []
for metric in metrics:
_buffer = []
_buffer_packet = []
_load = []
for individual_simulator in simulator.simulator:
_buffer_queue, _buffer_size = _get_buffer_size(simulator=individual_simulator, metric=metric)
_buffer.append(_buffer_size)
_buffer_packet.append(_buffer_queue)
_load_percentage = individual_simulator.model.constants['average_bit_rate'] / \
individual_simulator.model.constants['maximum_bit_rate'] * 100
if individual_simulator.bidirectional:
_load.append(_load_percentage / 2)
else:
_load.append(_load_percentage)
buffer.append(_buffer)
buffer_packet.append(_buffer_packet)
load.append(_load)
init()
fig, ax1 = plt.subplots(1, 1, figsize=(FIG_WIDTH * 1.18, FIG_HEIGHT), dpi=DPI)
ax2 = ax1.twinx()
xlabel = "Network Traffic Load ($\%$)"
if len(metrics) == 1:
ylabel = _convert_keywords(metric) + " Buffer Size (kB)"
ylabel2 = _convert_keywords(metric) + " Buffer Size (No. of Packets)"
else:
ylabel = "Buffer Size (kB)"
ylabel2 = "Buffer Size (No. of Packets)"
for i in range(len(metrics)):
label = _convert_keywords(metrics[i])
sns.lineplot(x=load[i], y=buffer[i], lw=0.75, ls='-', marker=markers[i], markersize=MARKERSIZE,
markerfacecolor='None', markeredgecolor='k', markeredgewidth=MARKER_EDGE_WIDTH, ax=ax1,
color=colors[i], ci=95, label=label)
sns.lineplot(x=load[i], y=buffer_packet[i], lw=0, markers=False, ax=ax2, ci=None)
ax1.set_ylabel(ylabel)
ax2.set_ylabel(ylabel2)
ax1.set_xlabel(xlabel)
ax1.grid(which='both')
if len(metrics) > 1:
ax1.legend()
else:
ax1.legend().set_visible(False)
if semilog:
for ax in [ax1, ax2]:
ax.set_yscale('log')
ax.minorticks_on()
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.ticklabel_format(axis='y', style='plain')
else:
for ax in [ax1, ax2]:
ax.get_xaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax.get_yaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax1.xaxis.grid(True, lw=.5, which='major')
ax1.yaxis.grid(True, lw=.5, which='major')
ax1.xaxis.grid(True, lw=.3, which='minor', ls=':')
ax1.yaxis.grid(True, lw=.3, which='minor', ls=':')
for ax in [ax1, ax2]:
ax.tick_params(width=.5, which='major')
ax.tick_params(width=.3, which='minor')
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_linewidth(0.5)
fig.tight_layout()
return fig
def _extract_simulators(simulator, data_rate):
"""Function to extractor the simulators with specified data rate.
Parameters
----------
simulator : ParallelSimulator
The ParallelSimulator to be unpacked.
data_rate : float
Data rate of the simulators to be extracted.
Returns
-------
simulators : list
A list of simulators with the specified data rate.
"""
_check_for_ParallelSimulator(simulator)
simulators = []
for individual_simulator in simulator.simulator:
if individual_simulator.model.constants['average_bit_rate'] == data_rate:
simulators.append(individual_simulator)
print(str(len(simulators)) + " simulators found.")
return simulators
def _compute_delay_heatmap(simulators, delay, span):
"""Function to compute the mean delay statistics for the heatmap plot.
Parameters
----------
simulators : list
List of BaseSimulator used.
delay : str
Type of delay to be computed.
Returns
-------
delay_heatmap : numpy 2d array
An array of computed mean delay values to be used in the heatmap.
Raises
------
ValueError
Raised when inconsistency in simulator node number is found.
"""
_check_delay_keywords(delay=delay)
_check_span_keywords(span=span)
if delay in queueing_delay_keywords:
delay_type = 'Queueing Delay'
elif delay in transfer_delay_keywords:
delay_type = 'Transfer Delay'
n = simulators[0].model.network.num_nodes
packet_count = np.zeros([n, n])
delay_sum = np.zeros([n, n])
delay_heatmap = np.zeros([n, n])
delay_heatmap[:] = np.nan
for simulator in simulators:
if simulator.model.network.num_nodes != n:
raise ValueError("Inconsistent number of nodes are found among the simulators.")
if span in overall_range_keywords:
latency = simulator.latency
elif span in extended_range_keywords:
start_index = simulator.batch_stats[-1]['end_index']
latency = simulator.latency[start_index:]
elif span in final_batch_range_keywords:
start_index = simulator.batch_stats[-1]['start_index']
end_index = simulator.batch_stats[-1]['end_index']
latency = simulator.latency[start_index:end_index]
for latency_dict in latency:
packet_count[latency_dict['Source ID']][latency_dict['Destination ID']] += 1
delay_sum[latency_dict['Source ID']][latency_dict['Destination ID']] += latency_dict[delay_type]
for i in range(n):
for j in range(n):
if i != j and packet_count[i][j] > 0:
delay_heatmap[i][j] = delay_sum[i][j] / packet_count[i][j]
return delay_heatmap
[docs]def plot_delay_heatmap(simulator, data_rate, span='e', delay='td'):
"""Plot mean delay statistics of a specified data rate in a ParallelSimulator.
Parameters
----------
simulator : ParallelSimulator
The simulator used.
data_rate : float
The chosen data rate.
span : str, optional
The time span of the delay statistics in the simulation, by default ``extended``, chosen from the following:
- `overall` or `all`
The entire duration of the simulation.
- `final batch` or `fb`
The statistics from the final batch when simulation converges.
- `extended` or `e`
The extended period of the simulation.
delay : str, optional
The type of delay to be plotted, by default ``td``, chosen from the following:
- `qd`, `queueing delay` or `queueing`
The overall queueing delay.
- `td`, `transfer delay` or `transfer`
The transfer delay.
Returns
-------
fig : Figure
The figure plotted.
"""
# Get simulators with the specified
simulators = _extract_simulators(simulator=simulator, data_rate=data_rate)
# Compute data to be plotted
data = _compute_delay_heatmap(simulators=simulators, delay=delay, span=span) / 1000
# Generate plot
init()
fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH * 1.1, FIG_HEIGHT * 1.1), dpi=DPI)
label = 'Average ' + _convert_keywords(delay) + ' ($\mu$s)'
start, end = 0, len(data)
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='3%', pad=0.1)
sns.heatmap(data=data, cmap='RdBu_r', ax=ax, lw=.125, xticklabels=20, yticklabels=20, square=True, cbar_ax=cax,
cbar_kws={'shrink': .6, 'pad': .1},)
ax.set_xlim([start, end])
ax.set_ylim([end, start])
ax.set_facecolor('k')
ax.set_xticklabels(ax.get_xticklabels(), rotation=0)
ax.set_yticklabels(ax.get_yticklabels(), rotation=0)
ax.tick_params(width=.5, which='major')
cax.tick_params(width=.3, labelsize=5, length=3)
cax.set_ylabel(label, fontsize=CAX_FONT_SIZE)
ax.set_xlabel('Destination Node')
ax.set_ylabel('Source Node')
frame_linewidth = .5
ax.axhline(y=start, color='k', linewidth=frame_linewidth)
ax.axhline(y=end, color='k', linewidth=frame_linewidth)
ax.axvline(x=start, color='k', linewidth=frame_linewidth)
ax.axvline(x=end, color='k', linewidth=frame_linewidth)
ax.set_aspect(1 / ax.get_data_ratio(), adjustable='box')
fig.tight_layout()
return fig
def _compute_packet_heatmap(simulators, span):
"""Function to compute the normalised packet number statistics for the heatmap plot.
Parameters
----------
simulators : list
List of BaseSimulator used.
Returns
-------
packet_heatmap : numpy 2d array
An array of computed packet number values to be used in the heatmap.
Raises
------
ValueError
Raised when inconsistency in simulator node number is found.
"""
_check_span_keywords(span=span)
n = simulators[0].model.network.num_nodes
packet_count = np.zeros([n, n])
for simulator in simulators:
if simulator.model.network.num_nodes != n:
raise ValueError("Inconsistent number of nodes are found among the simulators.")
if span in overall_range_keywords:
latency = simulator.latency
elif span in extended_range_keywords:
start_index = simulator.batch_stats[-1]['end_index']
latency = simulator.latency[start_index:]
elif span in final_batch_range_keywords:
start_index = simulator.batch_stats[-1]['start_index']
end_index = simulator.batch_stats[-1]['end_index']
latency = simulator.latency[start_index:end_index]
for latency_dict in latency:
packet_count[latency_dict['Source ID']][latency_dict['Destination ID']] += 1
max_packet = np.nanmax(packet_count)
min_packet = np.nanmin(packet_count)
for i in range(n):
for j in range(n):
if i == j:
packet_count[i][j] = np.nan
else:
packet_count[i][j] = (packet_count[i][j] - min_packet) / (max_packet - min_packet)
return packet_count
[docs]def plot_packet_heatmap(simulator, data_rate, span='e'):
"""Plot mean packet number statistics of a specified data rate in a ParallelSimulator.
Parameters
----------
simulator : ParallelSimulator
The simulator used.
data_rate : float
The chosen data rate.
span : str, optional
The time span of the delay statistics in the simulation, by default ``extended``, chosen from the following:
- `overall` or `all`
The entire duration of the simulation.
- `final batch` or `fb`
The statistics from the final batch when simulation converges.
- `extended` or `e`
The extended period of the simulation.
Returns
-------
fig : Figure
The figure plotted.
"""
# Get simulators with the specified
simulators = _extract_simulators(simulator, data_rate)
# Compute data to be plotted
data = _compute_packet_heatmap(simulators=simulators, span=span)
# Generate plot
init()
fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH * 1.1, FIG_HEIGHT * 1.1), dpi=DPI)
label = 'Normalised No. of Packets Transmitted'
start, end = 0, len(data)
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='3%', pad=0.1)
sns.heatmap(data=data, cmap='RdBu_r', ax=ax, lw=.125, xticklabels=20, yticklabels=20, square=True, cbar_ax=cax,
cbar_kws={'shrink': .6, 'pad': .1},)
ax.set_xlim([start, end])
ax.set_ylim([end, start])
ax.set_facecolor('k')
ax.set_xticklabels(ax.get_xticklabels(), rotation=0)
ax.set_yticklabels(ax.get_yticklabels(), rotation=0)
ax.tick_params(width=.5, which='major')
cax.tick_params(width=.3, labelsize=5, length=3)
cax.set_ylabel(label, fontsize=CAX_FONT_SIZE)
ax.set_xlabel('Destination Node')
ax.set_ylabel('Source Node')
frame_linewidth = .5
ax.axhline(y=start, color='k', linewidth=frame_linewidth)
ax.axhline(y=end, color='k', linewidth=frame_linewidth)
ax.axvline(x=start, color='k', linewidth=frame_linewidth)
ax.axvline(x=end, color='k', linewidth=frame_linewidth)
ax.set_aspect(1 / ax.get_data_ratio(), adjustable='box')
fig.tight_layout()
return fig
[docs]def plot_ook_ber(snr_min=0, snr_max=15, show_coherent=False):
"""Plot OOK bit error rate vs average energy-to-noise ratio [1]_.
Parameters
----------
snr_max : int, optional
Maximum energy-to-noise ratio value in dB, by default ``15``.
show_coherent : bool, optional
If coherent detection is shown, by default ``False``.
Returns
-------
fig : Figure
The figure plotted.
References
----------
.. [1] Tang, Qinghui, Sandeep KS Gupta, and Loren Schwiebert. "BER performance analysis of an on-off keying \
based minimum energy coding for energy constrained wireless sensor applications." \
In IEEE International Conference on Communications, 2005. \
ICC 2005. 2005, vol. 4, pp. 2734-2738. IEEE, 2005.
"""
# Compute values
SNR_MIN = snr_min
SNR_MAX = snr_max
Eb_No_dB = np.linspace(SNR_MIN, SNR_MAX, 10000)
Eb_No = 10**(Eb_No_dB / 10.0)
sigma = 0.1
a = 4 * Eb_No
b_th = sigma * np.sqrt(2 + Eb_No)
sigma = b_th / np.sqrt(2 + Eb_No)
b = (b_th / sigma) * (b_th / sigma)
pes = np.exp(-b_th * b_th / (2 * sigma * sigma))
pem = 1 - (ncx2.sf(x=b, df=2, nc=a))
ook_non_coherent = 0.5 * (pem + pes)
ook_coherent = 0.5 * erfc(np.sqrt(Eb_No / 2))
# Generate plot
init()
fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH, FIG_HEIGHT), dpi=DPI)
xlabel = "$E_b/N_o$ (dB)"
ylabel = "BER"
labels = ["Noncoherent", "Coherent"]
sns.lineplot(x=Eb_No_dB, y=ook_non_coherent, lw=0.75, markers=False,
ax=ax, ci=None, color=colors[0], label=labels[0])
if show_coherent:
sns.lineplot(x=Eb_No_dB, y=ook_coherent, lw=0.75, markers=False,
ax=ax, ci=None, color=colors[-1], label=labels[1])
ax.set_ylabel(ylabel)
ax.set_xlabel(xlabel)
ax.grid(which='both')
ax.set_yscale('log')
ax.minorticks_on()
if not show_coherent:
ax.legend().set_visible(False)
ax.xaxis.grid(True, lw=.5, which='major')
ax.yaxis.grid(True, lw=.5, which='major')
ax.xaxis.grid(True, lw=.3, which='minor', ls=':')
ax.yaxis.grid(True, lw=.3, which='minor', ls=':')
ax.tick_params(width=.5, which='major')
ax.tick_params(width=.3, which='minor')
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_linewidth(0.5)
ax.set_aspect(1 / ax.get_data_ratio(), adjustable='box')
fig.tight_layout()
return fig
def _compute_scaled_network_transfer_delay(data_type):
"""Function to compute the data to be plotted in a scaled network.
Parameters
----------
data_type : str
The type of data to be plotted.
Returns
-------
x : list
A list of x values to be plotted.
y : list
A list of y values to be plotted.
labels : list
A list of y labels.
"""
_check_scaled_network_keywords(data_type=data_type)
transfer_delay = []
if data_type in load_keywards:
load_list = [10, 15, 20, 25]
num_nodes_list = np.arange(100, 11100, 1000)
elif data_type in scale_keywords:
load_list = np.arange(1, 44, 1)
num_nodes_list = [512, 1280, 5120, 10240]
for j in range(len(num_nodes_list)):
num_nodes = num_nodes_list[j]
length = num_nodes * 1.2
network = Network(length=length, num_nodes=num_nodes)
model = Model(network=network)
_transfer_delay = []
for i in range(len(load_list)):
model.constants['average_bit_rate'] = load_list[i]
simulator = BaseSimulator(convergence=True, model=model)
_transfer_delay.append(get_transfer_delay(simulator=simulator) / 1000)
if np.isinf(_transfer_delay[-1]):
_transfer_delay[-1] = np.nan
transfer_delay.append(_transfer_delay)
if data_type in load_keywards:
x = num_nodes_list
y = list(map(list, zip(*transfer_delay)))
labels = [str(item) + "\%" for item in load_list]
elif data_type in scale_keywords:
x = load_list
y = transfer_delay
labels = [str(item) for item in num_nodes_list]
return x, y, labels
[docs]def plot_scaled_network_delay(data_type='load'):
"""Plot delay of a scaled network.
Parameters
----------
data_type : str, optional
The data of focus, chosen from the following:
- `load` or `l`: Varying load of the network at 10%, 15%, 20% and 25% for \
network node number from 100 to 10100.
- `scale`or `s`: Varying number of nodes in the network at 512, 1280, 5120, and 10240 \
for network traffic load from 1% to 40$.
Default is ``load``.
Returns
-------
fig : Figure
The figure plotted.
"""
x, y, labels = _compute_scaled_network_transfer_delay(data_type=data_type)
init()
fig, ax = plt.subplots(1, 1, figsize=(FIG_WIDTH, FIG_HEIGHT), dpi=DPI)
xlabel = ""
ylabel = "Analytical Average Transfer Delay ($\mu$s)"
for i in range(len(y)):
sns.lineplot(x=x, y=y[i], lw=0.75, ls='-', marker=markers[i], markersize=MARKERSIZE,
markerfacecolor='None', markeredgecolor='k', markeredgewidth=MARKER_EDGE_WIDTH, ax=ax,
color=colors[i], label=labels[i], zorder=len(y) - i)
ax.grid(which='both')
if data_type in scale_keywords:
ax.set_yscale('log')
ylim = np.power(10, int(np.log10(np.nanmax(y))) + 1)
ax.set_ylim(top=ylim)
xlabel = "Network Traffic Load (\%)"
elif data_type in load_keywards:
ax.get_xaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
ax.get_yaxis().set_minor_locator(matplotlib.ticker.AutoMinorLocator())
xlabel = "Number of Nodes"
ax.set_ylabel(ylabel)
ax.set_xlabel(xlabel)
ax.legend(loc='upper left')
ax.xaxis.grid(True, lw=.5, which='major')
ax.yaxis.grid(True, lw=.5, which='major')
ax.xaxis.grid(True, lw=.3, which='minor', ls=':')
ax.yaxis.grid(True, lw=.3, which='minor', ls=':')
ax.tick_params(width=.5, which='major')
ax.tick_params(width=.3, which='minor')
for axis in ['top', 'bottom', 'left', 'right']:
ax.spines[axis].set_linewidth(0.5)
ax.set_aspect(1 / ax.get_data_ratio(), adjustable='box')
fig.tight_layout()
return fig