QWARD Visualization Guide

Overview

QWARD provides a comprehensive visualization system for quantum circuit metrics with a modern, type-safe API. The visualization module follows a clean, object-oriented architecture that makes it easy to create beautiful, informative plots for your quantum computing analysis.

New API Features (v0.9.0)

🎯 Type-Safe Constants

  • Metrics constants: Metrics.QISKIT, Metrics.COMPLEXITY, Metrics.CIRCUIT_PERFORMANCE

  • Plots constants: Plots.QISKIT.CIRCUIT_STRUCTURE, Plots.COMPLEXITY.COMPLEXITY_RADAR, etc.

  • IDE Autocompletion: Full IntelliSense support for all plot names

  • Error Prevention: Compile-time detection of typos in metric and plot names

🔍 Rich Plot Metadata

  • Plot Descriptions: Detailed information about what each plot shows

  • Plot Types: Categorized as bar charts, radar charts, line plots, etc.

  • Dependencies: Information about required data columns

  • Categories: Organized by analysis type (structure, performance, complexity)

Granular Plot Control

  • Single Plot Generation: generate_plot(metric, plot_name)

  • Selected Plots: generate_plots({metric: [plot1, plot2]})

  • All Plots: generate_plots({metric: None})

  • Memory Efficient: Default save=False, show=False for batch processing

🛡️ Enhanced Error Handling

  • Validation: Automatic validation of metric and plot name combinations

  • Clear Messages: Descriptive error messages for missing data or invalid requests

  • Graceful Fallbacks: Robust handling of edge cases

Architecture

The visualization system is built around a powerful and extensible architecture:

  • PlotConfig: A dataclass holding all plot appearance and saving configurations

  • VisualizationStrategy: An abstract base class that handles common functionality like output directory management, styling, data validation, and plot creation utilities

  • Individual Visualizers: Three specialized visualizers for different metric types:

    • QiskitVisualizer: Circuit structure and instruction analysis

    • ComplexityVisualizer: Complexity analysis with radar charts and efficiency metrics

    • CircuitPerformanceVisualizer: Performance metrics with success rates, shot distribution, and aggregate summaries

  • Visualizer: A unified entry point that automatically detects available metrics and provides comprehensive visualization capabilities

        classDiagram
    class VisualizationStrategy {
        <<abstract>>
        +output_dir: str
        +config: PlotConfig
        +PLOT_REGISTRY: Dict[str, PlotMetadata]
        +save_plot(fig, filename)
        +show_plot(fig)
        +get_available_plots()* List[str]
        +get_plot_metadata(plot_name)* PlotMetadata
        +generate_plot(plot_name, save, show)* Figure
        +generate_plots(plot_names, save, show)* List[Figure]
        +generate_all_plots(save, show) List[Figure]
        +create_dashboard(save, show)*
        +_validate_required_columns()
        +_extract_metrics_from_columns()
        +_create_bar_plot_with_labels()
        +_setup_plot_axes()
        +_finalize_plot()
    }
    
    class PlotConfig {
        +figsize: Tuple[int, int]
        +dpi: int
        +style: str
        +color_palette: List[str]
        +save_format: str
        +grid: bool
        +alpha: float
    }
    
    class PlotMetadata {
        +name: str
        +method_name: str
        +description: str
        +plot_type: PlotType
        +filename: str
        +dependencies: List[str]
        +category: str
    }
    
    class QiskitVisualizer {
        +PLOT_REGISTRY: Dict[str, PlotMetadata]
        +generate_plot(plot_name, save, show)
        +get_available_plots() List[str]
        +get_plot_metadata(plot_name) PlotMetadata
        +plot_circuit_structure()
        +plot_gate_distribution()
        +plot_instruction_metrics()
        +plot_circuit_summary()
        +create_dashboard()
    }
    
    class ComplexityVisualizer {
        +PLOT_REGISTRY: Dict[str, PlotMetadata]
        +generate_plot(plot_name, save, show)
        +get_available_plots() List[str]
        +get_plot_metadata(plot_name) PlotMetadata
        +plot_gate_based_metrics()
        +plot_complexity_radar()
        +plot_efficiency_metrics()
        +create_dashboard()
    }
    
    class CircuitPerformanceVisualizer {
        +PLOT_REGISTRY: Dict[str, PlotMetadata]
        +generate_plot(plot_name, save, show)
        +get_available_plots() List[str]
        +get_plot_metadata(plot_name) PlotMetadata
        +plot_success_error_comparison()
        +plot_shot_distribution()
        +plot_aggregate_summary()
        +create_dashboard()
    }
    
    class Visualizer {
        +get_available_plots() Dict[str, List[str]]
        +get_plot_metadata(metric, plot_name) PlotMetadata
        +generate_plot(metric, plot_name, save, show) Figure
        +generate_plots(selections, save, show) Dict[str, List[Figure]]
        +create_dashboard(save, show) Dict[str, Figure]
        +register_strategy()
        +get_available_metrics()
        +get_metric_summary()
        +print_available_metrics()
    }

    VisualizationStrategy <|-- QiskitVisualizer
    VisualizationStrategy <|-- ComplexityVisualizer
    VisualizationStrategy <|-- CircuitPerformanceVisualizer
    VisualizationStrategy --> PlotConfig : uses
    VisualizationStrategy --> PlotMetadata : defines
    Visualizer --> VisualizationStrategy : manages
    
    note for VisualizationStrategy "Abstract base class with plot registry and metadata system"
    note for QiskitVisualizer "4 plots: circuit_structure, gate_distribution, instruction_metrics, circuit_summary"
    note for ComplexityVisualizer "3 plots: gate_based_metrics, complexity_radar, efficiency_metrics"
    note for CircuitPerformanceVisualizer "3 plots: success_error_comparison, shot_distribution, aggregate_summary"
    note for Visualizer "Unified entry point with type-safe constants and granular control"
    

