QWARD Quickstart Guide

Welcome to QWARD! This guide will get you up and running with quantum circuit analysis in just a few minutes.

What is QWARD?

QWARD is a Python library for analyzing quantum circuits and their execution results. It provides:

  • Circuit Analysis: Extract metrics from quantum circuits (depth, complexity, gate counts)

  • Performance Analysis: Analyze execution results (success rates, error rates, statistical properties)

  • Schema Validation: Type-safe, validated metrics with IDE support

  • Visualization: Beautiful plots and dashboards for your analysis

Installation

# Clone and install
git clone https://github.com/your-org/qiskit-qward.git
cd qiskit-qward
pip install -e .

Quick Example

Fastest Way (New Fluent API)

from qiskit import QuantumCircuit
from qward import Scanner

circuit = QuantumCircuit(2, 2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()

# One-liner: analyze with all pre-runtime metrics and visualize
Scanner(circuit).scan().summary().visualize(save=True, show=False)

# Or pick specific metrics
from qward.metrics import QiskitMetrics, ComplexityMetrics
results = Scanner(circuit, strategies=[QiskitMetrics, ComplexityMetrics]).scan()
print(results["QiskitMetrics"])

Detailed Way (Full Control)

Let’s analyze a simple Bell state circuit:

from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qward import Scanner
from qward.metrics import QiskitMetrics, ComplexityMetrics, CircuitPerformanceMetrics

# 1. Create a Bell state circuit
circuit = QuantumCircuit(2, 2)
circuit.h(0)
circuit.cx(0, 1)
circuit.measure_all()

# 2. Run on simulator (optional, needed for CircuitPerformanceMetrics)
simulator = AerSimulator()
job = simulator.run(circuit, shots=1000)

# 3. Analyze with Scanner (returns DataFrames)
scanner = Scanner(circuit=circuit, job=job)
scanner.add_strategy(QiskitMetrics(circuit))
scanner.add_strategy(ComplexityMetrics(circuit))
scanner.add_strategy(CircuitPerformanceMetrics(circuit=circuit, job=job))

results = scanner.calculate_metrics()
print("Available metrics:", list(results.keys()))

# 4. Use schema-based API for type-safe access
qiskit_metrics = QiskitMetrics(circuit)
metrics = qiskit_metrics.get_metrics()  # Returns QiskitMetricsSchema

print(f"Circuit depth: {metrics.basic_metrics.depth}")
print(f"Gate count: {metrics.basic_metrics.size}")
print(f"Number of qubits: {metrics.basic_metrics.num_qubits}")

Core Concepts

Unified API

All metric classes use the same simple interface:

# All metric classes work the same way
calculator = QiskitMetrics(circuit)        # or ComplexityMetrics(circuit)
metrics = calculator.get_metrics()         # Returns validated schema object
depth = metrics.basic_metrics.depth       # Type-safe access with IDE support

Schema Validation

QWARD provides automatic data validation:

# Type safety and validation built-in
complexity_metrics = ComplexityMetrics(circuit)
schema = complexity_metrics.get_metrics()

# IDE autocomplete and type checking
print(f"Gate count: {schema.gate_based_metrics.gate_count}")
print(f"T-gate count: {schema.gate_based_metrics.t_count}")
print(f"Circuit efficiency: {schema.advanced_metrics.circuit_efficiency:.3f}")

# Validation catches errors automatically
# (e.g., efficiency values must be between 0.0-1.0)

Scanner Integration

Scanner automatically handles schema-to-DataFrame conversion:

# Scanner works seamlessly with schema objects
scanner = Scanner(circuit=circuit)
scanner.add_strategy(QiskitMetrics(circuit))
scanner.add_strategy(ComplexityMetrics(circuit))

# Returns DataFrames for analysis and visualization
dataframes = scanner.calculate_metrics()

Available Metrics

QiskitMetrics

Extracts basic circuit properties:

qiskit_metrics = QiskitMetrics(circuit)
metrics = qiskit_metrics.get_metrics()

# Basic circuit properties
print(f"Depth: {metrics.basic_metrics.depth}")
print(f"Width: {metrics.basic_metrics.width}")
print(f"Size: {metrics.basic_metrics.size}")
print(f"Qubits: {metrics.basic_metrics.num_qubits}")

# Instruction analysis
print(f"Connected components: {metrics.instruction_metrics.num_connected_components}")
print(f"Non-local gates: {metrics.instruction_metrics.num_nonlocal_gates}")

ComplexityMetrics

Analyzes circuit complexity:

complexity_metrics = ComplexityMetrics(circuit)
metrics = complexity_metrics.get_metrics()

# Gate-based complexity
print(f"Gate count: {metrics.gate_based_metrics.gate_count}")
print(f"T-gate count: {metrics.gate_based_metrics.t_count}")
print(f"CNOT count: {metrics.gate_based_metrics.cnot_count}")

# Advanced complexity indicators
print(f"Circuit volume: {metrics.standardized_metrics.circuit_volume}")
print(f"Parallelism factor: {metrics.advanced_metrics.parallelism_factor:.3f}")
print(f"Weighted complexity: {metrics.derived_metrics.weighted_complexity}")

CircuitPerformanceMetrics

Analyzes execution performance:

# Requires job execution results
circuit_performance = CircuitPerformanceMetrics(circuit=circuit, job=job)
metrics = circuit_performance.get_metrics()

# Success analysis
print(f"Success rate: {metrics.success_metrics.success_rate:.3f}")
print(f"Error rate: {metrics.success_metrics.error_rate:.3f}")
print(f"Successful shots: {metrics.success_metrics.successful_shots}")

# Statistical analysis
print(f"Entropy: {metrics.statistical_metrics.entropy:.3f}")
print(f"Uniformity: {metrics.statistical_metrics.uniformity:.3f}")

Custom Success Criteria

Define custom success criteria for CircuitPerformanceMetrics:

# Custom success criteria for Bell state
def bell_state_success(result: str) -> bool:
    clean_result = result.replace(" ", "")
    return clean_result in ["00", "11"]  # |00⟩ or |11⟩ states

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

metrics = circuit_performance.get_metrics()
print(f"Bell state success rate: {metrics.success_metrics.success_rate:.3f}")

Visualization

Create beautiful visualizations of your analysis with the new type-safe API:

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

# New fluent way
Scanner(circuit).scan().visualize(save=True, show=False)

# Existing approach (still fully supported)
visualizer = Visualizer(scanner=scanner, output_dir="my_analysis")
visualizer.create_dashboard(save=True, show=False)

# Calculate metrics first
scanner = Scanner(circuit=circuit, job=job)
scanner.add_strategy(QiskitMetrics(circuit))
scanner.add_strategy(ComplexityMetrics(circuit))
scanner.add_strategy(CircuitPerformanceMetrics(circuit=circuit, job=job))

# Create unified visualizer for full control APIs
visualizer = Visualizer(scanner=scanner, output_dir="my_analysis")

# NEW API: Generate specific plots with type-safe constants
selected_plots = visualizer.generate_plots(
    selections={
        Metrics.QISKIT: [
            Plots.Qiskit.CIRCUIT_STRUCTURE,
            Plots.Qiskit.GATE_DISTRIBUTION
        ],
        Metrics.COMPLEXITY: [
            Plots.Complexity.COMPLEXITY_RADAR
        ]
    },
    save=True,
    show=False,
)

# NEW API: Generate all plots for specific metrics
all_qiskit_plots = visualizer.generate_plots(
    selections={Metrics.QISKIT: None},  # None = all plots
    save=True,
    show=False,
)

# NEW API: Generate single plot
single_plot = visualizer.generate_plot(
    metric_name=Metrics.CIRCUIT_PERFORMANCE,
    plot_name=Plots.CircuitPerformance.SUCCESS_ERROR_COMPARISON,
    save=True, 
    show=False
)

# Create comprehensive dashboards (unchanged)
dashboards = visualizer.create_dashboard(save=True, show=False)

# NEW API: Explore available plots and metadata
available_plots = visualizer.get_available_plots()
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}: {metadata.description}")

