From a722831d97ceeb01cd2c12492f8d97595458c167 Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Tue, 6 May 2025 11:18:26 +0200 Subject: [PATCH] loss rate --- src/main.py | 4 +- src/simulation.py | 101 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/main.py b/src/main.py index 0ccfc30..29e3683 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,6 @@ #!/bin/python3 import argparse -from simulation import simulation_wrapper +from simulation import simulation_wrapper2 if __name__ == "__main__": parser = argparse.ArgumentParser(description='Simulation server cluster') @@ -18,5 +18,5 @@ if __name__ == "__main__": lambda_vals = [l/100 for l in range(1, 301)] # λ from 0.01 to 3.00 - simulation_wrapper(args.simulation_time, args.num_runs, args.min_runs, args.confidence_level, lambda_vals) + simulation_wrapper2(args.simulation_time, args.num_runs, args.min_runs, args.confidence_level, lambda_vals) diff --git a/src/simulation.py b/src/simulation.py index dc5eb3b..966a4a9 100644 --- a/src/simulation.py +++ b/src/simulation.py @@ -57,7 +57,7 @@ class Simulation: heappush(self.event_queue, (arrival_time, request_event)) - def handle_request(self, request): + def handle_request(self, request, max_loss_value): self.total_requests += 1 if len(self.router_queue) == 0 and self.router_state == "idle": self.router_process(request) @@ -66,7 +66,7 @@ class Simulation: else: self.lost_requests += 1 self.loss_rate = self.lost_requests / self.total_requests - if self.loss_rate > 0.05 : + if self.loss_rate > max_loss_value : raise ValueError("lossrate too high") def router_process(self, request): @@ -113,7 +113,7 @@ class Simulation: self.router_process_next() - def run(self, max_time): + def run(self, max_time, max_loss_value): # first request self.next_request() @@ -123,7 +123,7 @@ class Simulation: match current_event[1].event_type: case "request": self.next_request() - self.handle_request(current_event[1].request) + self.handle_request(current_event[1].request, max_loss_value) case "router_finish": self.router_process_finish(current_event[1].request) case "process_finish": @@ -134,12 +134,12 @@ class Simulation: def run_single_simulation(args): - c, lambda_val, simulation_time = args + c, lambda_val, simulation_time, max_loss_value = args # for different seed in each process random.seed(time.time() + os.getpid()) try: sim = Simulation(c, lambda_val) - sim.run(simulation_time) + sim.run(simulation_time, max_loss_value) if len(sim.response_times) > 0: run_mean = sum(sim.response_times) / len(sim.response_times) loss_rate = sim.loss_rate @@ -149,12 +149,13 @@ def run_single_simulation(args): except ValueError: # Loss rate too high return None -def simulation_wrapper(simulation_time, num_runs, min_runs, confidence_level, lambda_vals): +def simulation_wrapper1(simulation_time, num_runs, min_runs, confidence_level, lambda_vals): C_values = [1, 2, 3, 6] plt.figure(figsize=(12, 8)) mean_at_lambda1 = {} + loss_data = {} with Pool() as pool: # pool of workers for c in C_values: @@ -162,11 +163,12 @@ def simulation_wrapper(simulation_time, num_runs, min_runs, confidence_level, la means = [] ci_lower = [] ci_upper = [] + losses = [] print(f"\nProcessing C={c}") for lambda_val in lambda_vals: # run num_runs simulation for each lambda - args_list = [(c, lambda_val, simulation_time) for _ in range(num_runs)] + args_list = [(c, lambda_val, simulation_time, 0.05) for _ in range(num_runs)] results = pool.map(run_single_simulation, args_list) # collect results from successful simulations @@ -193,8 +195,9 @@ def simulation_wrapper(simulation_time, num_runs, min_runs, confidence_level, la means.append(mean_rt) ci_lower.append(mean_rt - ci) ci_upper.append(mean_rt + ci) + losses.append(mean_loss) - print(f"C={c}, λ={lambda_val:.2f}, Mean RT={mean_rt:.2f} ± {ci:.2f}, Loss Rate={mean_loss:.2%}") + print(f"C={c}, λ={lambda_val:.2f}, Mean RT={mean_rt:.2f} ± {ci:.2f}, Mean Loss Rate={mean_loss:.2%}") elif len(run_results) > 0: print(f"λ={lambda_val:.2f} skipped - only {len(run_results)} successful run(s)") continue @@ -210,6 +213,8 @@ def simulation_wrapper(simulation_time, num_runs, min_runs, confidence_level, la idx = lambda_points.index(1) mean_at_lambda1[c] = (means[idx], ci_lower[idx], ci_upper[idx]) + loss_data[c] = (np.array(lambda_points), np.array(losses)) + # determine optimal C for lamba = 1 if mean_at_lambda1: sorted_C = sorted(mean_at_lambda1.items(), key=lambda item: item[1][0]) @@ -229,4 +234,80 @@ def simulation_wrapper(simulation_time, num_runs, min_runs, confidence_level, la plt.title(f'Mean Response Time vs Arrival Rate ({num_runs} runs, 95% CI)') plt.legend() plt.grid(True) - plt.show() + + return loss_data + +def simulation_wrapper2(simulation_time, num_runs, min_runs, confidence_level, lambda_vals, max_loss_value=0.1): + loss_data = simulation_wrapper1(simulation_time, num_runs, min_runs, confidence_level, lambda_vals) + plt.show() + + for c, (lams, losses) in loss_data.items(): + lambda_vals2 = [] + if len(lams)==0: + print(f"C={c}: no data at all.") + continue + pos_idx = np.where(losses > 0)[0] + if pos_idx.size > 0: + first_lambda = lams[pos_idx[0]] + else: + first_lambda = None + last_lambda = lams[-1] + + print(f"C={c:>1} first λ with loss > 0: " + f"{first_lambda if first_lambda is not None else 'never'}; " + f" last λ with data: {last_lambda}") + + lambda_vals2.extend([last_lambda + i/1000 for i in range(-100,51)]) + + + + lambda_points = [] + losses = [] + ci_lower = [] + ci_upper = [] + print(f"\nProcessing C={c}") + + with Pool() as pool: + for lambda_val in lambda_vals2: + args_list = [(c, lambda_val, simulation_time, max_loss_value) for _ in range(num_runs)] + results = pool.map(run_single_simulation, args_list) + + successful_results = [res for res in results if res is not None] + loss_rates = [res[1] for res in successful_results] + + n = len(loss_rates) + + if n >= min_runs: + # statistics + mean_loss = np.mean(loss_rates) + std_dev = np.std(loss_rates, ddof=1) + + # confidence interval + t_value = t.ppf((1 + confidence_level)/2, n-1) + ci = t_value * std_dev / np.sqrt(n) + + # store results + lambda_points.append(lambda_val) + losses.append(mean_loss) + ci_lower.append(mean_loss - ci) + ci_upper.append(mean_loss + ci) + + print(f"C={c}, λ={lambda_val:.4f}, Mean Loss Rate={mean_loss:.2%} ± {ci:.2f}") + elif n > 0: + print(f"λ={lambda_val:.4f} skipped - only {n} successful run(s)") + continue + else: + print(f"Stopped at λ={lambda_val:.4f} - no successful run") + break + + plt.plot(lambda_points, losses, label=f'C={c}') + plt.fill_between(lambda_points, ci_lower, ci_upper, alpha=0.2) + + plt.xlabel('Arrival Rate (λ)') + plt.ylabel('Mean Loss Rate') + plt.title(f'Mean Loss Rate vs Arrival Rate ({num_runs} runs, 95% CI)') + plt.legend() + plt.grid(True) + plt.show() + +