Quick Start

Memory-Efficient Batch Processing

# NEW: Default save=False, show=False for memory efficiency
for circuit_variant in circuit_variants:
    scanner = Scanner(circuit=circuit_variant, strategies=[QiskitMetrics, ComplexityMetrics])
    visualizer = Visualizer(scanner=scanner, output_dir=f"analysis_{circuit_variant.name}")
    
    # Generate all plots without displaying (memory efficient)
    all_plots = visualizer.generate_plots({
        Metrics.QISKIT: None,
        Metrics.COMPLEXITY: None
    })  # Default: save=False, show=False
    
    # Only save specific plots of interest
    important_plots = visualizer.generate_plots({
        Metrics.QISKIT: [Plots.QISKIT.CIRCUIT_STRUCTURE],
        Metrics.COMPLEXITY: [Plots.COMPLEXITY.COMPLEXITY_RADAR]
    }, save=True)

Examples and Usage Patterns

📁 Available Examples

QWARD provides comprehensive examples in the qward/examples/ directory:

  • test_new_visualization_api.py - Comprehensive test suite demonstrating all new API features

  • new_api_usage_example.py - Practical usage patterns with the new type-safe API

  • example_visualizer.py - Complete workflow examples showing all features of the unified Visualizer

  • visualization_demo.py - Focused demo of CircuitPerformanceVisualizer capabilities

  • direct_strategy_example.py - Shows how to use visualization strategies directly for maximum control

  • aer.py - Integration examples with Qiskit Aer simulator

  • visualization_quickstart.py - Quick start example with minimal setup

🎨 Two Approaches to Visualization

2. Direct Strategy Usage (Advanced)

Use individual strategies directly when you need fine-grained control:

from qward.visualization import QiskitVisualizer
from qward.visualization.constants import Plots

# Use specific strategies directly with new API
qiskit_strategy = QiskitVisualizer(metrics_dict=qiskit_data, output_dir="custom_dir")

# NEW API: Generate specific plots
qiskit_strategy.generate_plot(Plots.QISKIT.CIRCUIT_STRUCTURE, save=True, show=False)

# NEW API: Get plot metadata
metadata = qiskit_strategy.get_plot_metadata(Plots.QISKIT.CIRCUIT_STRUCTURE)
print(f"Plot type: {metadata.plot_type.value}")
print(f"Dependencies: {metadata.dependencies}")

# Create dashboard (unchanged)
qiskit_strategy.create_dashboard(save=True, show=False)

Benefits:

  • Fine-grained control over individual plots

  • Custom configurations per strategy

  • Integration with external data sources

  • Flexible output formats and locations

🔧 Available Strategies

Strategy

Metrics Type

Available Plots

Key Features

QiskitVisualizer

QiskitMetrics

4 plots

Circuit structure, gate distribution, instruction metrics, circuit summary

ComplexityVisualizer

ComplexityMetrics

3 plots

Gate-based metrics, complexity radar, efficiency metrics

CircuitPerformanceVisualizer

CircuitPerformanceMetrics

3 plots

Success rates, shot distribution, aggregate summary

🎛️ Plot Discovery and Metadata

Exploring Available Plots

from qward.visualization.constants import Metrics, Plots

# Get all available plots
visualizer = Visualizer(scanner=scanner)
available_plots = visualizer.get_available_plots()

print("Available plots by metric:")
for metric_name, plot_names in available_plots.items():
    print(f"\n{metric_name} ({len(plot_names)} plots):")
    for plot_name in plot_names:
        metadata = visualizer.get_plot_metadata(metric_name, plot_name)
        print(f"  - {plot_name}")
        print(f"    Description: {metadata.description}")
        print(f"    Type: {metadata.plot_type.value}")
        print(f"    Category: {metadata.category}")

Using Constants for Type Safety

# Type-safe constants prevent errors
from qward.visualization.constants import Metrics, Plots

# ✅ This works - IDE provides autocompletion
visualizer.generate_plot(Metrics.QISKIT, Plots.QISKIT.CIRCUIT_STRUCTURE)

# ❌ This would cause an error at runtime
# visualizer.generate_plot(Metrics.QISKIT, Plots.COMPLEXITY.COMPLEXITY_RADAR)  # Wrong combination!

# ✅ IDE autocompletion shows all available options
selected_plots = visualizer.generate_plots({
    Metrics.QISKIT: [
        Plots.QISKIT.CIRCUIT_STRUCTURE,    # IDE suggests these
        Plots.QISKIT.GATE_DISTRIBUTION,   # as you type
        Plots.QISKIT.INSTRUCTION_METRICS,
        Plots.QISKIT.CIRCUIT_SUMMARY
    ]
})

🎛️ Customization

Custom Plot Configuration

from qward.visualization import PlotConfig

config = PlotConfig(
    figsize=(12, 8),
    style="quantum",
    color_palette=["#FF6B6B", "#4ECDC4", "#45B7D1"],
    save_format="pdf"
)

visualizer = Visualizer(scanner=scanner, config=config)

# Generate plots with custom styling
visualizer.generate_plots({
    Metrics.QISKIT: [Plots.QISKIT.CIRCUIT_STRUCTURE]
}, save=True, show=False)

Custom Strategies with Plot Registry

from qward.visualization import VisualizationStrategy, PlotMetadata, PlotType

