kailash 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,23 +8,23 @@ execution patterns and performance characteristics.
8
8
 
9
9
  Examples:
10
10
  Basic profiling setup:
11
-
11
+
12
12
  >>> profiler = CycleProfiler(enable_advanced_metrics=True)
13
13
  >>> # Add execution traces
14
14
  >>> profiler.add_trace(execution_trace)
15
15
  >>> # Analyze performance
16
16
  >>> metrics = profiler.analyze_performance()
17
17
  >>> print(f"Overall efficiency: {metrics.efficiency_score}")
18
-
18
+
19
19
  Comparative analysis:
20
-
20
+
21
21
  >>> # Compare multiple cycles
22
22
  >>> comparison = profiler.compare_cycles(["cycle_1", "cycle_2", "cycle_3"])
23
23
  >>> print(f"Best cycle: {comparison['best_cycle']}")
24
24
  >>> print(f"Performance gaps: {comparison['significant_differences']}")
25
-
25
+
26
26
  Optimization recommendations:
27
-
27
+
28
28
  >>> # Get actionable recommendations
29
29
  >>> recommendations = profiler.get_optimization_recommendations()
30
30
  >>> for rec in recommendations:
@@ -32,9 +32,9 @@ Examples:
32
32
  ... print(f" Suggestion: {rec['suggestion']}")
33
33
  ... print(f" Current: {rec['current_value']}")
34
34
  ... print(f" Target: {rec['target_improvement']}")
35
-
35
+
36
36
  Comprehensive reporting:
37
-
37
+
38
38
  >>> # Generate detailed report
39
39
  >>> report = profiler.generate_performance_report()
40
40
  >>> # Export for external analysis
@@ -56,11 +56,11 @@ logger = logging.getLogger(__name__)
56
56
  class PerformanceMetrics:
57
57
  """
58
58
  Comprehensive performance metrics for cycle analysis.
59
-
59
+
60
60
  This class aggregates and analyzes performance data from cycle executions,
61
61
  providing detailed insights into timing, resource usage, and efficiency
62
62
  characteristics of cyclic workflows.
63
-
63
+
64
64
  Attributes:
65
65
  total_cycles: Number of cycles analyzed.
66
66
  total_iterations: Total iterations across all cycles.
@@ -75,12 +75,12 @@ class PerformanceMetrics:
75
75
  bottlenecks: Identified performance bottlenecks.
76
76
  optimization_opportunities: Suggested optimizations.
77
77
  """
78
-
78
+
79
79
  total_cycles: int = 0
80
80
  total_iterations: int = 0
81
81
  avg_cycle_time: float = 0.0
82
82
  avg_iteration_time: float = 0.0
83
- min_iteration_time: float = float('inf')
83
+ min_iteration_time: float = float("inf")
84
84
  max_iteration_time: float = 0.0
85
85
  iteration_time_stddev: float = 0.0
86
86
  memory_stats: Dict[str, float] = field(default_factory=dict)
@@ -88,10 +88,10 @@ class PerformanceMetrics:
88
88
  convergence_stats: Dict[str, Any] = field(default_factory=dict)
89
89
  bottlenecks: List[str] = field(default_factory=list)
90
90
  optimization_opportunities: List[str] = field(default_factory=list)
91
-
91
+
92
92
  def to_dict(self) -> Dict[str, Any]:
93
93
  """Convert metrics to dictionary for serialization.
94
-
94
+
95
95
  Returns:
96
96
  Dictionary representation of performance metrics.
97
97
  """
@@ -101,69 +101,75 @@ class PerformanceMetrics:
101
101
  "timing": {
102
102
  "avg_cycle_time": self.avg_cycle_time,
103
103
  "avg_iteration_time": self.avg_iteration_time,
104
- "min_iteration_time": self.min_iteration_time if self.min_iteration_time != float('inf') else 0.0,
104
+ "min_iteration_time": (
105
+ self.min_iteration_time
106
+ if self.min_iteration_time != float("inf")
107
+ else 0.0
108
+ ),
105
109
  "max_iteration_time": self.max_iteration_time,
106
- "iteration_time_stddev": self.iteration_time_stddev
110
+ "iteration_time_stddev": self.iteration_time_stddev,
107
111
  },
108
112
  "memory_stats": self.memory_stats,
109
113
  "cpu_stats": self.cpu_stats,
110
114
  "convergence_stats": self.convergence_stats,
111
115
  "bottlenecks": self.bottlenecks,
