Skip to content

Commit 74d1918

Browse files
committed
fix for TMPA reporting
1 parent 1ee47c5 commit 74d1918

File tree

2 files changed

+95
-39
lines changed

2 files changed

+95
-39
lines changed

ngraph/monte_carlo/results.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,44 @@ def success_rate_distribution(self) -> pd.DataFrame:
235235
Returns:
236236
DataFrame with success rates across iterations.
237237
"""
238-
results = []
239-
for i, result in enumerate(self.raw_results["results"]):
240-
success_rate = result.get("overall_placement_ratio", 0.0)
241-
results.append({"iteration": i, "success_rate": success_rate})
242-
return pd.DataFrame(results)
238+
# Support two shapes for raw_results["results"]:
239+
# 1) List[dict] with per-iteration overall summary containing
240+
# {"overall_placement_ratio": float}
241+
# 2) List[list[FlowResult]] where FlowResult are per-demand dicts
242+
# with metric == "placement_ratio" and field "value" in [0, 1].
243+
rows: list[dict[str, float | int]] = []
244+
raw = self.raw_results.get("results", [])
245+
for i, entry in enumerate(raw):
246+
# Shape 1: dict with overall_placement_ratio
247+
if isinstance(entry, dict):
248+
try:
249+
success_rate = float(entry.get("overall_placement_ratio", 0.0))
250+
except Exception:
251+
success_rate = 0.0
252+
rows.append({"iteration": i, "success_rate": success_rate})
253+
continue
254+
255+
# Shape 2: list of FlowResult dicts → aggregate mean of ratios
256+
if isinstance(entry, list):
257+
ratios: list[float] = []
258+
for fr in entry:
259+
if not isinstance(fr, dict):
260+
continue
261+
metric = fr.get("metric")
262+
if metric != "placement_ratio":
263+
continue
264+
try:
265+
ratios.append(float(fr.get("value", 0.0)))
266+
except Exception:
267+
continue
268+
agg = sum(ratios) / len(ratios) if ratios else 0.0
269+
rows.append({"iteration": i, "success_rate": float(agg)})
270+
continue
271+
272+
# Unknown entry type – record zero conservatively
273+
rows.append({"iteration": i, "success_rate": 0.0})
274+
275+
return pd.DataFrame(rows)
243276

244277
def summary_statistics(self) -> dict[str, float]:
245278
"""Get summary statistics for success rates.

ngraph/workflow/traffic_matrix_placement_analysis.py

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -368,47 +368,61 @@ def run(self, scenario: "Scenario") -> None:
368368