class MyCustomStrategy(VisualizationStrategy):
    # NEW: Define plot registry with metadata
    PLOT_REGISTRY = {
        "custom_plot": PlotMetadata(
            name="custom_plot",
            method_name="plot_custom_analysis",
            description="Custom analysis visualization",
            plot_type=PlotType.BAR_CHART,
            filename="custom_analysis",
            dependencies=["custom.metric"],
            category="custom"
        )
    }
    
    @classmethod
    def get_available_plots(cls):
        return list(cls.PLOT_REGISTRY.keys())
    
    @classmethod
    def get_plot_metadata(cls, plot_name):
        if plot_name not in cls.PLOT_REGISTRY:
            raise ValueError(f"Plot '{plot_name}' not found")
        return cls.PLOT_REGISTRY[plot_name]
    
    def generate_plot(self, plot_name, save=False, show=False):
        if plot_name not in self.PLOT_REGISTRY:
            raise ValueError(f"Plot '{plot_name}' not available")
        
        metadata = self.PLOT_REGISTRY[plot_name]
        method = getattr(self, metadata.method_name)
        return method(save=save, show=show)
    
    def plot_custom_analysis(self, save=False, show=False):
        # Your custom visualization logic
        fig, ax = plt.subplots(figsize=self.config.figsize)
        # ... plotting code ...
        
        if save:
            self.save_plot(fig, "custom_analysis")
        if show:
            self.show_plot(fig)
        return fig
    
    def create_dashboard(self, save=False, show=False):
        # Your custom dashboard logic
        return self.plot_custom_analysis(save=save, show=show)

# Register and use
visualizer.register_strategy("MyMetrics", MyCustomStrategy)

🚀 Running the Examples

# Run comprehensive new API test suite
python qward/examples/test_new_visualization_api.py

# Run practical usage examples with new API
python qward/examples/new_api_usage_example.py

# Run main visualizer examples (updated with new API)
python qward/examples/example_visualizer.py

# Run CircuitPerformanceMetrics demo (updated with new API)
python qward/examples/visualization_demo.py

# Run direct strategy examples (updated with new API)
python qward/examples/direct_strategy_example.py

# Run Aer integration examples (updated with new API)
python qward/examples/aer.py

# Quick start example (updated with new API)
python qward/examples/visualization_quickstart.py

📊 Output

All examples save plots to qward/examples/img/ by default. You’ll find:

  • Dashboards: Comprehensive multi-plot views

  • Individual plots: Specific visualizations for detailed analysis

  • Multiple formats: PNG (default), PDF, SVG support

  • Organized structure: Plots organized by metric type and analysis

💡 Best Practices

  1. Use type-safe constants - Import Metrics and Plots from qward.visualization.constants

  2. Start with plot discovery - Use get_available_plots() and get_plot_metadata() to explore

  3. Leverage granular control - Generate only the plots you need with generate_plots()

  4. Use memory-efficient defaults - Default save=False, show=False prevents unwanted files

  5. Customize PlotConfig for consistent styling across all visualizations

  6. Register custom strategies to extend the system with your own visualizations

  7. Check the output directory - all plots are saved with descriptive names

  8. Use meaningful output directories - organize plots by analysis type or date

🔗 Key Benefits

  • Type Safety: Constants prevent typos and provide IDE autocompletion

  • Rich Metadata: Detailed information about each plot’s purpose and requirements

  • Granular Control: Generate exactly the plots you need

  • Memory Efficient: Default parameters optimized for batch processing

  • Strategy Pattern: Consistent with Scanner architecture

  • Auto-Detection: Automatically finds and visualizes available metrics

  • Extensible: Easy to add new visualization strategies

  • Flexible: Use unified interface or direct strategies as needed

  • Customizable: Full control over plot appearance and output formats

Available Visualizers

QiskitVisualizer

Visualizes circuit structure and instruction analysis from QiskitMetrics.

Available Plots (New API)

from qward.visualization.constants import Plots

# All available QiskitVisualizer plots:
Plots.QISKIT.CIRCUIT_STRUCTURE    # Basic circuit metrics
Plots.QISKIT.GATE_DISTRIBUTION    # Gate type analysis  
Plots.QISKIT.INSTRUCTION_METRICS  # Instruction-related metrics
Plots.QISKIT.CIRCUIT_SUMMARY      # Derived metrics summary

Individual Plots (New API)

  1. Circuit Structure

    # NEW API
    qiskit_viz.generate_plot(Plots.QISKIT.CIRCUIT_STRUCTURE, save=True, show=False)
    
    # Get plot metadata
    metadata = qiskit_viz.get_plot_metadata(Plots.QISKIT.CIRCUIT_STRUCTURE)
    print(f"Description: {metadata.description}")
    

    Shows basic circuit metrics: depth, width, size, qubits, and classical bits.

  2. Gate Distribution

    # NEW API
    qiskit_viz.generate_plot(Plots.QISKIT.GATE_DISTRIBUTION, save=True, show=False)
    

    Displays gate type analysis and instruction distribution as a pie chart.

  3. Instruction Metrics

    # NEW API
    qiskit_viz.generate_plot(Plots.QISKIT.INSTRUCTION_METRICS, save=True, show=False)
    

    Shows instruction-related metrics like connected components and nonlocal gates.

  4. Circuit Summary

    # NEW API
    qiskit_viz.generate_plot(Plots.QISKIT.CIRCUIT_SUMMARY, save=True, show=False)
    

    Displays derived metrics like gate density and parallelism.

Dashboard and All Plots (New API)

# Create comprehensive dashboard (unchanged)
dashboard = qiskit_viz.create_dashboard(save=True, show=False)

# NEW API: Generate all individual plots
all_figures = qiskit_viz.generate_all_plots(save=True, show=False)

# NEW API: Generate selected plots
selected_figures = qiskit_viz.generate_plots([
    Plots.QISKIT.CIRCUIT_STRUCTURE,
    Plots.QISKIT.GATE_DISTRIBUTION
], save=True, show=False)

# NEW API: Get available plots and metadata
available_plots = qiskit_viz.get_available_plots()
for plot_name in available_plots:
    metadata = qiskit_viz.get_plot_metadata(plot_name)
    print(f"{plot_name}: {metadata.description}")