112
- "optimization_opportunities": self.optimization_opportunities
116
+ "optimization_opportunities": self.optimization_opportunities,
113
117
  }
114
118
 
115
119
 
116
120
  class CycleProfiler:
117
121
  """
118
122
  Advanced profiling and performance analysis for cyclic workflows.
119
-
123
+
120
124
  This class provides comprehensive performance analysis capabilities for
121
125
  cycles, including statistical analysis, bottleneck identification,
122
126
  comparative analysis across multiple cycles, and detailed optimization
123
127
  recommendations based on execution patterns.
124
-
128
+
125
129
  Examples:
126
130
  >>> profiler = CycleProfiler()
127
131
  >>> profiler.add_trace(execution_trace)
128
132
  >>> metrics = profiler.analyze_performance()
129
133
  >>> recommendations = profiler.get_optimization_recommendations()
130
134
  """
131
-
135
+
132
136
  def __init__(self, enable_advanced_metrics: bool = True):
133
137
  """
134
138
  Initialize cycle profiler.
135
-
139
+
136
140
  Args:
137
141
  enable_advanced_metrics: Whether to enable advanced statistical analysis.
138
142
  """
139
143
  self.enable_advanced_metrics = enable_advanced_metrics
140
144
  self.traces: List[CycleExecutionTrace] = []
141
145
  self.performance_history: List[PerformanceMetrics] = []
142
-
146
+
143
147
  def add_trace(self, trace: CycleExecutionTrace):
144
148
  """
145
149
  Add a cycle execution trace for analysis.
146
-
150
+
147
151
  Args:
148
152
  trace: Completed execution trace to analyze.
149
-
153
+
150
154
  Examples:
151
155
  >>> profiler.add_trace(execution_trace)
152
156
  """
153
157
  self.traces.append(trace)
154
- logger.debug(f"Added trace for cycle '{trace.cycle_id}' with {len(trace.iterations)} iterations")
155
-
158
+ logger.debug(
159
+ f"Added trace for cycle '{trace.cycle_id}' with {len(trace.iterations)} iterations"
160
+ )
161
+
156
162
  def analyze_performance(self) -> PerformanceMetrics:
157
163
  """
158
164
  Perform comprehensive performance analysis on all traces.
159
-
165
+
160
166
  Analyzes all collected traces to generate comprehensive performance
161
167
  metrics, identify bottlenecks, and provide optimization recommendations
162
168
  based on statistical analysis of execution patterns.
163
-
169
+
164
170
  Returns:
165
171
  Comprehensive performance analysis results.
166
-
172
+
167
173
  Examples:
168
174
  >>> metrics = profiler.analyze_performance()
169
175
  >>> print(f"Average cycle time: {metrics.avg_cycle_time:.3f}s")
@@ -171,18 +177,18 @@ class CycleProfiler:
171
177
  if not self.traces:
172
178
  logger.warning("No traces available for performance analysis")
173
179
  return PerformanceMetrics()
174
-
180
+
175
181
  # Collect all timing data
176
182
  cycle_times = []
177
183
  iteration_times = []
178
184
  memory_values = []
179
185
  cpu_values = []
180
186
  convergence_data = []
181
-
187
+
182
188
  for trace in self.traces:
183
189
  if trace.total_execution_time:
184
190
  cycle_times.append(trace.total_execution_time)
185
-
191
+
186
192
  for iteration in trace.iterations:
187
193
  if iteration.execution_time:
188
194
  iteration_times.append(iteration.execution_time)
@@ -192,34 +198,36 @@ class CycleProfiler:
192
198
  cpu_values.append(iteration.cpu_usage_percent)
193
199
  if iteration.convergence_value:
194
200
  convergence_data.append(iteration.convergence_value)
195
-
201
+
196
202
  # Calculate basic metrics
197
203
  metrics = PerformanceMetrics(
198
204
  total_cycles=len(self.traces),
199
- total_iterations=sum(len(trace.iterations) for trace in self.traces)
205
+ total_iterations=sum(len(trace.iterations) for trace in self.traces),
200
206
  )
201
-
207
+
202
208
  # Timing analysis
203
209
  if cycle_times:
204
210
  metrics.avg_cycle_time = statistics.mean(cycle_times)
205
-
211
+
206
212
  if iteration_times:
207
213
  metrics.avg_iteration_time = statistics.mean(iteration_times)
208
214
  metrics.min_iteration_time = min(iteration_times)
209
215
  metrics.max_iteration_time = max(iteration_times)
210
216
  if len(iteration_times) > 1:
211
217
  metrics.iteration_time_stddev = statistics.stdev(iteration_times)
212
-
218
+
213
219
  # Memory analysis
214
220
  if memory_values:
215
221
  metrics.memory_stats = {
216
222
  "avg": statistics.mean(memory_values),
217
223
  "min": min(memory_values),
218
224
  "max": max(memory_values),
219
- "stddev": statistics.stdev(memory_values) if len(memory_values) > 1 else 0.0,
220
- "median": statistics.median(memory_values)
225
+ "stddev": (
226
+ statistics.stdev(memory_values) if len(memory_values) > 1 else 0.0
227
+ ),
228
+ "median": statistics.median(memory_values),
221
229
  }
222
-
230
+
223
231
  # CPU analysis
224
232
  if cpu_values:
225
233
  metrics.cpu_stats = {
@@ -227,290 +235,316 @@ class CycleProfiler:
227
235
  "min": min(cpu_values),
228
236
  "max": max(cpu_values),
229
237
  "stddev": statistics.stdev(cpu_values) if len(cpu_values) > 1 else 0.0,
230
- "median": statistics.median(cpu_values)
238
+ "median": statistics.median(cpu_values),
231
239
  }
232
-
240
+
233
241
  # Convergence analysis
234
242
  if convergence_data:
235
- metrics.convergence_stats = self._analyze_convergence_performance(convergence_data)
236
-
243
+ metrics.convergence_stats = self._analyze_convergence_performance(
244
+ convergence_data
245
+ )
246
+
237
247
  # Advanced analysis if enabled
238
248
  if self.enable_advanced_metrics:
239
249
  metrics.bottlenecks = self._identify_bottlenecks(metrics)
240
250
  metrics.optimization_opportunities = self._identify_optimizations(metrics)
241
-
251
+
242
252
  # Store in history
243
253
  self.performance_history.append(metrics)
244
-
254
+
245
255
  logger.info(
246
256
  f"Analyzed performance for {metrics.total_cycles} cycles, "
247
257
  f"{metrics.total_iterations} iterations, "
248
258
  f"avg cycle time: {metrics.avg_cycle_time:.3f}s"
249
259
  )
250
-
260
+
251
261
  return metrics
252
-
262
+
253
263
  def compare_cycles(self, cycle_ids: List[str]) -> Dict[str, Any]:
254
264
  """