print(f"Created {len(dashboards)} dashboards and {len(selected_plots)} plot collections")

Memory-Efficient Visualization

The new API defaults to memory-efficient settings:

# 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(
        selections={
            Metrics.QISKIT: None,
            Metrics.COMPLEXITY: None
        }
    )  # Default: save=False, show=False
    
    # Only save specific plots of interest
    important_plots = visualizer.generate_plots(
        selections={
            Metrics.QISKIT: [Plots.Qiskit.CIRCUIT_STRUCTURE],
            Metrics.COMPLEXITY: [Plots.Complexity.COMPLEXITY_RADAR]
        },
        save=True,
    )

Error Handling

Handle validation and execution errors gracefully:

try:
    # Calculate metrics with validation
    metrics = calculator.get_metrics()
    
    # Use validated data with confidence
    depth = metrics.basic_metrics.depth
    print(f"Circuit depth: {depth}")
    
except ImportError as e:
    print(f"Missing dependencies: {e}")
except Exception as e:
    print(f"Calculation error: {e}")

Next Steps

Key Benefits

Simple API: All metric classes use get_metrics() for consistent interface
Type Safety: Schema validation with IDE autocomplete and error prevention
Flexible Analysis: Scanner for DataFrames, schemas for type-safe access
Rich Visualization: Automatic plot generation with customizable styling
Extensible: Easy to add custom metrics and success criteria

Start analyzing your quantum circuits today with QWARD!