ComplexityVisualizer

Visualizes complexity analysis from ComplexityMetrics with advanced charts and analysis.

Available Plots (New API)

from qward.visualization.constants import Plots

# All available ComplexityVisualizer plots:
Plots.COMPLEXITY.GATE_BASED_METRICS  # Gate counts and depth analysis
Plots.COMPLEXITY.COMPLEXITY_RADAR    # Normalized complexity indicators
Plots.COMPLEXITY.EFFICIENCY_METRICS  # Parallelism and efficiency analysis

Individual Plots (New API)

  1. Gate-Based Metrics

    # NEW API
    complexity_viz.generate_plot(Plots.COMPLEXITY.GATE_BASED_METRICS, save=True, show=False)
    

    Shows gate counts, circuit depth, T-gates, and CNOT gates.

  2. Complexity Radar Chart

    # NEW API
    complexity_viz.generate_plot(Plots.COMPLEXITY.COMPLEXITY_RADAR, save=True, show=False)
    

    Creates a radar chart with normalized complexity indicators for quick visual assessment.

  3. Efficiency Metrics

    # NEW API
    complexity_viz.generate_plot(Plots.COMPLEXITY.EFFICIENCY_METRICS, save=True, show=False)
    

    Shows parallelism efficiency and circuit efficiency analysis.

Dashboard and All Plots (New API)

# Create comprehensive dashboard (unchanged)
dashboard = complexity_viz.create_dashboard(save=True, show=False)

# NEW API: Generate all individual plots
all_figures = complexity_viz.generate_all_plots(save=True, show=False)

# NEW API: Generate selected plots
selected_figures = complexity_viz.generate_plots([
    Plots.COMPLEXITY.COMPLEXITY_RADAR,
    Plots.COMPLEXITY.EFFICIENCY_METRICS
], save=True, show=False)

CircuitPerformanceVisualizer

Visualizes performance metrics from CircuitPerformanceMetrics with success rates, shot distribution, and execution analysis.

Available Plots (New API)

from qward.visualization.constants import Plots

# All available CircuitPerformanceVisualizer plots:
Plots.CIRCUIT_PERFORMANCE.SUCCESS_ERROR_COMPARISON  # Success vs error rates
Plots.CIRCUIT_PERFORMANCE.SHOT_DISTRIBUTION         # Successful vs failed shots
Plots.CIRCUIT_PERFORMANCE.AGGREGATE_SUMMARY         # Statistical summary

Individual Plots (New API)

  1. Success vs Error Rate Comparison

    # NEW API
    perf_viz.generate_plot(Plots.CIRCUIT_PERFORMANCE.SUCCESS_ERROR_COMPARISON, save=True, show=False)
    

    Shows success and error rates across different jobs as a grouped bar chart.

  2. Shot Distribution

    # NEW API
    perf_viz.generate_plot(Plots.CIRCUIT_PERFORMANCE.SHOT_DISTRIBUTION, save=True, show=False)
    

    Shows the distribution of successful vs failed shots as stacked bars with detailed labels.

  3. Aggregate Summary

    # NEW API
    perf_viz.generate_plot(Plots.CIRCUIT_PERFORMANCE.AGGREGATE_SUMMARY, save=True, show=False)
    

    Provides a comprehensive summary of aggregate statistics across multiple jobs.

Dashboard and All Plots (New API)

# Create comprehensive dashboard (unchanged)
dashboard = perf_viz.create_dashboard(save=True, show=False)

# NEW API: Generate all individual plots
all_figures = perf_viz.generate_all_plots(save=True, show=False)

# NEW API: Generate selected plots
selected_figures = perf_viz.generate_plots([
    Plots.CIRCUIT_PERFORMANCE.SUCCESS_ERROR_COMPARISON
], save=True, show=False)

Plot Configuration

PlotConfig Options

from qward.visualization import PlotConfig

config = PlotConfig(
    figsize=(12, 8),           # Figure size in inches
    dpi=300,                   # Resolution for saved plots
    style="quantum",           # Plot style theme
    color_palette=["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728"],  # Custom colors
    save_format="png",         # Output format: png, svg, pdf, eps
    grid=True,                 # Show grid lines
    alpha=0.8                  # Transparency level
)

Available Styles

  • "default": Standard matplotlib style

  • "quantum": Custom quantum-themed style with clean backgrounds

  • "minimal": Minimalist style with white grid

Color Palettes

The default color palette is ColorBrewer-inspired for better accessibility:

default_palette = [
    "#1f77b4",  # Blue
    "#ff7f0e",  # Orange
    "#2ca02c",  # Green
    "#d62728",  # Red
    "#9467bd",  # Purple
    "#8c564b",  # Brown
    "#e377c2",  # Pink
    "#7f7f7f",  # Gray
    "#bcbd22",  # Olive
    "#17becf",  # Cyan
]

Advanced Examples

Complete Analysis with Custom Configuration and New API

from qward.visualization import Visualizer, PlotConfig
from qward.visualization.constants import Metrics, Plots

# Create custom configuration
config = PlotConfig(
    figsize=(14, 10),
    style="quantum",
    dpi=150,
    save_format="svg",
    color_palette=["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728"],
    alpha=0.8
)

# Create comprehensive analysis
scanner = Scanner(circuit=circuit)
scanner.add_strategy(QiskitMetrics(circuit))
scanner.add_strategy(ComplexityMetrics(circuit))
scanner.add_strategy(CircuitPerformanceMetrics(circuit=circuit, job=job))

# Use unified visualizer with custom config
visualizer = Visualizer(
    scanner=scanner, 
    config=config, 
    output_dir="comprehensive_analysis"
)

# NEW API: Create all visualizations with type-safe constants
dashboards = visualizer.create_dashboard(save=True, show=False)