255
265
  Compare performance across multiple specific cycles.
256
-
266
+
257
267
  Provides detailed comparative analysis between specific cycles,
258
268
  highlighting performance differences, convergence patterns, and
259
269
  relative efficiency metrics.
260
-
270
+
261
271
  Args:
262
272
  cycle_ids (List[str]): List of cycle IDs to compare
263
-
273
+
264
274
  Returns:
265
275
  Dict[str, Any]: Comparative analysis results
266
-
276
+
267
277
  Side Effects:
268
278
  None - this is a pure analysis method
269
-
279
+
270
280
  Example:
271
281
  >>> comparison = profiler.compare_cycles(["cycle_1", "cycle_2"])
272
282
  >>> print(f"Best performing cycle: {comparison['best_cycle']}")
273
283
  """
274
- relevant_traces = [trace for trace in self.traces if trace.cycle_id in cycle_ids]
275
-
284
+ relevant_traces = [
285
+ trace for trace in self.traces if trace.cycle_id in cycle_ids
286
+ ]
287
+
276
288
  if len(relevant_traces) < 2:
277
289
  return {"error": "Need at least 2 cycles for comparison"}
278
-
290
+
279
291
  comparison = {
280
292
  "cycles_compared": len(relevant_traces),
281
293
  "cycle_details": {},
282
294
  "performance_ranking": [],
283
- "significant_differences": []
295
+ "significant_differences": [],
284
296
  }
285
-
297
+
286
298
  # Analyze each cycle
287
299
  for trace in relevant_traces:
288
300
  stats = trace.get_statistics()
289
-
301
+
290
302
  comparison["cycle_details"][trace.cycle_id] = {
291
303
  "execution_time": trace.total_execution_time,
292
304
  "iterations": len(trace.iterations),
293
305
  "converged": trace.converged,
294
306
  "efficiency_score": stats["efficiency_score"],
295
307
  "avg_iteration_time": stats["avg_iteration_time"],
296
- "convergence_rate": stats["convergence_rate"]
308
+ "convergence_rate": stats["convergence_rate"],
297
309
  }
298
-
310
+
299
311
  # Rank by efficiency score
300
312
  ranking = sorted(
301
313
  comparison["cycle_details"].items(),
302
314
  key=lambda x: x[1]["efficiency_score"],
303
- reverse=True
315
+ reverse=True,
304
316
  )
305
317
  comparison["performance_ranking"] = [cycle_id for cycle_id, _ in ranking]
306
318
  comparison["best_cycle"] = ranking[0][0] if ranking else None
307
319
  comparison["worst_cycle"] = ranking[-1][0] if ranking else None
308
-
320
+
309
321
  # Identify significant differences
310
322
  if len(ranking) >= 2:
311
323
  best_score = ranking[0][1]["efficiency_score"]
312
324
  worst_score = ranking[-1][1]["efficiency_score"]
313
-
325
+
314
326
  if best_score - worst_score > 0.2:
315
327
  comparison["significant_differences"].append(
316
328
  f"Large efficiency gap: {best_score:.2f} vs {worst_score:.2f}"
317
329
  )
318
-
330
+
319
331
  # Compare convergence rates
320
332
  convergence_rates = [details["convergence_rate"] for _, details in ranking]
321
333
  if max(convergence_rates) - min(convergence_rates) > 0.3:
322
334
  comparison["significant_differences"].append(
323
335
  "Significant variation in convergence rates"
324
336
  )
325
-
337
+
326
338
  return comparison
327
-
328
- def get_optimization_recommendations(self, trace: Optional[CycleExecutionTrace] = None) -> List[Dict[str, Any]]:
339
+
340
+ def get_optimization_recommendations(
341
+ self, trace: Optional[CycleExecutionTrace] = None
342
+ ) -> List[Dict[str, Any]]:
329
343
  """
