Spaces:
Configuration error
Configuration error
| """ | |
| Export and Reporting Module | |
| ============================ | |
| Generate PDF reports and export data in various formats for analysis results. | |
| """ | |
| import matplotlib.pyplot as plt | |
| import numpy as np | |
| from matplotlib.backends.backend_pdf import PdfPages | |
| from datetime import datetime | |
| import json | |
| import pandas as pd | |
| from typing import Dict, List | |
| def create_pdf_report(results: Dict, filename: str = None) -> str: | |
| """ | |
| Generate comprehensive PDF report from analysis results. | |
| Args: | |
| results: Analysis results dictionary | |
| filename: Output PDF filename (auto-generated if None) | |
| Returns: | |
| Path to generated PDF file | |
| """ | |
| if filename is None: | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = f"quantum_catalyst_report_{timestamp}.pdf" | |
| with PdfPages(filename) as pdf: | |
| # Page 1: Title and Summary | |
| fig = plt.figure(figsize=(8.5, 11)) | |
| fig.suptitle('Quantum Catalyst Analysis Report', fontsize=24, fontweight='bold') | |
| ax = fig.add_subplot(111) | |
| ax.axis('off') | |
| # Report metadata | |
| report_text = f""" | |
| Generated: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} | |
| Platform: Quantum Catalyst Discovery Platform | |
| Analysis Type: {results.get('type', 'Unknown')} | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| EXECUTIVE SUMMARY | |
| This report contains quantum computing and machine learning | |
| analysis results for catalyst discovery and evaluation. | |
| All energies calculated using real Variational Quantum | |
| Eigensolver (VQE) algorithms running on Qiskit. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| ax.text(0.1, 0.8, report_text, transform=ax.transAxes, | |
| fontsize=12, verticalalignment='top', family='monospace') | |
| pdf.savefig(fig, bbox_inches='tight') | |
| plt.close() | |
| # Add result-specific pages | |
| result_type = results.get('type', '') | |
| if result_type == 'AI Discovery' and 'candidates' in results: | |
| _add_discovery_pages(pdf, results) | |
| elif result_type == 'Learning Game': | |
| _add_learning_game_pages(pdf, results) | |
| elif result_type == 'Comparison': | |
| _add_comparison_pages(pdf, results) | |
| # Final page: Methodology | |
| _add_methodology_page(pdf) | |
| return filename | |
| def export_discovery_batch_to_csv(candidates: list) -> str: | |
| """ | |
| Export discovery candidates to CSV string. | |
| Fields: | |
| - SMILES | |
| - QSVM Score | |
| - Classification | |
| """ | |
| rows = [] | |
| for cand in candidates or []: | |
| rows.append({ | |
| "SMILES": cand.get("smiles", ""), | |
| "QSVM Score": cand.get("catalyst_score", ""), | |
| "Classification": cand.get("classification", ""), | |
| }) | |
| df = pd.DataFrame(rows, columns=["SMILES", "QSVM Score", "Classification"]) | |
| return df.to_csv(index=False) | |
| def _add_discovery_pages(pdf: PdfPages, results: Dict): | |
| """Add AI discovery result pages to PDF.""" | |
| candidates = results.get('candidates', []) | |
| # Candidates summary page | |
| fig, ax = plt.subplots(figsize=(8.5, 11)) | |
| ax.text(0.5, 0.95, f'AI Catalyst Discovery Results', | |
| ha='center', fontsize=18, fontweight='bold', | |
| transform=ax.transAxes) | |
| ax.text(0.5, 0.90, f'Reaction: {results.get("reaction", "Unknown")}', | |
| ha='center', fontsize=14, transform=ax.transAxes) | |
| # Table of candidates | |
| table_data = [] | |
| for i, cand in enumerate(candidates[:10]): | |
| table_data.append([ | |
| f"#{i+1}", | |
| cand['smiles'], | |
| cand.get('metal_type', 'N/A'), | |
| f"{cand['catalyst_score']:.2f}", | |
| cand['classification'] | |
| ]) | |
| table = ax.table(cellText=table_data, | |
| colLabels=['Rank', 'Catalyst', 'Metal', 'Score', 'Class'], | |
| cellLoc='center', | |
| loc='center', | |
| bbox=[0.1, 0.3, 0.8, 0.5]) | |
| table.auto_set_font_size(False) | |
| table.set_fontsize(10) | |
| table.scale(1, 2) | |
| ax.axis('off') | |
| pdf.savefig(fig, bbox_inches='tight') | |
| plt.close() | |
| # Score comparison chart | |
| fig, ax = plt.subplots(figsize=(8.5, 6)) | |
| scores = [c['catalyst_score'] for c in candidates[:10]] | |
| labels = [f"#{i+1}\n{c['smiles']}" for i, c in enumerate(candidates[:10])] | |
| colors = plt.cm.viridis(np.linspace(0.3, 0.9, len(scores))) | |
| bars = ax.bar(range(len(scores)), scores, color=colors, edgecolor='black', linewidth=1.5) | |
| for i, bar in enumerate(bars): | |
| height = bar.get_height() | |
| ax.text(bar.get_x() + bar.get_width()/2., height, | |
| f'{scores[i]:.1f}', | |
| ha='center', va='bottom', fontweight='bold') | |
| ax.set_xticks(range(len(labels))) | |
| ax.set_xticklabels(labels, rotation=45, ha='right', fontsize=9) | |
| ax.set_ylabel('Catalyst Score (0-100)', fontsize=12, fontweight='bold') | |
| ax.set_title('Generated Catalyst Candidates - Score Comparison', fontsize=14, fontweight='bold') | |
| ax.grid(True, alpha=0.3, axis='y') | |
| ax.set_ylim(0, 100) | |
| plt.tight_layout() | |
| pdf.savefig(fig, bbox_inches='tight') | |
| plt.close() | |
| def _add_learning_game_pages(pdf: PdfPages, results: Dict): | |
| """Add learning game result pages to PDF.""" | |
| fig, ax = plt.subplots(figsize=(8.5, 11)) | |
| ax.text(0.5, 0.95, 'Learning Game Results', | |
| ha='center', fontsize=18, fontweight='bold', | |
| transform=ax.transAxes) | |
| score = results.get('score', 0) | |
| # Score display | |
| score_color = 'green' if score >= 80 else 'orange' if score >= 60 else 'red' | |
| ax.text(0.5, 0.80, f'Final Score: {score:.1f}/100', | |
| ha='center', fontsize=24, fontweight='bold', | |
| color=score_color, transform=ax.transAxes) | |
| ax.text(0.5, 0.70, f'Reaction: {results.get("reaction", "Unknown")}', | |
| ha='center', fontsize=14, transform=ax.transAxes) | |
| ax.text(0.5, 0.65, f'Your Catalyst: {results.get("user_catalyst", "Unknown")}', | |
| ha='center', fontsize=14, transform=ax.transAxes) | |
| # Performance assessment | |
| if score >= 80: | |
| assessment = "Excellent! You have a strong understanding of catalyst chemistry." | |
| elif score >= 60: | |
| assessment = "Good work! You're on the right track." | |
| else: | |
| assessment = "Keep learning! Review the ideal catalysts for this reaction." | |
| ax.text(0.5, 0.50, assessment, | |
| ha='center', fontsize=12, style='italic', | |
| transform=ax.transAxes, wrap=True) | |
| ax.axis('off') | |
| pdf.savefig(fig, bbox_inches='tight') | |
| plt.close() | |
| def _add_comparison_pages(pdf: PdfPages, results: Dict): | |
| """Add comparison result pages to PDF.""" | |
| fig, ax = plt.subplots(figsize=(8.5, 11)) | |
| ax.text(0.5, 0.95, 'Quantum vs Classical Comparison', | |
| ha='center', fontsize=18, fontweight='bold', | |
| transform=ax.transAxes) | |
| ax.text(0.5, 0.88, f'Analysis Type: {results.get("comparison_type", "Unknown")}', | |
| ha='center', fontsize=14, transform=ax.transAxes) | |
| ax.text(0.5, 0.83, f'Molecule: {results.get("molecule", "Unknown")}', | |
| ha='center', fontsize=14, transform=ax.transAxes) | |
| comparison_text = """ | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| This comparison demonstrates the quantum advantage | |
| in computational chemistry and machine learning. | |
| Quantum methods (VQE, QSVM) provide more accurate | |
| results compared to classical approximations. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| ax.text(0.5, 0.60, comparison_text, | |
| ha='center', va='center', fontsize=11, | |
| transform=ax.transAxes, family='monospace') | |
| ax.axis('off') | |
| pdf.savefig(fig, bbox_inches='tight') | |
| plt.close() | |
| def _add_methodology_page(pdf: PdfPages): | |
| """Add methodology explanation page.""" | |
| fig, ax = plt.subplots(figsize=(8.5, 11)) | |
| ax.text(0.5, 0.95, 'Methodology', | |
| ha='center', fontsize=18, fontweight='bold', | |
| transform=ax.transAxes) | |
| methodology_text = """ | |
| QUANTUM ALGORITHMS | |
| β’ VQE (Variational Quantum Eigensolver) | |
| - Hybrid quantum-classical algorithm | |
| - Finds molecular ground state energies | |
| - More accurate than Hartree-Fock | |
| β’ QSVM (Quantum Support Vector Machine) | |
| - Quantum kernel methods for classification | |
| - Enhanced pattern recognition | |
| - Scores catalyst effectiveness | |
| β’ QGAN (Quantum Generative Adversarial Network) | |
| - Generates novel catalyst candidates | |
| - Quantum-enhanced feature learning | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CLASSICAL BASELINES | |
| β’ Hartree-Fock (HF) | |
| - Mean-field approximation | |
| - Fast but less accurate | |
| β’ DFT (Density Functional Theory) | |
| - Exchange-correlation functionals | |
| - Balance of speed and accuracy | |
| β’ Classical ML | |
| - Random Forest, SVM, Gradient Boosting | |
| - Standard machine learning methods | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| CHEMISTRY MODELS | |
| β’ D-band Model | |
| - Predicts catalyst-adsorbate binding | |
| - NΓΈrskov's theoretical framework | |
| β’ BEP Relation | |
| - BrΓΈnsted-Evans-Polanyi correlation | |
| - Estimates activation energies | |
| βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| Platform: Quantum Catalyst Discovery Platform | |
| Framework: Qiskit, RDKit, Scikit-learn | |
| License: Educational & Research Use | |
| """ | |
| ax.text(0.1, 0.85, methodology_text, | |
| verticalalignment='top', fontsize=9, | |
| transform=ax.transAxes, family='monospace') | |
| ax.axis('off') | |
| pdf.savefig(fig, bbox_inches='tight') | |
| plt.close() | |
| def export_to_csv(results_history: List[Dict], filename: str = None) -> str: | |
| """ | |
| Export results history to CSV format. | |
| Args: | |
| results_history: List of result dictionaries | |
| filename: Output CSV filename | |
| Returns: | |
| Path to generated CSV file | |
| """ | |
| if filename is None: | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = f"quantum_catalyst_data_{timestamp}.csv" | |
| # Flatten data | |
| csv_data = [] | |
| for result in results_history: | |
| row = { | |
| 'Timestamp': result.get('timestamp', ''), | |
| 'Type': result.get('type', ''), | |
| 'Reaction': result.get('reaction', 'N/A'), | |
| } | |
| if result.get('type') == 'AI Discovery' and 'candidates' in result: | |
| row['Num_Candidates'] = len(result['candidates']) | |
| if result['candidates']: | |
| row['Top_Score'] = result['candidates'][0].get('catalyst_score', 0) | |
| row['Top_Catalyst'] = result['candidates'][0].get('smiles', '') | |
| elif result.get('type') == 'Learning Game': | |
| row['User_Catalyst'] = result.get('user_catalyst', '') | |
| row['Score'] = result.get('score', 0) | |
| csv_data.append(row) | |
| df = pd.DataFrame(csv_data) | |
| df.to_csv(filename, index=False) | |
| return filename | |
| def export_to_json(results_history: List[Dict], filename: str = None) -> str: | |
| """ | |
| Export results history to JSON format. | |
| Args: | |
| results_history: List of result dictionaries | |
| filename: Output JSON filename | |
| Returns: | |
| Path to generated JSON file | |
| """ | |
| if filename is None: | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filename = f"quantum_catalyst_data_{timestamp}.json" | |
| with open(filename, 'w') as f: | |
| json.dump(results_history, f, indent=2) | |
| return filename | |
| def generate_summary_stats(results_history: List[Dict]) -> Dict: | |
| """ | |
| Generate summary statistics from results history. | |
| Args: | |
| results_history: List of result dictionaries | |
| Returns: | |
| Dictionary with summary statistics | |
| """ | |
| stats = { | |
| 'total_analyses': len(results_history), | |
| 'by_type': {}, | |
| 'reactions_analyzed': set(), | |
| 'catalysts_tested': set(), | |
| 'avg_scores': {} | |
| } | |
| for result in results_history: | |
| result_type = result.get('type', 'Unknown') | |
| stats['by_type'][result_type] = stats['by_type'].get(result_type, 0) + 1 | |
| if 'reaction' in result: | |
| stats['reactions_analyzed'].add(result['reaction']) | |
| if result.get('type') == 'AI Discovery' and 'candidates' in result: | |
| for cand in result['candidates']: | |
| stats['catalysts_tested'].add(cand.get('smiles', '')) | |
| elif result.get('type') == 'Learning Game': | |
| if 'user_catalyst' in result: | |
| stats['catalysts_tested'].add(result['user_catalyst']) | |
| if 'score' in result: | |
| if result_type not in stats['avg_scores']: | |
| stats['avg_scores'][result_type] = [] | |
| stats['avg_scores'][result_type].append(result['score']) | |
| # Convert sets to lists and calculate averages | |
| stats['reactions_analyzed'] = list(stats['reactions_analyzed']) | |
| stats['catalysts_tested'] = list(stats['catalysts_tested']) | |
| for result_type, scores in stats['avg_scores'].items(): | |
| stats['avg_scores'][result_type] = np.mean(scores) if scores else 0 | |
| return stats | |