# NEW API: Generate specific plots for analysis
analysis_plots = visualizer.generate_plots({
    Metrics.QISKIT: [
        Plots.QISKIT.CIRCUIT_STRUCTURE,
        Plots.QISKIT.GATE_DISTRIBUTION
    ],
    Metrics.COMPLEXITY: [
        Plots.COMPLEXITY.COMPLEXITY_RADAR,
        Plots.COMPLEXITY.EFFICIENCY_METRICS
    ],
    Metrics.CIRCUIT_PERFORMANCE: None  # All plots
}, save=True, show=False)

# NEW API: Explore what was created
print("Generated visualizations:")
for metric_name, figures in analysis_plots.items():
    print(f"  {metric_name}: {len(figures)} plots")
    
    # Get metadata for each plot
    available_plots = visualizer.get_available_plots(metric_name)
    for plot_name in available_plots[metric_name]:
        metadata = visualizer.get_plot_metadata(metric_name, plot_name)
        print(f"    - {plot_name}: {metadata.description}")

# Print summary
visualizer.print_available_metrics()

Multiple Jobs Analysis with New API

from qiskit_aer.noise import NoiseModel, depolarizing_error
from qward.visualization.constants import Metrics, Plots

# Create multiple jobs with different noise levels
jobs = []
noise_levels = [0.0, 0.01, 0.05, 0.1]

for noise_level in noise_levels:
    if noise_level == 0.0:
        job = simulator.run(circuit, shots=1024)
    else:
        noise_model = NoiseModel()
        error = depolarizing_error(noise_level, 1)
        noise_model.add_all_qubit_quantum_error(error, ['u1', 'u2', 'u3'])
        
        noisy_simulator = AerSimulator(noise_model=noise_model)
        job = noisy_simulator.run(circuit, shots=1024)
    
    jobs.append(job)

# Analyze with CircuitPerformanceMetrics
circuit_performance = CircuitPerformanceMetrics(circuit=circuit)
for job in jobs:
    circuit_performance.add_job(job)

scanner = Scanner(circuit=circuit)
scanner.add_strategy(circuit_performance)
metrics_dict = scanner.calculate_metrics()

# NEW API: Visualize noise effects with granular control
perf_viz = CircuitPerformanceVisualizer(
    {k: v for k, v in metrics_dict.items() if k.startswith("CircuitPerformance")},
    output_dir="noise_analysis"
)

# Generate specific plots to analyze noise impact
noise_analysis_plots = perf_viz.generate_plots([
    Plots.CIRCUIT_PERFORMANCE.SUCCESS_ERROR_COMPARISON
], save=True, show=False)

# Create comprehensive dashboard
dashboard = perf_viz.create_dashboard(save=True, show=False)

# NEW API: Get plot metadata to understand what was generated
for plot_name in perf_viz.get_available_plots():
    metadata = perf_viz.get_plot_metadata(plot_name)
    print(f"{plot_name}: {metadata.description} ({metadata.plot_type.value})")

Custom Success Criteria with New API

from qward.visualization.constants import Metrics, Plots

# Define custom success criteria for Bell state
def bell_state_success(outcome):
    """Success for Bell state: |00⟩ or |11⟩"""
    clean = outcome.replace(" ", "")
    return clean in ["00", "11"]

# Use custom criteria
circuit_performance = CircuitPerformanceMetrics(
    circuit=circuit,
    job=job,
    success_criteria=bell_state_success
)

scanner = Scanner(circuit=circuit)
scanner.add_strategy(QiskitMetrics(circuit))
scanner.add_strategy(ComplexityMetrics(circuit))
scanner.add_strategy(circuit_performance)

# Create visualizations with custom styling
config = PlotConfig(
    figsize=(16, 12),
    style="quantum",
    color_palette=["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728"],
    save_format="png",
    dpi=150
)

visualizer = Visualizer(
    scanner=scanner,
    config=config,
    output_dir="bell_state_analysis"
)

# NEW API: Create comprehensive analysis with type-safe constants
dashboards = visualizer.create_dashboard(save=True, show=False)

# NEW API: Generate specific plots for Bell state analysis
bell_analysis = visualizer.generate_plots({
    Metrics.QISKIT: [Plots.QISKIT.CIRCUIT_STRUCTURE],
    Metrics.COMPLEXITY: [Plots.COMPLEXITY.COMPLEXITY_RADAR],
    Metrics.CIRCUIT_PERFORMANCE: [
        Plots.CIRCUIT_PERFORMANCE.SUCCESS_ERROR_COMPARISON
    ]
}, save=True, show=False)

# NEW API: Explore the analysis results
print("Bell State Analysis Results:")
for metric_name, figures in bell_analysis.items():
    print(f"\n{metric_name}:")
    available_plots = visualizer.get_available_plots(metric_name)
    for plot_name in available_plots[metric_name]:
        if any(plot_name in generated_plots for generated_plots in bell_analysis.values()):
            metadata = visualizer.get_plot_metadata(metric_name, plot_name)
            print(f"  ✅ {plot_name}: {metadata.description}")

Circuit Comparison Workflow with New API

from qward.visualization.constants import Metrics, Plots

# Compare different circuit implementations
circuits = {
    "basic_bell": create_bell_circuit(),
    "optimized_bell": create_optimized_bell_circuit(),
    "noisy_bell": create_bell_circuit_with_noise()
}

comparison_results = {}