330
344
  Generate detailed optimization recommendations.
331
-
345
+
332
346
  Provides specific, actionable optimization recommendations based on
333
347
  performance analysis, including parameter tuning suggestions,
334
348
  algorithmic improvements, and resource optimization strategies.
335
-
349
+
336
350
  Args:
337
351
  trace (Optional[CycleExecutionTrace]): Specific trace to analyze, or None for overall recommendations
338
-
352
+
339
353
  Returns:
340
354
  List[Dict[str, Any]]: List of optimization recommendations with details
341
-
355
+
342
356
  Side Effects:
343
357
  None - this is a pure analysis method
344
-
358
+
345
359
  Example:
346
360
  >>> recommendations = profiler.get_optimization_recommendations()
347
361
  >>> for rec in recommendations:
348
362
  ... print(f"{rec['priority']}: {rec['description']}")
349
363
  """
350
364
  recommendations = []
351
-
365
+
352
366
  if trace:
353
367
  traces_to_analyze = [trace]
354
368
  else:
355
369
  traces_to_analyze = self.traces
356
-
370
+
357
371
  if not traces_to_analyze:
358
372
  return recommendations
359
-
373
+
360
374
  # Analyze all traces for patterns
361
375
  self.analyze_performance() if not trace else None
362
-
376
+
363
377
  for target_trace in traces_to_analyze:
364
378
  stats = target_trace.get_statistics()
365
-
379
+
366
380
  # Efficiency recommendations
367
381
  if stats["efficiency_score"] < 0.3:
368
- recommendations.append({
369
- "priority": "HIGH",
370
- "category": "efficiency",
371
- "description": "Very low efficiency detected",
372
- "suggestion": "Consider reducing max_iterations or improving convergence condition",
373
- "cycle_id": target_trace.cycle_id,
374
- "current_value": stats["efficiency_score"],
375
- "target_improvement": "Increase to > 0.5"
376
- })
377
-
378
- # Convergence recommendations
379
- if not target_trace.converged:
380
- if stats["total_iterations"] >= (target_trace.max_iterations_configured or 0):
381
- recommendations.append({
382
+ recommendations.append(
383
+ {
382
384
  "priority": "HIGH",
383
- "category": "convergence",
384
- "description": "Cycle reached max_iterations without converging",
385
- "suggestion": "Increase max_iterations or improve algorithm",
385
+ "category": "efficiency",
386
+ "description": "Very low efficiency detected",
387
+ "suggestion": "Consider reducing max_iterations or improving convergence condition",
386
388
  "cycle_id": target_trace.cycle_id,
387
- "current_value": target_trace.max_iterations_configured,
388
- "target_improvement": f"Increase to {int((target_trace.max_iterations_configured or 10) * 1.5)}"
389
- })
390
-
389
+ "current_value": stats["efficiency_score"],
390
+ "target_improvement": "Increase to > 0.5",
391
+ }
392
+ )
393
+
394
+ # Convergence recommendations
395
+ if not target_trace.converged:
396
+ if stats["total_iterations"] >= (
397
+ target_trace.max_iterations_configured or 0
398
+ ):
399
+ recommendations.append(
400
+ {
401
+ "priority": "HIGH",
402
+ "category": "convergence",
403
+ "description": "Cycle reached max_iterations without converging",
404
+ "suggestion": "Increase max_iterations or improve algorithm",
405
+ "cycle_id": target_trace.cycle_id,
406
+ "current_value": target_trace.max_iterations_configured,
407
+ "target_improvement": f"Increase to {int((target_trace.max_iterations_configured or 10) * 1.5)}",
408
+ }
409
+ )
410
+
391
411
  # Performance recommendations
392
412
  if stats["avg_iteration_time"] > 0.5:
393
- recommendations.append({
394
- "priority": "MEDIUM",
395
- "category": "performance",
396
- "description": "High average iteration time",
397
- "suggestion": "Optimize node execution or reduce data processing",
398
- "cycle_id": target_trace.cycle_id,
399
- "current_value": stats["avg_iteration_time"],
400
- "target_improvement": "Reduce to < 0.5s per iteration"
401
- })
402
-
413
+ recommendations.append(
414
+ {
415
+ "priority": "MEDIUM",
416
+ "category": "performance",
417
+ "description": "High average iteration time",
418
+ "suggestion": "Optimize node execution or reduce data processing",
419
+ "cycle_id": target_trace.cycle_id,
420
+ "current_value": stats["avg_iteration_time"],
421
+ "target_improvement": "Reduce to < 0.5s per iteration",
422
+ }
423
+ )
424
+
403
425
  # Memory recommendations
404
426
  if target_trace.memory_peak_mb and target_trace.memory_peak_mb > 1000:
405
- recommendations.append({
406
- "priority": "MEDIUM",
407
- "category": "memory",
408
- "description": "High memory usage detected",
409
- "suggestion": "Consider data streaming, chunking, or garbage collection",
410
- "cycle_id": target_trace.cycle_id,
411
- "current_value": target_trace.memory_peak_mb,
412
- "target_improvement": "Reduce to < 1000 MB"
413
- })
414
-
427
+ recommendations.append(
428
+ {
429
+ "priority": "MEDIUM",
430
+ "category": "memory",
431
+ "description": "High memory usage detected",
432
+ "suggestion": "Consider data streaming, chunking, or garbage collection",
433
+ "cycle_id": target_trace.cycle_id,
434
+ "current_value": target_trace.memory_peak_mb,
435
+ "target_improvement": "Reduce to < 1000 MB",
436
+ }
437
+ )
438
+
415
439
  # Convergence pattern recommendations
416
440
  convergence_trend = target_trace.get_convergence_trend()
417
441
  if convergence_trend:
418
442
  pattern_analysis = self._analyze_convergence_pattern(convergence_trend)
419
443
  if pattern_analysis["unstable"]:
420
- recommendations.append({
421
- "priority": "HIGH",
422
- "category": "stability",
423
- "description": "Unstable convergence pattern detected",
424
- "suggestion": "Reduce learning rate or add regularization",
425
- "cycle_id": target_trace.cycle_id,
426
- "current_value": "Unstable",
427
- "target_improvement": "Stable convergence"
428
- })
429
-
444
+ recommendations.append(
445
+ {
446
+ "priority": "HIGH",
447
+ "category": "stability",
448
+ "description": "Unstable convergence pattern detected",
449
+ "suggestion": "Reduce learning rate or add regularization",
450
+ "cycle_id": target_trace.cycle_id,
451
+ "current_value": "Unstable",
452
+ "target_improvement": "Stable convergence",
453
+ }
454
+ )
455
+
430
456
  # Sort by priority
431
457
  priority_order = {"HIGH": 0, "MEDIUM": 1, "LOW": 2}
432
458
  recommendations.sort(key=lambda x: priority_order.get(x["priority"], 3))
433
-
459
+
434
460
  return recommendations
435
-
461
+
436
462
  def generate_performance_report(self) -> Dict[str, Any]:
437
463
  """