369369
# INFO-level outcome summary for workflow users
370370
try:
371-
stats = None
371+
from ngraph.monte_carlo.results import DemandPlacementResults
372+
373+
dpr = DemandPlacementResults(
374+
raw_results=results.raw_results,
375+
iterations=results.iterations,
376+
baseline=results.baseline,
377+
failure_patterns=results.failure_patterns,
378+
metadata=results.metadata,
379+
)
380+
381+
# Compute per-iteration success rates and summary statistics
382+
dist_df = dpr.success_rate_distribution()
383+
stats = dpr.summary_statistics() if not dist_df.empty else {}
384+
385+
# Also compute an overall demand-level mean from envelopes for validation
372386
try:
373-
from ngraph.monte_carlo.results import DemandPlacementResults
374-
375-
dpr = DemandPlacementResults(
376-
raw_results=results.raw_results,
377-
iterations=results.iterations,
378-
baseline=results.baseline,
379-
failure_patterns=results.failure_patterns,
380-
metadata=results.metadata,
387+
envelope_means = [
388+
float(env.get("mean", 0.0)) for env in envelopes.values()
389+
]
390+
overall_envelope_mean = (
391+
sum(envelope_means) / len(envelope_means) if envelope_means else 0.0
381392
)
382-
stats = dpr.summary_statistics()
383393
except Exception:
384-
# Fallback: compute mean from overall_placement_ratio if present
385-
ratios = [
386-
float(r.get("overall_placement_ratio", 0.0))
387-
for r in results.raw_results.get("results", [])
388-
if isinstance(r, dict)
389-
]
390-
if ratios:
391-
import statistics
394+
overall_envelope_mean = 0.0
392395

393-
stats = {
394-
"mean": float(statistics.mean(ratios)),
395-
"min": float(min(ratios)),
396-
"max": float(max(ratios)),
397-
}
396+
# Add a concise per-step summary object to the results store
397+
scenario.results.put(
398+
self.name,
399+
"placement_summary",
400+
{
401+
"iterations": int(results.metadata.get("iterations", 0)),
402+
"parallelism": int(
403+
results.metadata.get(
404+
"parallelism", self._resolve_parallelism(self.parallelism)
405+
)
406+
),
407+
"baseline": bool(results.metadata.get("baseline", False)),
408+
"alpha": float(step_metadata.get("alpha", 1.0)),
409+
"alpha_source": step_metadata.get("alpha_source", None),
410+
"demand_count": len(envelopes),
411+
"success_rate_stats": stats or {},
412+
"overall_envelope_mean": float(overall_envelope_mean),
413+
},
414+
)
398415

416+
# Prepare INFO log with consistent fields
399417
meta = results.metadata or {}
400418
iterations = int(meta.get("iterations", self.iterations))
401419
workers = int(
402420
meta.get("parallelism", self._resolve_parallelism(self.parallelism))
403421
)
404-
# Alpha transparency: print effective alpha and its source (explicit or MSD:<step>)
405422
try:
406423
alpha_value = float(step_metadata.get("alpha")) # type: ignore[arg-type]
407424
except Exception:
408-
try:
409-
alpha_value = float(effective_alpha)
410-
except Exception:
411-
alpha_value = 1.0
425+
alpha_value = float(effective_alpha) if effective_alpha else 1.0
412426
alpha_source = (
413427
step_metadata.get("alpha_source")
414428
if isinstance(step_metadata, dict)
@@ -419,21 +433,30 @@ def run(self, scenario: "Scenario") -> None:
419433
if alpha_source
420434
else ("explicit" if not isinstance(self.alpha, str) else "auto")
421435
)
436+
437+
mean_v = float(stats.get("mean", 0.0)) if stats else 0.0
438+
p50_v = float(stats.get("p50", 0.0)) if stats else 0.0
439+
p95_v = float(stats.get("p95", 0.0)) if stats else 0.0
440+
min_v = float(stats.get("min", 0.0)) if stats else 0.0
441+
max_v = float(stats.get("max", 0.0)) if stats else 0.0
442+
422443
logger.info(
423-
"Placement summary: name=%s alpha=%.6g source=%s demands=%d iters=%d workers=%d ratio_mean=%.3f p50=%.3f p95=%.3f min=%.3f max=%.3f",
444+
"Placement summary: name=%s alpha=%.6g source=%s demands=%d iters=%d workers=%d iter_mean=%.4f p50=%.4f p95=%.4f min=%.4f max=%.4f env_mean=%.4f",
424445
self.name,
425446
alpha_value,
426447
alpha_source_str,
427448
len(envelopes),
428449
iterations,
429450
workers,
430-
float(stats.get("mean", 0.0)) if stats else 0.0,
431-
float(stats.get("p50", 0.0)) if stats else 0.0,
432-
float(stats.get("p95", 0.0)) if stats else 0.0,
433-
float(stats.get("min", 0.0)) if stats else 0.0,
434-
float(stats.get("max", 0.0)) if stats else 0.0,
451+
mean_v,
452+
p50_v,
453+
p95_v,
454+
min_v,
455+
max_v,
456+
overall_envelope_mean,
435457
)
436458
except Exception:
459+
# Logging must not raise
437460
pass
438461

439462
logger.info(

0 commit comments

Comments
 (0)