feat: add descriptions to bandwidth plot

This commit is contained in:
Youngjoon Lee 2024-05-21 19:57:22 +09:00
parent 9e592bcc03
commit 7820e298c8
No known key found for this signature in database
GPG Key ID: 09B750B5BD6F08A2
5 changed files with 66 additions and 14 deletions

View File

@ -3,28 +3,41 @@ import seaborn
from matplotlib import pyplot as plt
from adversary import NodeState
from config import Config
from simulation import Simulation
class Analysis:
def __init__(self, sim: Simulation):
def __init__(self, sim: Simulation, config: Config):
self.sim = sim
self.config = config
def run(self):
self.bandwidth()
self.message_size_distribution()
message_size_df = self.message_size_distribution()
self.bandwidth(message_size_df)
self.messages_emitted_around_interval()
self.mixed_messages_per_node_over_time()
if self.config.mixnet.is_mixing_on():
self.mixed_messages_per_node_over_time()
self.node_states()
def bandwidth(self):
def bandwidth(self, message_size_df: pd.DataFrame):
dataframes = []
for ingress_bandwidths, egress_bandwidths in zip(self.sim.p2p.measurement.ingress_bandwidth_per_time, self.sim.p2p.measurement.egress_bandwidth_per_time):
nonzero_ingresses = []
nonzero_egresses = []
for ingress_bandwidths, egress_bandwidths in zip(self.sim.p2p.measurement.ingress_bandwidth_per_time,
self.sim.p2p.measurement.egress_bandwidth_per_time):
rows = []
for node in self.sim.p2p.nodes:
rows.append((node.id, ingress_bandwidths[node]/1024.0, egress_bandwidths[node]/1024.0))
ingress = ingress_bandwidths[node] / 1024.0
egress = egress_bandwidths[node] / 1024.0
rows.append((node.id, ingress, egress))
if ingress > 0:
nonzero_ingresses.append(ingress)
if egress > 0:
nonzero_egresses.append(egress)
df = pd.DataFrame(rows, columns=["node_id", "ingress", "egress"])
dataframes.append(df)
times = range(len(dataframes))
df = pd.concat([df.assign(Time=time) for df, time in zip(dataframes, times)], ignore_index=True)
df = df.pivot(index="Time", columns="node_id", values=["ingress", "egress"])
@ -41,11 +54,25 @@ class Analysis:
by_label = dict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())
plt.grid(True)
# Adding descriptions on the right size of the plot
ingress_series = pd.Series(nonzero_ingresses)
egress_series = pd.Series(nonzero_egresses)
desc = (
f"message: {message_size_df["message_size"].mean():.0f} bytes\n"
f"{self.config.description()}\n\n"
f"[ingress(>0)]\nmean: {ingress_series.mean():.2f} KiB/s\nmax: {ingress_series.max():.2f} KiB/s\n\n"
f"[egress(>0)]\nmean: {egress_series.mean():.2f} KiB/s\nmax: {egress_series.max():.2f} KiB/s"
)
plt.text(1.02, 0.5, desc, transform=plt.gca().transAxes, verticalalignment="center", fontsize=12)
plt.subplots_adjust(right=0.8) # Adjust layout to make room for the text
plt.show()
def message_size_distribution(self):
def message_size_distribution(self) -> pd.DataFrame:
df = pd.DataFrame(self.sim.p2p.adversary.message_sizes, columns=["message_size"])
print(df.describe())
return df
def messages_emitted_around_interval(self):
df = pd.DataFrame(
@ -89,10 +116,10 @@ class Analysis:
df = pd.DataFrame(rows, columns=["time", "node_id", "state"])
plt.figure(figsize=(10, 6))
seaborn.scatterplot(data=df, x="time", y="node_id", hue="state", palette={NodeState.SENDING: "red", NodeState.RECEIVING: "blue"})
seaborn.scatterplot(data=df, x="time", y="node_id", hue="state",
palette={NodeState.SENDING: "red", NodeState.RECEIVING: "blue"})
plt.title("Node states over time")
plt.xlabel("Time")
plt.ylabel("Node ID")
plt.legend(title="state")
plt.show()

View File

@ -29,6 +29,12 @@ class Config:
return config
def description(self):
return (
f"{self.mixnet.description()}\n"
f"{self.p2p.description()}"
)
@dataclass
class SimulationConfig:
@ -55,7 +61,7 @@ class MixnetConfig:
real_message_prob_weights: list[float]
# A probability of sending a cover message within one cycle if not sending a real message
cover_message_prob: float
# A maximum preparation time (delay) before sending the message
# A maximum preparation time (computation time) for a message sender before sending the message
max_message_prep_time: float
# A maximum delay of messages mixed in a mix node
max_mix_delay: float
@ -73,6 +79,20 @@ class MixnetConfig:
assert self.max_message_prep_time >= 0
assert self.max_mix_delay >= 0
def description(self):
return (
f"payload: {self.payload_size} bytes\n"
f"num_nodes: {self.num_nodes}\n"
f"num_mix_layers: {self.num_mix_layers}\n"
f"max_mix_delay: {self.max_mix_delay}\n"
f"msg_interval: {self.message_interval}\n"
f"real_msg_prob: {self.real_message_prob:.2f}\n"
f"cover_msg_prob: {self.cover_message_prob:.2f}"
)
def is_mixing_on(self) -> bool:
return self.num_mix_layers > 0
@dataclass
class P2pConfig:
@ -82,6 +102,11 @@ class P2pConfig:
def validate(self):
assert self.max_network_latency >= 0
def description(self):
return (
f"max_net_latency: {self.max_network_latency:.2f}"
)
@dataclass
class MeasurementConfig:

View File

@ -20,7 +20,7 @@ mixnet:
real_message_prob_weights: [3, 2, 5]
# A probability of sending a cover message within a cycle if not sending a real message
cover_message_prob: 0.2
# A maximum preparation time (delay) before sending the message
# A maximum preparation time (computation time) for a message sender before sending the message
max_message_prep_time: 0.05
# A maximum delay of messages mixed in a mix node
max_mix_delay: 3

View File

@ -13,6 +13,6 @@ if __name__ == "__main__":
sim = Simulation(config)
sim.run()
Analysis(sim).run()
Analysis(sim, config).run()
print("Simulation complete!")

View File

@ -63,7 +63,7 @@ class Node:
Creates a real or cover message
@return:
"""
if self.config.mixnet.num_mix_layers == 0: # if mixing is turned off
if not self.config.mixnet.is_mixing_on():
return self.build_payload()
mixes = self.p2p.get_nodes(self.config.mixnet.num_mix_layers)