438
464
  Generate comprehensive performance report.
439
-
465
+
440
466
  Creates a detailed performance report including metrics analysis,
441
467
  recommendations, trends, and comparative insights across all
442
468
  analyzed cycles.
443
-
469
+
444
470
  Returns:
445
471
  Dict[str, Any]: Comprehensive performance report
446
-
472
+
447
473
  Side Effects:
448
474
  None - this is a pure analysis method
449
-
475
+
450
476
  Example:
451
477
  >>> report = profiler.generate_performance_report()
452
478
  >>> print(f"Overall score: {report['overall_score']}")
453
479
  """
454
480
  metrics = self.analyze_performance()
455
-
481
+
456
482
  # Calculate overall performance score
457
483
  overall_score = self._calculate_overall_score(metrics)
458
-
484
+
459
485
  # Generate trend analysis if we have history
460
- trend_analysis = self._analyze_performance_trends() if len(self.performance_history) > 1 else None
461
-
486
+ trend_analysis = (
487
+ self._analyze_performance_trends()
488
+ if len(self.performance_history) > 1
489
+ else None
490
+ )
491
+
462
492
  # Get top recommendations
463
493
  recommendations = self.get_optimization_recommendations()
464
-
494
+
465
495
  report = {
466
496
  "summary": {
467
497
  "overall_score": overall_score,
468
498
  "total_cycles_analyzed": metrics.total_cycles,
469
499
  "total_iterations": metrics.total_iterations,
470
500
  "avg_cycle_time": metrics.avg_cycle_time,
471
- "primary_bottlenecks": metrics.bottlenecks[:3]
501
+ "primary_bottlenecks": metrics.bottlenecks[:3],
472
502
  },
473
503
  "detailed_metrics": metrics.to_dict(),
474
504
  "trend_analysis": trend_analysis,
475
505
  "recommendations": recommendations[:10], # Top 10 recommendations
476
506
  "cycle_comparisons": self._get_cycle_comparisons(),
477
- "generated_at": datetime.now().isoformat()
507
+ "generated_at": datetime.now().isoformat(),
478
508
  }
479
-
509
+
480
510
  return report
481
-
511
+
482
512
  def export_profile_data(self, filepath: str, format: str = "json"):
483
513
  """