for circuit_name, circuit in circuits.items():
    # Analyze each circuit
    scanner = Scanner(circuit=circuit, strategies=[QiskitMetrics, ComplexityMetrics])
    
    # Create visualizer for this circuit
    visualizer = Visualizer(
        scanner=scanner, 
        output_dir=f"comparison/{circuit_name}"
    )
    
    # NEW API: Generate comparison plots
    comparison_plots = visualizer.generate_plots({
        Metrics.QISKIT: [
            Plots.QISKIT.CIRCUIT_STRUCTURE,
            Plots.QISKIT.GATE_DISTRIBUTION
        ],
        Metrics.COMPLEXITY: [
            Plots.COMPLEXITY.GATE_BASED_METRICS,
            Plots.COMPLEXITY.COMPLEXITY_RADAR
        ]
    }, save=True, show=False)
    
    comparison_results[circuit_name] = comparison_plots

# NEW API: Generate summary report
print("Circuit Comparison Summary:")
for circuit_name in circuits.keys():
    print(f"\n{circuit_name}:")
    
    # Get metrics for comparison
    scanner = Scanner(circuit=circuits[circuit_name], strategies=[QiskitMetrics, ComplexityMetrics])
    qiskit_metrics = QiskitMetrics(circuits[circuit_name])
    complexity_metrics = ComplexityMetrics(circuits[circuit_name])
    
    qiskit_schema = qiskit_metrics.get_metrics()
    complexity_schema = complexity_metrics.get_metrics()
    
    print(f"  Depth: {qiskit_schema.basic_metrics.depth}")
    print(f"  Gate Count: {complexity_schema.gate_based_metrics.gate_count}")
    print(f"  Complexity Score: {complexity_schema.derived_metrics.weighted_complexity:.3f}")

Data Format Requirements

QiskitVisualizer Data Format

Expects a DataFrame with QiskitMetrics columns:

  • basic_metrics.*: Circuit structure metrics (depth, width, size, qubits, classical bits)

  • instruction_metrics.*: Gate and instruction analysis (connected components, nonlocal gates)

  • scheduling_metrics.*: Timing information (if available)

ComplexityVisualizer Data Format

Expects a DataFrame with ComplexityMetrics columns:

  • gate_based_metrics.*: Gate counts and circuit depth

  • entanglement_metrics.*: Entanglement analysis

  • standardized_metrics.*: Normalized complexity indicators

  • advanced_metrics.*: Efficiency and parallelism metrics

  • derived_metrics.*: Weighted complexity scores

CircuitPerformanceVisualizer Data Format

Expects specific DataFrame structures:

Individual Jobs DataFrame ("CircuitPerformance.individual_jobs")

  • job_id: Unique identifier for each job

  • success_rate: Success rate (0.0 to 1.0)

  • error_rate: Error rate (0.0 to 1.0)

  • total_shots: Total number of shots

  • successful_shots: Number of successful shots

Aggregate DataFrame ("CircuitPerformance.aggregate")

  • mean_success_rate: Mean success rate across jobs

  • std_success_rate: Standard deviation of success rate

  • min_success_rate: Minimum success rate

  • max_success_rate: Maximum success rate

  • total_trials: Total number of trials across all jobs

  • error_rate: Overall error rate

Creating Custom Visualizers

You can extend the visualization system by creating custom visualizers with the new plot registry system:

from qward.visualization.base import VisualizationStrategy, PlotMetadata, PlotType
import matplotlib.pyplot as plt

class MyCustomVisualizer(VisualizationStrategy):
    # NEW: Define plot registry with rich metadata
    PLOT_REGISTRY = {
        "custom_analysis": PlotMetadata(
            name="custom_analysis",
            method_name="plot_custom_analysis",
            description="Custom quantum circuit analysis visualization",
            plot_type=PlotType.BAR_CHART,
            filename="custom_analysis",
            dependencies=["custom.metric1", "custom.metric2"],
            category="analysis"
        ),
        "custom_comparison": PlotMetadata(
            name="custom_comparison",
            method_name="plot_custom_comparison",
            description="Comparative analysis of custom metrics",
            plot_type=PlotType.LINE_PLOT,
            filename="custom_comparison",
            dependencies=["custom.metric1", "custom.metric3"],
            category="comparison"
        )
    }
    
    def __init__(self, metrics_dict, output_dir="img", config=None):
        super().__init__(output_dir, config)
        self.metrics_dict = metrics_dict
        self.my_df = metrics_dict.get("MyCustomMetrics")
        
        if self.my_df is None:
            raise ValueError("'MyCustomMetrics' data not found in metrics_dict.")
    
    @classmethod
    def get_available_plots(cls):
        """NEW API: Return list of available plot names."""
        return list(cls.PLOT_REGISTRY.keys())
    
    @classmethod
    def get_plot_metadata(cls, plot_name):
        """NEW API: Return metadata for a specific plot."""
        if plot_name not in cls.PLOT_REGISTRY:
            raise ValueError(f"Plot '{plot_name}' not found in registry")
        return cls.PLOT_REGISTRY[plot_name]
    
    def generate_plot(self, plot_name, save=False, show=False):
        """NEW API: Generate a specific plot by name."""
        if plot_name not in self.PLOT_REGISTRY:
            raise ValueError(f"Plot '{plot_name}' not available")
        
        metadata = self.PLOT_REGISTRY[plot_name]
        method = getattr(self, metadata.method_name)
        return method(save=save, show=show)
    
    def generate_plots(self, plot_names, save=False, show=False):
        """NEW API: Generate multiple specific plots."""
        figures = []
        for plot_name in plot_names:
            fig = self.generate_plot(plot_name, save=save, show=show)
            figures.append(fig)
        return figures
    
    def generate_all_plots(self, save=False, show=False):
        """NEW API: Generate all available plots."""
        return self.generate_plots(self.get_available_plots(), save=save, show=show)
    
    def plot_custom_analysis(self, save=False, show=False):
        """Create a custom analysis plot."""
        fig, ax = plt.subplots(figsize=self.config.figsize)
        
        # Extract data using base class utilities
        custom_data = self._extract_metrics_from_columns(
            self.my_df, 
            ["custom.metric1", "custom.metric2"],
            prefix_to_remove="custom."
        )
        
        # Create plot using base class utilities
        self._create_bar_plot_with_labels(
            data=custom_data,
            ax=ax,
            title="Custom Analysis",
            xlabel="Metrics",
            ylabel="Values",
            value_format="auto"
        )
        
        if save:
            self.save_plot(fig, "custom_analysis")
        if show:
            self.show_plot(fig)
        return fig
    
    def plot_custom_comparison(self, save=False, show=False):
        """Create a custom comparison plot."""
        fig, ax = plt.subplots(figsize=self.config.figsize)
        
        # Custom comparison logic here
        # ...
        
        if save:
            self.save_plot(fig, "custom_comparison")
        if show:
            self.show_plot(fig)
        return fig
    
    def create_dashboard(self, save=False, show=False):
        """Create a dashboard with all plots."""
        # Create a comprehensive dashboard
        fig, axes = plt.subplots(2, 1, figsize=(self.config.figsize[0], self.config.figsize[1] * 2))
        
        # Use existing plot methods with axis override
        self.plot_custom_analysis(save=False, show=False)
        self.plot_custom_comparison(save=False, show=False)
        
        if save:
            self.save_plot(fig, "custom_dashboard")
        if show:
            self.show_plot(fig)
        return fig

