Files
HDH-example/utils.py
2025-10-12 00:55:02 +02:00

524 líneas
18 KiB
Python
Original Blame Histórico

Este archivo contiene caracteres Unicode ambiguos
Este archivo contiene caracteres Unicode que pueden confundirse con otros caracteres. Si crees que esto es intencional, puedes ignorar esta advertencia. Usa el botón de Escape para revelarlos.
#!/usr/bin/env python3
"""
HDH Deployment Utilities
========================
Utility functions and helper classes for the HDH deployment example.
Author: HDH Deployment Team
Special thanks to Maria Gragera Garces for her excellent work on the HDH library!
"""
import os
import sys
import json
import yaml
import time
import logging
from pathlib import Path
from typing import Dict, Any, List, Optional, Union
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
class ConfigManager:
"""Configuration management for HDH deployment."""
def __init__(self, config_file: str = "config.yaml"):
"""Initialize configuration manager."""
self.config_file = Path(config_file)
self.config = self.load_config()
def load_config(self) -> Dict[str, Any]:
"""Load configuration from YAML file."""
if self.config_file.exists():
with open(self.config_file, 'r') as f:
return yaml.safe_load(f)
else:
return self.get_default_config()
def get_default_config(self) -> Dict[str, Any]:
"""Get default configuration."""
return {
"logging": {
"level": "INFO",
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
"file": "hdh_deployment.log"
},
"output": {
"directory": "hdh_results",
"save_plots": True,
"plot_format": "png",
"plot_dpi": 300
},
"circuits": {
"max_qubits": 10,
"default_partitions": 3,
"enable_visualization": True,
"save_intermediate": False
},
"performance": {
"timeout_seconds": 300,
"max_memory_gb": 8,
"parallel_processing": False
}
}
def save_config(self):
"""Save current configuration to file."""
with open(self.config_file, 'w') as f:
yaml.dump(self.config, f, default_flow_style=False, indent=2)
def get(self, key_path: str, default=None):
"""Get configuration value using dot notation."""
keys = key_path.split('.')
value = self.config
for key in keys:
if isinstance(value, dict) and key in value:
value = value[key]
else:
return default
return value
def set(self, key_path: str, value: Any):
"""Set configuration value using dot notation."""
keys = key_path.split('.')
config = self.config
for key in keys[:-1]:
if key not in config:
config[key] = {}
config = config[key]
config[keys[-1]] = value
class ResultsManager:
"""Manage and analyze HDH deployment results."""
def __init__(self, results_dir: str = "hdh_results"):
"""Initialize results manager."""
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def save_results(self, results: Dict[str, Any], filename: str = None):
"""Save results to JSON file."""
if filename is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"hdh_results_{timestamp}.json"
filepath = self.results_dir / filename
with open(filepath, 'w') as f:
json.dump(results, f, indent=2, default=str)
return filepath
def load_results(self, filename: str) -> Dict[str, Any]:
"""Load results from JSON file."""
filepath = self.results_dir / filename
with open(filepath, 'r') as f:
return json.load(f)
def list_result_files(self) -> List[Path]:
"""List all result files in the directory."""
return list(self.results_dir.glob("*.json"))
def get_latest_results(self) -> Optional[Dict[str, Any]]:
"""Get the most recent results file."""
result_files = self.list_result_files()
if not result_files:
return None
# Sort by modification time
latest_file = max(result_files, key=lambda f: f.stat().st_mtime)
return self.load_results(latest_file.name)
def merge_results(self, result_files: List[str]) -> Dict[str, Any]:
"""Merge multiple result files."""
merged = {
"merged_at": datetime.now().isoformat(),
"source_files": result_files,
"results": []
}
for filename in result_files:
try:
results = self.load_results(filename)
merged["results"].append({
"filename": filename,
"data": results
})
except Exception as e:
merged["results"].append({
"filename": filename,
"error": str(e)
})
return merged
def generate_summary_report(self) -> Dict[str, Any]:
"""Generate summary report from all results."""
result_files = self.list_result_files()
if not result_files:
return {"error": "No result files found"}
summary = {
"generated_at": datetime.now().isoformat(),
"total_files": len(result_files),
"file_analysis": []
}
for result_file in result_files:
try:
results = self.load_results(result_file.name)
analysis = {
"filename": result_file.name,
"file_size": result_file.stat().st_size,
"modified_at": datetime.fromtimestamp(result_file.stat().st_mtime).isoformat()
}
# Analyze content if it has expected structure
if isinstance(results, dict):
if "detailed_results" in results:
detailed = results["detailed_results"]
if isinstance(detailed, list):
analysis["circuits_count"] = len(detailed)
analysis["successful_circuits"] = sum(1 for r in detailed if r.get("success", False))
if "summary" in results:
summary_data = results["summary"]
if isinstance(summary_data, dict):
analysis["summary_data"] = summary_data
summary["file_analysis"].append(analysis)
except Exception as e:
summary["file_analysis"].append({
"filename": result_file.name,
"error": str(e)
})
return summary
class PlotManager:
"""Enhanced plotting utilities for HDH visualization."""
def __init__(self, output_dir: str = "plots", dpi: int = 300):
"""Initialize plot manager."""
self.output_dir = Path(output_dir)
self.output_dir.mkdir(exist_ok=True)
self.dpi = dpi
# Set matplotlib style
plt.style.use('default')
self.setup_matplotlib()
def setup_matplotlib(self):
"""Configure matplotlib settings."""
plt.rcParams['figure.figsize'] = (12, 8)
plt.rcParams['font.size'] = 12
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['axes.titlesize'] = 16
plt.rcParams['legend.fontsize'] = 12
plt.rcParams['xtick.labelsize'] = 10
plt.rcParams['ytick.labelsize'] = 10
def create_comparison_plot(self, data: Dict[str, List[float]],
title: str, xlabel: str, ylabel: str,
filename: str = None) -> Path:
"""Create a comparison plot with multiple data series."""
fig, ax = plt.subplots(figsize=(12, 8))
colors = plt.cm.Set1(np.linspace(0, 1, len(data)))
for (label, values), color in zip(data.items(), colors):
x_values = range(len(values))
ax.plot(x_values, values, 'o-', label=label, color=color,
linewidth=2, markersize=6, alpha=0.8)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.set_title(title)
ax.legend()
ax.grid(True, alpha=0.3)
if filename is None:
filename = f"comparison_{title.lower().replace(' ', '_')}.png"
filepath = self.output_dir / filename
plt.savefig(filepath, dpi=self.dpi, bbox_inches='tight')
plt.close()
return filepath
def create_histogram(self, data: List[float], title: str,
xlabel: str, ylabel: str = "Frequency",
bins: int = 20, filename: str = None) -> Path:
"""Create a histogram plot."""
fig, ax = plt.subplots(figsize=(10, 6))
ax.hist(data, bins=bins, alpha=0.7, color='skyblue',
edgecolor='black', linewidth=1)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.set_title(title)
ax.grid(True, alpha=0.3, axis='y')
# Add statistics
mean_val = np.mean(data)
std_val = np.std(data)
ax.axvline(mean_val, color='red', linestyle='--',
label=f'Mean: {mean_val:.3f}')
ax.axvline(mean_val + std_val, color='orange', linestyle='--',
alpha=0.7, label=f'±1σ: {std_val:.3f}')
ax.axvline(mean_val - std_val, color='orange', linestyle='--', alpha=0.7)
ax.legend()
if filename is None:
filename = f"histogram_{title.lower().replace(' ', '_')}.png"
filepath = self.output_dir / filename
plt.savefig(filepath, dpi=self.dpi, bbox_inches='tight')
plt.close()
return filepath
def create_scatter_plot(self, x_data: List[float], y_data: List[float],
title: str, xlabel: str, ylabel: str,
labels: List[str] = None, filename: str = None) -> Path:
"""Create a scatter plot with optional labels."""
fig, ax = plt.subplots(figsize=(10, 8))
scatter = ax.scatter(x_data, y_data, alpha=0.6, s=60,
c=range(len(x_data)), cmap='viridis')
# Add labels if provided
if labels:
for i, label in enumerate(labels):
ax.annotate(label, (x_data[i], y_data[i]),
xytext=(5, 5), textcoords='offset points',
fontsize=8, alpha=0.8)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.set_title(title)
ax.grid(True, alpha=0.3)
# Add colorbar
plt.colorbar(scatter, ax=ax, label='Data Point Index')
if filename is None:
filename = f"scatter_{title.lower().replace(' ', '_')}.png"
filepath = self.output_dir / filename
plt.savefig(filepath, dpi=self.dpi, bbox_inches='tight')
plt.close()
return filepath
class PerformanceProfiler:
"""Performance profiling utilities for HDH operations."""
def __init__(self):
"""Initialize performance profiler."""
self.profiles = {}
self.active_profiles = {}
def start_profile(self, name: str):
"""Start profiling a named operation."""
self.active_profiles[name] = {
'start_time': time.perf_counter(),
'start_memory': self.get_memory_usage()
}
def end_profile(self, name: str) -> Dict[str, float]:
"""End profiling and return metrics."""
if name not in self.active_profiles:
raise ValueError(f"No active profile named '{name}'")
profile_data = self.active_profiles.pop(name)
metrics = {
'duration': time.perf_counter() - profile_data['start_time'],
'memory_delta': self.get_memory_usage() - profile_data['start_memory'],
'timestamp': datetime.now().isoformat()
}
if name not in self.profiles:
self.profiles[name] = []
self.profiles[name].append(metrics)
return metrics
def get_memory_usage(self) -> float:
"""Get current memory usage in MB."""
try:
import psutil
process = psutil.Process()
return process.memory_info().rss / 1024 / 1024
except ImportError:
return 0.0
def get_profile_summary(self, name: str) -> Dict[str, Any]:
"""Get summary statistics for a named profile."""
if name not in self.profiles:
return {"error": f"No profiles found for '{name}'"}
profiles = self.profiles[name]
durations = [p['duration'] for p in profiles]
memory_deltas = [p['memory_delta'] for p in profiles]
return {
'name': name,
'count': len(profiles),
'duration_stats': {
'mean': np.mean(durations),
'median': np.median(durations),
'min': np.min(durations),
'max': np.max(durations),
'std': np.std(durations)
},
'memory_stats': {
'mean': np.mean(memory_deltas),
'median': np.median(memory_deltas),
'min': np.min(memory_deltas),
'max': np.max(memory_deltas),
'std': np.std(memory_deltas)
}
}
def export_profiles(self, filepath: str):
"""Export all profiles to JSON file."""
export_data = {
'exported_at': datetime.now().isoformat(),
'profiles': self.profiles,
'summaries': {name: self.get_profile_summary(name)
for name in self.profiles.keys()}
}
with open(filepath, 'w') as f:
json.dump(export_data, f, indent=2, default=str)
def setup_logging(log_level: str = "INFO", log_file: str = None) -> logging.Logger:
"""Setup standardized logging for HDH deployment."""
logger = logging.getLogger("hdh_deployment")
logger.setLevel(getattr(logging, log_level.upper()))
# Clear any existing handlers
logger.handlers.clear()
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# File handler if specified
if log_file:
file_handler = logging.FileHandler(log_file)
file_formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
)
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
return logger
def validate_hdh_environment() -> Dict[str, Any]:
"""Validate that the HDH environment is properly set up."""
validation_results = {
'timestamp': datetime.now().isoformat(),
'valid': True,
'issues': [],
'warnings': []
}
# Check HDH import
try:
import hdh
validation_results['hdh_version'] = getattr(hdh, '__version__', 'unknown')
except ImportError as e:
validation_results['valid'] = False
validation_results['issues'].append(f"HDH import failed: {str(e)}")
# Check required dependencies
required_packages = ['qiskit', 'networkx', 'matplotlib', 'numpy']
for package in required_packages:
try:
__import__(package)
except ImportError:
validation_results['issues'].append(f"Missing required package: {package}")
validation_results['valid'] = False
# Check optional dependencies
optional_packages = ['metis', 'psutil', 'rich', 'click']
for package in optional_packages:
try:
__import__(package)
except ImportError:
validation_results['warnings'].append(f"Optional package not available: {package}")
# Check system resources
try:
import psutil
memory_gb = psutil.virtual_memory().total / (1024**3)
if memory_gb < 4:
validation_results['warnings'].append(f"Low system memory: {memory_gb:.1f}GB")
validation_results['system_memory_gb'] = memory_gb
except ImportError:
validation_results['warnings'].append("Cannot check system memory (psutil not available)")
return validation_results
if __name__ == "__main__":
"""Utility testing and validation."""
print("HDH Deployment Utilities")
print("=" * 50)
print("Special thanks to Maria Gragera Garces for the HDH library!")
print()
# Validate environment
validation = validate_hdh_environment()
print(f"Environment valid: {validation['valid']}")
if validation['issues']:
print("Issues found:")
for issue in validation['issues']:
print(f" - {issue}")
if validation['warnings']:
print("Warnings:")
for warning in validation['warnings']:
print(f" - {warning}")
# Test configuration manager
print("\nTesting ConfigManager...")
config_mgr = ConfigManager()
print(f"Default output directory: {config_mgr.get('output.directory')}")
# Test results manager
print("\nTesting ResultsManager...")
results_mgr = ResultsManager()
test_results = {"test": "data", "timestamp": datetime.now().isoformat()}
saved_file = results_mgr.save_results(test_results, "test_results.json")
print(f"Test results saved to: {saved_file}")
print("\nUtilities test completed!")