484
514
  Export profiling data for external analysis.
485
-
515
+
486
516
  Args:
487
517
  filepath (str): Output file path
488
518
  format (str): Export format ("json", "csv")
489
-
519
+
490
520
  Side Effects:
491
521
  Creates file with profiling data
492
-
522
+
493
523
  Example:
494
524
  >>> profiler.export_profile_data("profile_analysis.json")
495
525
  """
496
526
  report = self.generate_performance_report()
497
-
527
+
498
528
  if format == "json":
499
529
  import json
500
- with open(filepath, 'w') as f:
530
+
531
+ with open(filepath, "w") as f:
501
532
  json.dump(report, f, indent=2)
502
533
  elif format == "csv":
503
534
  import csv
504
- with open(filepath, 'w', newline='') as f:
535
+
536
+ with open(filepath, "w", newline="") as f:
505
537
  writer = csv.writer(f)
506
-
538
+
507
539
  # Write summary data
508
540
  writer.writerow(["Metric", "Value"])
509
541
  writer.writerow(["Overall Score", report["summary"]["overall_score"]])
510
- writer.writerow(["Total Cycles", report["summary"]["total_cycles_analyzed"]])
542
+ writer.writerow(
543
+ ["Total Cycles", report["summary"]["total_cycles_analyzed"]]
544
+ )
511
545
  writer.writerow(["Avg Cycle Time", report["summary"]["avg_cycle_time"]])
512
546
  writer.writerow([])
513
-
547
+
514
548
  # Write detailed metrics
515
549
  writer.writerow(["Detailed Metrics"])
516
550
  metrics = report["detailed_metrics"]
@@ -522,150 +556,186 @@ class CycleProfiler:
522
556
  writer.writerow([key, value])
523
557
  else:
524
558
  raise ValueError(f"Unsupported export format: {format}")
525
-
559
+
526
560
  logger.info(f"Exported profiling data to {filepath} in {format} format")
527
-
528
- def _analyze_convergence_performance(self, convergence_data: List[float]) -> Dict[str, Any]:
561
+
562
+ def _analyze_convergence_performance(
563
+ self, convergence_data: List[float]
564
+ ) -> Dict[str, Any]:
529
565
  """Analyze convergence performance characteristics."""
530
566
  if not convergence_data:
531
567
  return {}
532
-
568
+
533
569
  return {
534
570
  "avg_convergence": statistics.mean(convergence_data),
535
571
  "min_convergence": min(convergence_data),
536
572
  "max_convergence": max(convergence_data),
537
- "convergence_stddev": statistics.stdev(convergence_data) if len(convergence_data) > 1 else 0.0,
538
- "convergence_trend": "improving" if convergence_data[0] > convergence_data[-1] else "degrading",
539
- "data_points": len(convergence_data)
573
+ "convergence_stddev": (
574
+ statistics.stdev(convergence_data) if len(convergence_data) > 1 else 0.0
575
+ ),
576
+ "convergence_trend": (
577
+ "improving"
578
+ if convergence_data[0] > convergence_data[-1]
579
+ else "degrading"
580
+ ),
581
+ "data_points": len(convergence_data),
540
582
  }
541
-
542
- def _analyze_convergence_pattern(self, convergence_trend: List[Tuple[int, Optional[float]]]) -> Dict[str, Any]:
583
+
584
+ def _analyze_convergence_pattern(
585
+ self, convergence_trend: List[Tuple[int, Optional[float]]]
586
+ ) -> Dict[str, Any]:
543
587
  """Analyze convergence pattern for stability."""
544
588
  valid_points = [value for _, value in convergence_trend if value is not None]
545
-
589
+
546
590
  if len(valid_points) < 3:
547
591
  return {"unstable": False, "reason": "insufficient_data"}
548
-
592
+
549
593
  # Calculate volatility
550
- differences = [abs(valid_points[i] - valid_points[i-1]) for i in range(1, len(valid_points))]
594
+ differences = [
595
+ abs(valid_points[i] - valid_points[i - 1])
596
+ for i in range(1, len(valid_points))
597
+ ]
551
598
  avg_difference = statistics.mean(differences)
552
599
  max_difference = max(differences)
553
-
600
+
554
601
  # Consider unstable if large swings or high volatility
555
602
  unstable = max_difference > (2 * avg_difference) and avg_difference > 0.1
556
-
603
+
557
604
  return {
558
605
  "unstable": unstable,
559
606
  "avg_volatility": avg_difference,
560
607
  "max_volatility": max_difference,
561
- "reason": "high_volatility" if unstable else "stable"
608
+ "reason": "high_volatility" if unstable else "stable",
562
609
  }
563
-
610
+
564
611
  def _identify_bottlenecks(self, metrics: PerformanceMetrics) -> List[str]:
565
612
  """Identify performance bottlenecks from metrics."""
566
613
  bottlenecks = []
567
-
614
+
568
615
  # High iteration time variance suggests inconsistent performance
569
616
  if metrics.iteration_time_stddev > metrics.avg_iteration_time * 0.5:
570
- bottlenecks.append("High iteration time variance - inconsistent node performance")
571
-
617
+ bottlenecks.append(
618
+ "High iteration time variance - inconsistent node performance"
619
+ )
620
+
572
621
  # Very slow iterations
573
622
  if metrics.max_iteration_time > metrics.avg_iteration_time * 3:
574
- bottlenecks.append("Outlier slow iterations detected - potential resource contention")
575
-
623
+ bottlenecks.append(
624
+ "Outlier slow iterations detected - potential resource contention"
625
+ )
626
+
576
627
  # Memory bottlenecks
577
628
  if metrics.memory_stats and metrics.memory_stats.get("max", 0) > 2000:
578
- bottlenecks.append("High memory usage - potential memory leaks or inefficient data handling")
579
-
629
+ bottlenecks.append(
630
+ "High memory usage - potential memory leaks or inefficient data handling"
631
+ )
632
+
580
633
  # CPU bottlenecks
581
634
  if metrics.cpu_stats and metrics.cpu_stats.get("avg", 0) > 80:
582
635
  bottlenecks.append("High CPU usage - computationally intensive operations")
583
-
636
+
584
637
  return bottlenecks
585
-
638
+
586
639
  def _identify_optimizations(self, metrics: PerformanceMetrics) -> List[str]:
587
640
  """Identify optimization opportunities."""
588
641
  optimizations = []
589
-
642
+
590
643
  # Low convergence rate suggests early termination opportunities
591
644
  convergence_rate = metrics.convergence_stats.get("avg_convergence")
592
645
  if convergence_rate and convergence_rate < 0.5:
593
- optimizations.append("Add early termination conditions for faster convergence")
594
-
646
+ optimizations.append(
647
+ "Add early termination conditions for faster convergence"
648
+ )
649
+
595
650
  # High memory variance suggests optimization potential
596
651
  if metrics.memory_stats and metrics.memory_stats.get("stddev", 0) > 100:
597
652
  optimizations.append("Optimize memory usage patterns for consistency")
598
-
653
+
599
654
  # Slow average iteration time
600
655
  if metrics.avg_iteration_time > 0.1:
601
656
  optimizations.append("Optimize node execution performance")
602
-
657
+
603
658
  return optimizations
604
-
659
+
605
660
  def _calculate_overall_score(self, metrics: PerformanceMetrics) -> float:
606
661
  """Calculate overall performance score (0-1, higher is better)."""
607
662
  score_components = []
608
-
663
+
609
664
  # Efficiency component (convergence rate)
610
665
  if metrics.convergence_stats:
611
666
  avg_convergence = metrics.convergence_stats.get("avg_convergence", 0.5)
612
667
  score_components.append(min(1.0, avg_convergence))
613
-
668
+
614
669
  # Speed component (based on iteration time)
615
670
  if metrics.avg_iteration_time > 0:
616
671
  speed_score = max(0.0, 1.0 - min(1.0, metrics.avg_iteration_time / 2.0))
617
672
  score_components.append(speed_score)
618
-
673
+
619
674
  # Consistency component (low variance is good)
620
675
  if metrics.iteration_time_stddev >= 0:
621
- consistency_score = max(0.0, 1.0 - min(1.0, metrics.iteration_time_stddev / metrics.avg_iteration_time if metrics.avg_iteration_time > 0 else 0))
676
+ consistency_score = max(
677
+ 0.0,
678
+ 1.0
679
+ - min(
680
+ 1.0,
681
+ (
682
+ metrics.iteration_time_stddev / metrics.avg_iteration_time
683
+ if metrics.avg_iteration_time > 0
684
+ else 0
685
+ ),
686
+ ),
687
+ )
622
688
  score_components.append(consistency_score)
623
-
689
+
624
690
  # Memory efficiency component
625
691
  if metrics.memory_stats:
626
692
  max_memory = metrics.memory_stats.get("max", 500)
627
- memory_score = max(0.0, 1.0 - min(1.0, max_memory / 2000)) # Penalty after 2GB
693
+ memory_score = max(
694
+ 0.0, 1.0 - min(1.0, max_memory / 2000)
695
+ ) # Penalty after 2GB
628
696
  score_components.append(memory_score)
629
-
697
+
630
698
  return statistics.mean(score_components) if score_components else 0.5
631
-
699
+
632
700
  def _analyze_performance_trends(self) -> Dict[str, Any]:
633
701
  """Analyze performance trends over time."""
634
702
  if len(self.performance_history) < 2:
635
703
  return {"trend": "insufficient_data"}
636
-
637
- recent_scores = [self._calculate_overall_score(m) for m in self.performance_history[-5:]]
638
-
704
+
705
+ recent_scores = [
706
+ self._calculate_overall_score(m) for m in self.performance_history[-5:]
707
+ ]
708
+
639
709
  if len(recent_scores) >= 2:
640
710
  trend = "improving" if recent_scores[-1] > recent_scores[0] else "degrading"
641
711
  trend_strength = abs(recent_scores[-1] - recent_scores[0])
642
712
  else:
643
713
  trend = "stable"
644
714
  trend_strength = 0.0
645
-
715
+
646
716
  return {
647
717
  "trend": trend,
648
718
  "trend_strength": trend_strength,
649
719
  "recent_scores": recent_scores,
650
- "performance_history_length": len(self.performance_history)
720
+ "performance_history_length": len(self.performance_history),
651
721
  }
652
-
722
+
653
723
  def _get_cycle_comparisons(self) -> Dict[str, Any]:
654
724
  """Get comparative analysis across all cycles."""
655
725
  if len(self.traces) < 2:
656
726
  return {"comparison": "insufficient_data"}
657
-
727
+
658
728
  cycle_scores = {}
659
729
  for trace in self.traces:
660
730
  stats = trace.get_statistics()
661
731
  cycle_scores[trace.cycle_id] = stats["efficiency_score"]
662
-
732
+
663
733
  best_cycle = max(cycle_scores.items(), key=lambda x: x[1])
664
734
  worst_cycle = min(cycle_scores.items(), key=lambda x: x[1])
665
-
735
+
666
736
  return {
667
737
  "best_cycle": {"id": best_cycle[0], "score": best_cycle[1]},
668
738
  "worst_cycle": {"id": worst_cycle[0], "score": worst_cycle[1]},
669
739
  "score_range": best_cycle[1] - worst_cycle[1],
670
- "total_cycles": len(cycle_scores)
671
- }
740
+ "total_cycles": len(cycle_scores),
741
+ }