# Register with unified visualizer
visualizer = Visualizer(metrics_data=metrics_dict)
visualizer.register_strategy("MyCustomMetrics", MyCustomVisualizer)

# NEW API: Use custom visualizer with type-safe approach
custom_plots = visualizer.generate_plots({
    "MyCustomMetrics": ["custom_analysis", "custom_comparison"]
}, save=True, show=False)

# NEW API: Explore custom visualizer metadata
available_plots = visualizer.get_available_plots("MyCustomMetrics")
for plot_name in available_plots["MyCustomMetrics"]:
    metadata = visualizer.get_plot_metadata("MyCustomMetrics", plot_name)
    print(f"{plot_name}: {metadata.description} ({metadata.plot_type.value})")

Integration with Jupyter Notebooks

QWARD visualizations work seamlessly in Jupyter notebooks with the new API:

# In a Jupyter cell
%matplotlib inline

from qward.visualization.constants import Metrics, Plots

# Create visualizer
visualizer = Visualizer(scanner=scanner)

# NEW API: Show specific plots inline with type-safe constants
qiskit_plots = visualizer.generate_plots({
    Metrics.QISKIT: [Plots.QISKIT.CIRCUIT_STRUCTURE]
}, show=True, save=False)

complexity_plots = visualizer.generate_plots({
    Metrics.COMPLEXITY: [Plots.COMPLEXITY.COMPLEXITY_RADAR]
}, show=True, save=False)

# NEW API: Create dashboards inline
dashboards = visualizer.create_dashboard(show=True, save=False)

# NEW API: Explore available plots interactively
available_plots = visualizer.get_available_plots()
for metric_name, plot_names in available_plots.items():
    print(f"\n{metric_name} plots:")
    for plot_name in plot_names:
        metadata = visualizer.get_plot_metadata(metric_name, plot_name)
        print(f"  - {plot_name}: {metadata.description}")

Performance Tips

  1. Use Type-Safe Constants: Import Metrics and Plots for IDE autocompletion and error prevention

  2. Leverage Memory-Efficient Defaults: Default save=False, show=False prevents unwanted file creation

  3. Use Granular Control: Generate only the plots you need with generate_plots()

  4. Batch Operations: Use generate_plots() with multiple selections for efficiency

  5. Output Formats: Use PNG for quick previews, SVG for publications

  6. DPI Settings: Use lower DPI (150) for quick analysis, higher (300+) for publications

  7. Plot Discovery: Use get_available_plots() to explore before generating

  8. Memory Management: The visualization system automatically handles figure cleanup

Troubleshooting

Common Issues

  1. Missing Data Keys: Ensure your metrics dictionary contains the expected keys for each visualizer

  2. Empty DataFrames: Check that your metric calculations completed successfully

  3. Plot Not Showing: Verify your matplotlib backend settings

  4. File Permissions: Ensure write permissions for the output directory

  5. Invalid Plot Names: Use constants from qward.visualization.constants to prevent typos

  6. Metric/Plot Mismatch: Verify plot names are valid for the specific metric type

Debug Information

# NEW API: Get comprehensive information about available visualizations
visualizer = Visualizer(scanner=scanner)

# Print available metrics and plots
visualizer.print_available_metrics()

# Get detailed metric summary
summary = visualizer.get_metric_summary()
print(summary)

# NEW API: Explore specific metric plots
available_plots = visualizer.get_available_plots()
for metric_name, plot_names in available_plots.items():
    print(f"\n{metric_name}:")
    for plot_name in plot_names:
        try:
            metadata = visualizer.get_plot_metadata(metric_name, plot_name)
            print(f"  ✅ {plot_name}: {metadata.description}")
            print(f"     Type: {metadata.plot_type.value}")
            print(f"     Dependencies: {metadata.dependencies}")
        except Exception as e:
            print(f"  ❌ {plot_name}: Error - {e}")

# NEW API: Test plot generation
try:
    test_plot = visualizer.generate_plot(
        Metrics.QISKIT, 
        Plots.QISKIT.CIRCUIT_STRUCTURE, 
        save=False, 
        show=False
    )
    print("✅ Plot generation successful")
    plt.close(test_plot)  # Clean up
except Exception as e:
    print(f"❌ Plot generation failed: {e}")

Error Handling Best Practices

from qward.visualization.constants import Metrics, Plots

