Add simulation_tool/src/simulation_tool/core.py
This commit is contained in:
parent
f32269e351
commit
53afd383cc
1 changed files with 111 additions and 0 deletions
111
simulation_tool/src/simulation_tool/core.py
Normal file
111
simulation_tool/src/simulation_tool/core.py
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import statistics
|
||||||
|
from typing import Dict, List
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from simulation_tool.models import SimulationResults
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
|
class SimulationError(Exception):
|
||||||
|
"""Custom Exception for any simulation-level error."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_schedule_params(schedule_params: Dict) -> None:
|
||||||
|
"""Validate schedule parameters strictly to ensure correct simulation configuration."""
|
||||||
|
if not isinstance(schedule_params, dict):
|
||||||
|
raise SimulationError("schedule_params must be a dictionary.")
|
||||||
|
required_keys = {"threads", "duration", "mechanism"}
|
||||||
|
missing = required_keys - schedule_params.keys()
|
||||||
|
if missing:
|
||||||
|
raise SimulationError(f"Missing required schedule parameters: {missing}")
|
||||||
|
|
||||||
|
threads = schedule_params.get("threads")
|
||||||
|
if not isinstance(threads, int) or threads <= 0:
|
||||||
|
raise SimulationError("'threads' must be a positive integer.")
|
||||||
|
|
||||||
|
duration = schedule_params.get("duration")
|
||||||
|
if not isinstance(duration, (int, float)) or duration <= 0:
|
||||||
|
raise SimulationError("'duration' must be a positive number.")
|
||||||
|
|
||||||
|
mechanism = schedule_params.get("mechanism")
|
||||||
|
if not isinstance(mechanism, str):
|
||||||
|
raise SimulationError("'mechanism' must be a string.")
|
||||||
|
|
||||||
|
|
||||||
|
def _simulate_time_distributions(threads: int, duration: float, mechanism: str) -> List[float]:
|
||||||
|
"""Simulate time distributions based on scheduling parameters."""
|
||||||
|
random.seed(42)
|
||||||
|
np.random.seed(42)
|
||||||
|
|
||||||
|
base_count = max(100, threads * 50)
|
||||||
|
|
||||||
|
if mechanism.lower() == "round_robin":
|
||||||
|
latencies = np.random.normal(loc=duration / 2, scale=duration / 10, size=base_count)
|
||||||
|
elif mechanism.lower() == "priority_queue":
|
||||||
|
latencies = np.random.lognormal(mean=np.log(duration / 3), sigma=0.5, size=base_count)
|
||||||
|
elif mechanism.lower() == "dynamic":
|
||||||
|
latencies = np.random.gamma(shape=2.0, scale=duration / 5, size=base_count)
|
||||||
|
else:
|
||||||
|
latencies = np.random.uniform(low=duration / 4, high=duration, size=base_count)
|
||||||
|
|
||||||
|
latencies = [abs(float(x)) for x in latencies]
|
||||||
|
return latencies
|
||||||
|
|
||||||
|
|
||||||
|
def _calculate_outliers(values: List[float]) -> int:
|
||||||
|
"""Calculate number of outliers using IQR method."""
|
||||||
|
if not values:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
q1 = np.percentile(values, 25)
|
||||||
|
q3 = np.percentile(values, 75)
|
||||||
|
iqr = q3 - q1
|
||||||
|
lower_bound = q1 - 1.5 * iqr
|
||||||
|
upper_bound = q3 + 1.5 * iqr
|
||||||
|
|
||||||
|
outliers = [v for v in values if v < lower_bound or v > upper_bound]
|
||||||
|
return len(outliers)
|
||||||
|
|
||||||
|
|
||||||
|
def simulate_scheduling(schedule_params: Dict) -> SimulationResults:
|
||||||
|
"""Führt eine Simulation verschiedener Scheduling-Parameter durch und liefert Metriken.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
schedule_params (Dict): Parameter zur Definition des Scheduling-Verhaltens.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SimulationResults: Simulationsergebnisse inklusive Zeitverteilungen und Outlier-Statistik.
|
||||||
|
"""
|
||||||
|
logger.info("Starting scheduling simulation with parameters: %s", schedule_params)
|
||||||
|
|
||||||
|
_validate_schedule_params(schedule_params)
|
||||||
|
|
||||||
|
threads = schedule_params.get("threads")
|
||||||
|
duration = schedule_params.get("duration")
|
||||||
|
mechanism = schedule_params.get("mechanism")
|
||||||
|
|
||||||
|
try:
|
||||||
|
latencies = _simulate_time_distributions(threads, duration, mechanism)
|
||||||
|
outliers = _calculate_outliers(latencies)
|
||||||
|
|
||||||
|
if pd.isna(outliers):
|
||||||
|
outliers = 0
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("Simulation failed due to internal error.")
|
||||||
|
raise SimulationError(f"Simulation failed: {e}") from e
|
||||||
|
|
||||||
|
assert isinstance(latencies, list), "latencies must be a list of floats"
|
||||||
|
assert all(isinstance(x, float) for x in latencies), "All latencies must be floats"
|
||||||
|
|
||||||
|
results = SimulationResults(time_distributions=latencies, outlier_occurrences=outliers)
|
||||||
|
logger.info("Simulation completed successfully with %d outliers.", outliers)
|
||||||
|
return results
|
||||||
Loading…
Reference in a new issue