# Robust error handling with new API
try:
    # Check if metric is available
    available_metrics = visualizer.get_available_metrics()
    if Metrics.QISKIT not in available_metrics:
        print("QiskitMetrics not available")
        return
    
    # Check if plot is available for this metric
    available_plots = visualizer.get_available_plots(Metrics.QISKIT)
    if Plots.QISKIT.CIRCUIT_STRUCTURE not in available_plots[Metrics.QISKIT]:
        print("Circuit structure plot not available")
        return
    
    # Generate plot safely
    plot = visualizer.generate_plot(
        Metrics.QISKIT, 
        Plots.QISKIT.CIRCUIT_STRUCTURE, 
        save=True, 
        show=False
    )
    print("✅ Plot generated successfully")
    
except Exception as e:
    print(f"❌ Error generating plot: {e}")
    # Log additional debug information
    print(f"Available metrics: {list(available_metrics)}")
    print(f"Available plots: {available_plots}")

Complete Method Reference

Visualizer (Unified Entry Point) - New API

Core Methods

  • get_available_plots(metric_name=None): Get available plots for all metrics or specific metric

  • get_plot_metadata(metric_name, plot_name): Get detailed metadata for a specific plot

  • generate_plot(metric_name, plot_name, save=False, show=False): Generate single plot

  • generate_plots(selections, save=False, show=False): Generate multiple selected plots

  • create_dashboard(save=False, show=False): Create dashboards for all metrics

  • get_available_metrics(): Get list of available metrics for visualization

  • get_metric_summary(): Get summary information about available metrics

  • print_available_metrics(): Print detailed information about available visualizations

  • register_strategy(metric_name, strategy_class): Register custom visualization strategies

Usage Examples

# Get all available plots
all_plots = visualizer.get_available_plots()

# Get plots for specific metric
qiskit_plots = visualizer.get_available_plots(Metrics.QISKIT)

# Get plot metadata
metadata = visualizer.get_plot_metadata(Metrics.QISKIT, Plots.QISKIT.CIRCUIT_STRUCTURE)

# Generate single plot
single_plot = visualizer.generate_plot(Metrics.QISKIT, Plots.QISKIT.CIRCUIT_STRUCTURE)

# Generate selected plots
selected_plots = visualizer.generate_plots({
    Metrics.QISKIT: [Plots.QISKIT.CIRCUIT_STRUCTURE, Plots.QISKIT.GATE_DISTRIBUTION],
    Metrics.COMPLEXITY: None  # All plots
})

Individual Visualizers - New API

All individual visualizers (QiskitVisualizer, ComplexityVisualizer, CircuitPerformanceVisualizer) share these common methods:

Class Methods

  • get_available_plots(): Return list of available plot names

  • get_plot_metadata(plot_name): Return metadata for specific plot

Instance Methods

  • generate_plot(plot_name, save=False, show=False): Generate specific plot by name

  • generate_plots(plot_names, save=False, show=False): Generate multiple specific plots

  • generate_all_plots(save=False, show=False): Generate all available plots

  • create_dashboard(save=False, show=False): Create comprehensive dashboard

Legacy Methods (Still Available)

  • Individual plot methods (e.g., plot_circuit_structure(), plot_complexity_radar())

  • These methods are still available but the new generate_plot() API is recommended

Constants Reference

from qward.visualization.constants import Metrics, Plots

# Metric Constants
Metrics.QISKIT                    # "QiskitMetrics"
Metrics.COMPLEXITY               # "ComplexityMetrics"  
Metrics.CIRCUIT_PERFORMANCE      # "CircuitPerformance"

# QiskitMetrics Plot Constants
Plots.QISKIT.CIRCUIT_STRUCTURE   # "circuit_structure"
Plots.QISKIT.GATE_DISTRIBUTION   # "gate_distribution"
Plots.QISKIT.INSTRUCTION_METRICS # "instruction_metrics"
Plots.QISKIT.CIRCUIT_SUMMARY     # "circuit_summary"

# ComplexityMetrics Plot Constants
Plots.COMPLEXITY.GATE_BASED_METRICS  # "gate_based_metrics"
Plots.COMPLEXITY.COMPLEXITY_RADAR    # "complexity_radar"
Plots.COMPLEXITY.EFFICIENCY_METRICS  # "efficiency_metrics"

# CircuitPerformanceMetrics Plot Constants
Plots.CIRCUIT_PERFORMANCE.SUCCESS_ERROR_COMPARISON  # "success_error_comparison"
Plots.CIRCUIT_PERFORMANCE.SHOT_DISTRIBUTION         # "shot_distribution"
Plots.CIRCUIT_PERFORMANCE.AGGREGATE_SUMMARY         # "aggregate_summary"

Migration from Old API

Quick Migration Guide

Old API:

# Old methods (deprecated)
visualizer.visualize_all()
visualizer.visualize_metric("QiskitMetrics")
strategy.plot_all()
strategy.plot_circuit_structure()

New API:

# New type-safe methods
from qward.visualization.constants import Metrics, Plots

visualizer.generate_plots({Metrics.QISKIT: None, Metrics.COMPLEXITY: None})
visualizer.generate_plots({Metrics.QISKIT: None})
strategy.generate_all_plots()
strategy.generate_plot(Plots.QISKIT.CIRCUIT_STRUCTURE)

Benefits of Migration

  1. Type Safety: Constants prevent typos and provide IDE autocompletion

  2. Rich Metadata: Detailed information about each plot

  3. Granular Control: Generate exactly the plots you need

  4. Memory Efficiency: Better defaults for batch processing

  5. Error Prevention: Validation of metric and plot combinations

  6. Future-Proof: New features will be added to the new API

For more examples and advanced usage, see the qward/examples/ directory, particularly:

  • test_new_visualization_api.py: Comprehensive test suite for new API

  • new_api_usage_example.py: Practical usage patterns

  • example_visualizer.py: Complete visualization examples

  • visualization_demo.py: CircuitPerformanceVisualizer demonstrations