diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 62e40a2..ade2413 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -55,6 +55,7 @@ "./scientific-skills/networkx", "./scientific-skills/paper-2-web", "./scientific-skills/pathml", + "./scientific-skills/pennylane", "./scientific-skills/perplexity-search", "./scientific-skills/plotly", "./scientific-skills/polars", diff --git a/README.md b/README.md index 7f17600..e77adda 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # Claude Scientific Skills [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md) -[![Skills](https://img.shields.io/badge/Skills-125-brightgreen.svg)](#whats-included) +[![Skills](https://img.shields.io/badge/Skills-126-brightgreen.svg)](#whats-included) -A comprehensive collection of **125+ ready-to-use scientific skills** for Claude, created by the K-Dense team. Transform Claude into your AI research assistant capable of executing complex multi-step scientific workflows across biology, chemistry, medicine, and beyond. +A comprehensive collection of **126+ ready-to-use scientific skills** for Claude, created by the K-Dense team. Transform Claude into your AI research assistant capable of executing complex multi-step scientific workflows across biology, chemistry, medicine, and beyond. These skills enable Claude to seamlessly work with specialized scientific libraries, databases, and tools across multiple scientific domains: - 🧬 Bioinformatics & Genomics - Sequence analysis, single-cell RNA-seq, gene regulatory networks, variant annotation, phylogenetic analysis @@ -32,10 +32,10 @@ These skills enable Claude to seamlessly work with specialized scientific librar ## 📦 What's Included -This repository provides **125+ scientific skills** organized into the following categories: +This repository provides **126+ scientific skills** organized into the following categories: - **26+ Scientific Databases** - Direct API access to OpenAlex, PubMed, ChEMBL, UniProt, COSMIC, ClinicalTrials.gov, and more -- **52+ Python Packages** - RDKit, Scanpy, PyTorch Lightning, scikit-learn, BioPython, GeoPandas, and others +- **53+ Python Packages** - RDKit, Scanpy, PyTorch Lightning, scikit-learn, BioPython, PennyLane, and others - **15+ Scientific Integrations** - Benchling, DNAnexus, LatchBio, OMERO, Protocols.io, and more - **20+ Analysis & Communication Tools** - Literature review, scientific writing, peer review, document processing @@ -78,9 +78,9 @@ Each skill includes: - **Multi-Step Workflows** - Execute complex pipelines with a single prompt ### 🎯 **Comprehensive Coverage** -- **125+ Skills** - Extensive coverage across all major scientific domains +- **126+ Skills** - Extensive coverage across all major scientific domains - **26+ Databases** - Direct access to OpenAlex, PubMed, ChEMBL, UniProt, COSMIC, and more -- **52+ Python Packages** - RDKit, Scanpy, PyTorch Lightning, scikit-learn, GeoPandas, and others +- **53+ Python Packages** - RDKit, Scanpy, PyTorch Lightning, scikit-learn, PennyLane, and others ### 🔧 **Easy Integration** - **One-Click Setup** - Install via Claude Code or MCP server diff --git a/docs/scientific-skills.md b/docs/scientific-skills.md index b09b977..4c44b82 100644 --- a/docs/scientific-skills.md +++ b/docs/scientific-skills.md @@ -105,6 +105,7 @@ - **PyMC** - Comprehensive Python library for Bayesian statistical modeling and probabilistic programming. Provides intuitive syntax for building probabilistic models, advanced MCMC sampling algorithms (NUTS, Metropolis-Hastings, Slice sampling), variational inference methods (ADVI, SVGD), Gaussian processes, time series models (ARIMA, state space models), and model comparison tools (WAIC, LOO). Features include: automatic differentiation via Aesara (formerly Theano), GPU acceleration support, parallel sampling, model diagnostics and convergence checking, and integration with ArviZ for visualization and analysis. Supports hierarchical models, mixture models, survival analysis, and custom distributions. Use cases: Bayesian data analysis, uncertainty quantification, A/B testing, time series forecasting, hierarchical modeling, and probabilistic machine learning - **PyMOO** - Python framework for multi-objective optimization using evolutionary algorithms. Provides implementations of state-of-the-art algorithms including NSGA-II, NSGA-III, MOEA/D, SPEA2, and reference-point based methods. Features include: support for constrained and unconstrained optimization, multiple problem types (continuous, discrete, mixed-variable), performance indicators (hypervolume, IGD, GD), visualization tools (Pareto front plots, convergence plots), and parallel evaluation support. Supports custom problem definitions, algorithm configuration, and result analysis. Designed for engineering design, parameter optimization, and any problem requiring optimization of multiple conflicting objectives simultaneously. Use cases: multi-objective optimization problems, Pareto-optimal solution finding, engineering design optimization, and research in evolutionary computation - **PyTorch Lightning** - Deep learning framework that organizes PyTorch code to eliminate boilerplate while maintaining full flexibility. Automates training workflows (40+ tasks including epoch/batch iteration, optimizer steps, gradient management, checkpointing), supports multi-GPU/TPU training with DDP/FSDP/DeepSpeed strategies, includes LightningModule for model organization, Trainer for automation, LightningDataModule for data pipelines, callbacks for extensibility, and integrations with TensorBoard, Wandb, MLflow for experiment tracking +- **PennyLane** - Cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Enables building and training quantum circuits with automatic differentiation, seamless integration with PyTorch/JAX/NumPy, and device-independent execution across simulators and quantum hardware (IBM, Amazon Braket, Google, Rigetti, IonQ). Key features include: quantum circuit construction with QNodes (quantum functions with automatic differentiation), 100+ quantum gates and operations (Pauli, Hadamard, rotation, controlled gates), circuit templates and layers for common ansatze (StronglyEntanglingLayers, BasicEntanglerLayers, UCCSD for chemistry), gradient computation methods (parameter-shift rule for hardware, backpropagation for simulators, adjoint differentiation), quantum chemistry module (molecular Hamiltonian construction, VQE for ground state energy, differentiable Hartree-Fock solver), ML framework integration (TorchLayer for PyTorch models, JAX transformations, TensorFlow deprecated), built-in optimizers (Adam, GradientDescent, QNG, Rotosolve), measurement types (expectation values, probabilities, samples, state vectors), device ecosystem (default.qubit simulator, lightning.qubit for performance, hardware plugins for IBM/Braket/Cirq/Rigetti/IonQ), and Catalyst for just-in-time compilation with adaptive circuits. Supports variational quantum algorithms (VQE, QAOA), quantum neural networks, hybrid quantum-classical models, data encoding strategies (angle, amplitude, IQP embeddings), and pulse-level programming. Use cases: variational quantum eigensolver for molecular simulations, quantum circuit machine learning with gradient-based optimization, hybrid quantum-classical neural networks, quantum chemistry calculations with differentiable workflows, quantum algorithm prototyping with hardware-agnostic code, quantum machine learning research with automatic differentiation, and deploying quantum circuits across multiple quantum computing platforms - **QuTiP** - Quantum Toolbox in Python for simulating and analyzing quantum mechanical systems. Provides comprehensive tools for both closed (unitary) and open (dissipative) quantum systems including quantum states (kets, bras, density matrices, Fock states, coherent states), quantum operators (creation/annihilation operators, Pauli matrices, angular momentum operators, quantum gates), time evolution solvers (Schrödinger equation with sesolve, Lindblad master equation with mesolve, quantum trajectories with Monte Carlo mcsolve, Bloch-Redfield brmesolve, Floquet methods for periodic Hamiltonians), analysis tools (expectation values, entropy measures, fidelity, concurrence, correlation functions, steady state calculations), visualization (Bloch sphere with animations, Wigner functions, Q-functions, Fock distributions, matrix histograms), and advanced methods (Hierarchical Equations of Motion for non-Markovian dynamics, permutational invariance for identical particles, stochastic solvers, superoperators). Supports tensor products for composite systems, partial traces, time-dependent Hamiltonians, multiple dissipation channels, and parallel processing. Includes extensive documentation, tutorials, and examples. Use cases: quantum optics simulations (cavity QED, photon statistics), quantum computing (gate operations, circuit dynamics), open quantum systems (decoherence, dissipation), quantum information theory (entanglement dynamics, quantum channels), condensed matter physics (spin chains, many-body systems), and general quantum mechanics research and education - **scikit-learn** - Industry-standard Python library for classical machine learning providing comprehensive supervised learning (classification: Logistic Regression, SVM, Decision Trees, Random Forests with 17+ variants, Gradient Boosting with XGBoost-compatible HistGradientBoosting, Naive Bayes, KNN, Neural Networks/MLP; regression: Linear, Ridge, Lasso, ElasticNet, SVR, ensemble methods), unsupervised learning (clustering: K-Means, DBSCAN, HDBSCAN, OPTICS, Agglomerative/Hierarchical, Spectral, Gaussian Mixture Models, BIRCH, MeanShift; dimensionality reduction: PCA, Kernel PCA, t-SNE, Isomap, LLE, NMF, TruncatedSVD, FastICA, LDA; outlier detection: IsolationForest, LocalOutlierFactor, OneClassSVM), data preprocessing (scaling: StandardScaler, MinMaxScaler, RobustScaler; encoding: OneHotEncoder, OrdinalEncoder, LabelEncoder; imputation: SimpleImputer, KNNImputer, IterativeImputer; feature engineering: PolynomialFeatures, KBinsDiscretizer, text vectorization with CountVectorizer/TfidfVectorizer), model evaluation (cross-validation: KFold, StratifiedKFold, TimeSeriesSplit, GroupKFold; hyperparameter tuning: GridSearchCV, RandomizedSearchCV, HalvingGridSearchCV; metrics: 30+ evaluation metrics for classification/regression/clustering including accuracy, precision, recall, F1, ROC-AUC, MSE, R², silhouette score), and Pipeline/ColumnTransformer for production-ready workflows. Features consistent API (fit/predict/transform), extensive documentation, integration with NumPy/pandas/SciPy, joblib persistence, and scikit-learn-compatible ecosystem (XGBoost, LightGBM, CatBoost, imbalanced-learn). Optimized implementations using Cython/OpenMP for performance. Use cases: predictive modeling, customer segmentation, anomaly detection, feature engineering, model selection/validation, text classification, image classification (with feature extraction), time series forecasting (with preprocessing), medical diagnosis, fraud detection, recommendation systems, and any tabular data ML task requiring interpretable models or established algorithms - **scikit-survival** - Survival analysis and time-to-event modeling with censored data. Built on scikit-learn, provides Cox proportional hazards models (CoxPHSurvivalAnalysis, CoxnetSurvivalAnalysis with elastic net regularization), ensemble methods (Random Survival Forests, Gradient Boosting), Survival Support Vector Machines (linear and kernel), non-parametric estimators (Kaplan-Meier, Nelson-Aalen), competing risks analysis, and specialized evaluation metrics (concordance index, time-dependent AUC, Brier score). Handles right-censored data, integrates with scikit-learn pipelines, and supports feature selection and hyperparameter tuning via cross-validation diff --git a/scientific-skills/pennylane/SKILL.md b/scientific-skills/pennylane/SKILL.md new file mode 100644 index 0000000..9adf839 --- /dev/null +++ b/scientific-skills/pennylane/SKILL.md @@ -0,0 +1,220 @@ +--- +name: pennylane +description: Cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Enables building and training quantum circuits with automatic differentiation, seamless integration with PyTorch/JAX/TensorFlow, and device-independent execution across simulators and quantum hardware (IBM, Amazon Braket, Google, Rigetti, IonQ, etc.). Use when working with quantum circuits, variational quantum algorithms (VQE, QAOA), quantum neural networks, hybrid quantum-classical models, molecular simulations, quantum chemistry calculations, or any quantum computing tasks requiring gradient-based optimization, hardware-agnostic programming, or quantum machine learning workflows. +--- + +# PennyLane + +## Overview + +PennyLane is a quantum computing library that enables training quantum computers like neural networks. It provides automatic differentiation of quantum circuits, device-independent programming, and seamless integration with classical machine learning frameworks. + +## Installation + +Install using uv: + +```bash +uv pip install pennylane +``` + +For quantum hardware access, install device plugins: + +```bash +# IBM Quantum +uv pip install pennylane-qiskit + +# Amazon Braket +uv pip install amazon-braket-pennylane-plugin + +# Google Cirq +uv pip install pennylane-cirq + +# Rigetti Forest +uv pip install pennylane-rigetti + +# IonQ +uv pip install pennylane-ionq +``` + +## Quick Start + +Build a quantum circuit and optimize its parameters: + +```python +import pennylane as qml +from pennylane import numpy as np + +# Create device +dev = qml.device('default.qubit', wires=2) + +# Define quantum circuit +@qml.qnode(dev) +def circuit(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# Optimize parameters +opt = qml.GradientDescentOptimizer(stepsize=0.1) +params = np.array([0.1, 0.2], requires_grad=True) + +for i in range(100): + params = opt.step(circuit, params) +``` + +## Core Capabilities + +### 1. Quantum Circuit Construction + +Build circuits with gates, measurements, and state preparation. See `references/quantum_circuits.md` for: +- Single and multi-qubit gates +- Controlled operations and conditional logic +- Mid-circuit measurements and adaptive circuits +- Various measurement types (expectation, probability, samples) +- Circuit inspection and debugging + +### 2. Quantum Machine Learning + +Create hybrid quantum-classical models. See `references/quantum_ml.md` for: +- Integration with PyTorch, JAX, TensorFlow +- Quantum neural networks and variational classifiers +- Data encoding strategies (angle, amplitude, basis, IQP) +- Training hybrid models with backpropagation +- Transfer learning with quantum circuits + +### 3. Quantum Chemistry + +Simulate molecules and compute ground state energies. See `references/quantum_chemistry.md` for: +- Molecular Hamiltonian generation +- Variational Quantum Eigensolver (VQE) +- UCCSD ansatz for chemistry +- Geometry optimization and dissociation curves +- Molecular property calculations + +### 4. Device Management + +Execute on simulators or quantum hardware. See `references/devices_backends.md` for: +- Built-in simulators (default.qubit, lightning.qubit, default.mixed) +- Hardware plugins (IBM, Amazon Braket, Google, Rigetti, IonQ) +- Device selection and configuration +- Performance optimization and caching +- GPU acceleration and JIT compilation + +### 5. Optimization + +Train quantum circuits with various optimizers. See `references/optimization.md` for: +- Built-in optimizers (Adam, gradient descent, momentum, RMSProp) +- Gradient computation methods (backprop, parameter-shift, adjoint) +- Variational algorithms (VQE, QAOA) +- Training strategies (learning rate schedules, mini-batches) +- Handling barren plateaus and local minima + +### 6. Advanced Features + +Leverage templates, transforms, and compilation. See `references/advanced_features.md` for: +- Circuit templates and layers +- Transforms and circuit optimization +- Pulse-level programming +- Catalyst JIT compilation +- Noise models and error mitigation +- Resource estimation + +## Common Workflows + +### Train a Variational Classifier + +```python +# 1. Define ansatz +@qml.qnode(dev) +def classifier(x, weights): + # Encode data + qml.AngleEmbedding(x, wires=range(4)) + + # Variational layers + qml.StronglyEntanglingLayers(weights, wires=range(4)) + + return qml.expval(qml.PauliZ(0)) + +# 2. Train +opt = qml.AdamOptimizer(stepsize=0.01) +weights = np.random.random((3, 4, 3)) # 3 layers, 4 wires + +for epoch in range(100): + for x, y in zip(X_train, y_train): + weights = opt.step(lambda w: (classifier(x, w) - y)**2, weights) +``` + +### Run VQE for Molecular Ground State + +```python +from pennylane import qchem + +# 1. Build Hamiltonian +symbols = ['H', 'H'] +coords = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.74]) +H, n_qubits = qchem.molecular_hamiltonian(symbols, coords) + +# 2. Define ansatz +@qml.qnode(dev) +def vqe_circuit(params): + qml.BasisState(qchem.hf_state(2, n_qubits), wires=range(n_qubits)) + qml.UCCSD(params, wires=range(n_qubits)) + return qml.expval(H) + +# 3. Optimize +opt = qml.AdamOptimizer(stepsize=0.1) +params = np.zeros(10, requires_grad=True) + +for i in range(100): + params, energy = opt.step_and_cost(vqe_circuit, params) + print(f"Step {i}: Energy = {energy:.6f} Ha") +``` + +### Switch Between Devices + +```python +# Same circuit, different backends +circuit_def = lambda dev: qml.qnode(dev)(circuit_function) + +# Test on simulator +dev_sim = qml.device('default.qubit', wires=4) +result_sim = circuit_def(dev_sim)(params) + +# Run on quantum hardware +dev_hw = qml.device('qiskit.ibmq', wires=4, backend='ibmq_manila') +result_hw = circuit_def(dev_hw)(params) +``` + +## Detailed Documentation + +For comprehensive coverage of specific topics, consult the reference files: + +- **Getting started**: `references/getting_started.md` - Installation, basic concepts, first steps +- **Quantum circuits**: `references/quantum_circuits.md` - Gates, measurements, circuit patterns +- **Quantum ML**: `references/quantum_ml.md` - Hybrid models, framework integration, QNNs +- **Quantum chemistry**: `references/quantum_chemistry.md` - VQE, molecular Hamiltonians, chemistry workflows +- **Devices**: `references/devices_backends.md` - Simulators, hardware plugins, device configuration +- **Optimization**: `references/optimization.md` - Optimizers, gradients, variational algorithms +- **Advanced**: `references/advanced_features.md` - Templates, transforms, JIT compilation, noise + +## Best Practices + +1. **Start with simulators** - Test on `default.qubit` before deploying to hardware +2. **Use parameter-shift for hardware** - Backpropagation only works on simulators +3. **Choose appropriate encodings** - Match data encoding to problem structure +4. **Initialize carefully** - Use small random values to avoid barren plateaus +5. **Monitor gradients** - Check for vanishing gradients in deep circuits +6. **Cache devices** - Reuse device objects to reduce initialization overhead +7. **Profile circuits** - Use `qml.specs()` to analyze circuit complexity +8. **Test locally** - Validate on simulators before submitting to hardware +9. **Use templates** - Leverage built-in templates for common circuit patterns +10. **Compile when possible** - Use Catalyst JIT for performance-critical code + +## Resources + +- Official documentation: https://docs.pennylane.ai +- Codebook (tutorials): https://pennylane.ai/codebook +- QML demonstrations: https://pennylane.ai/qml/demonstrations +- Community forum: https://discuss.pennylane.ai +- GitHub: https://github.com/PennyLaneAI/pennylane diff --git a/scientific-skills/pennylane/references/advanced_features.md b/scientific-skills/pennylane/references/advanced_features.md new file mode 100644 index 0000000..731ae97 --- /dev/null +++ b/scientific-skills/pennylane/references/advanced_features.md @@ -0,0 +1,667 @@ +# Advanced Features in PennyLane + +## Table of Contents +1. [Templates and Layers](#templates-and-layers) +2. [Transforms](#transforms) +3. [Pulse Programming](#pulse-programming) +4. [Catalyst and JIT Compilation](#catalyst-and-jit-compilation) +5. [Adaptive Circuits](#adaptive-circuits) +6. [Noise Models](#noise-models) +7. [Resource Estimation](#resource-estimation) + +## Templates and Layers + +### Built-in Templates + +```python +import pennylane as qml +from pennylane.templates import * +from pennylane import numpy as np + +dev = qml.device('default.qubit', wires=4) + +# Strongly Entangling Layers +@qml.qnode(dev) +def circuit_sel(weights): + StronglyEntanglingLayers(weights, wires=range(4)) + return qml.expval(qml.PauliZ(0)) + +# Generate appropriately shaped weights +n_layers = 3 +n_wires = 4 +shape = StronglyEntanglingLayers.shape(n_layers, n_wires) +weights = np.random.random(shape) + +result = circuit_sel(weights) +``` + +### Basic Entangler Layers + +```python +@qml.qnode(dev) +def circuit_bel(weights): + # Simple entangling layer + BasicEntanglerLayers(weights, wires=range(4)) + return qml.expval(qml.PauliZ(0)) + +n_layers = 2 +weights = np.random.random((n_layers, 4)) +``` + +### Random Layers + +```python +@qml.qnode(dev) +def circuit_random(weights): + # Random circuit structure + RandomLayers(weights, wires=range(4)) + return qml.expval(qml.PauliZ(0)) + +n_layers = 5 +weights = np.random.random((n_layers, 4)) +``` + +### Simplified Two Design + +```python +@qml.qnode(dev) +def circuit_s2d(weights): + # Simplified two-design + SimplifiedTwoDesign(initial_layer_weights=weights[0], + weights=weights[1:], + wires=range(4)) + return qml.expval(qml.PauliZ(0)) +``` + +### Particle-Conserving Layers + +```python +@qml.qnode(dev) +def circuit_particle_conserving(weights): + # Preserve particle number (useful for chemistry) + ParticleConservingU1(weights, wires=range(4)) + return qml.expval(qml.PauliZ(0)) + +shape = ParticleConservingU1.shape(n_layers=2, n_wires=4) +weights = np.random.random(shape) +``` + +### Embedding Templates + +```python +# Angle embedding +@qml.qnode(dev) +def angle_embed(features): + AngleEmbedding(features, wires=range(4)) + return qml.expval(qml.PauliZ(0)) + +features = np.array([0.1, 0.2, 0.3, 0.4]) + +# Amplitude embedding +@qml.qnode(dev) +def amplitude_embed(features): + AmplitudeEmbedding(features, wires=range(2), normalize=True) + return qml.expval(qml.PauliZ(0)) + +features = np.array([0.5, 0.5, 0.5, 0.5]) + +# IQP embedding +@qml.qnode(dev) +def iqp_embed(features): + IQPEmbedding(features, wires=range(4), n_repeats=2) + return qml.expval(qml.PauliZ(0)) +``` + +### Custom Templates + +```python +def custom_layer(weights, wires): + """Define custom template.""" + n_wires = len(wires) + + # Rotation layer + for i, wire in enumerate(wires): + qml.RY(weights[i], wires=wire) + + # Entanglement pattern + for i in range(0, n_wires-1, 2): + qml.CNOT(wires=[wires[i], wires[i+1]]) + + for i in range(1, n_wires-1, 2): + qml.CNOT(wires=[wires[i], wires[i+1]]) + +@qml.qnode(dev) +def circuit_custom(weights, n_layers): + for i in range(n_layers): + custom_layer(weights[i], wires=range(4)) + return qml.expval(qml.PauliZ(0)) +``` + +## Transforms + +### Circuit Transformations + +```python +# Cancel adjacent inverse operations +from pennylane import transforms + +@transforms.cancel_inverses +@qml.qnode(dev) +def circuit(): + qml.Hadamard(wires=0) + qml.Hadamard(wires=0) # These cancel + qml.RX(0.5, wires=1) + return qml.expval(qml.PauliZ(0)) + +# Merge rotations +@transforms.merge_rotations +@qml.qnode(dev) +def circuit(): + qml.RX(0.1, wires=0) + qml.RX(0.2, wires=0) # These merge into single RX(0.3) + return qml.expval(qml.PauliZ(0)) + +# Commute measurements to end +@transforms.commute_controlled +@qml.qnode(dev) +def circuit(): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) +``` + +### Parameter Broadcasting + +```python +# Execute circuit with multiple parameter sets +@qml.qnode(dev) +def circuit(x): + qml.RX(x, wires=0) + return qml.expval(qml.PauliZ(0)) + +# Broadcast over parameters +params = np.array([0.1, 0.2, 0.3, 0.4]) +results = circuit(params) # Returns array of results +``` + +### Metric Tensor + +```python +# Compute quantum geometric tensor +@qml.qnode(dev) +def variational_circuit(params): + for i, param in enumerate(params): + qml.RY(param, wires=i % 4) + for i in range(3): + qml.CNOT(wires=[i, i+1]) + return qml.expval(qml.PauliZ(0)) + +params = np.array([0.1, 0.2, 0.3, 0.4], requires_grad=True) + +# Get metric tensor (useful for quantum natural gradient) +metric_tensor = qml.metric_tensor(variational_circuit)(params) +``` + +### Tape Manipulation + +```python +with qml.tape.QuantumTape() as tape: + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + qml.RX(0.5, wires=1) + qml.expval(qml.PauliZ(0)) + +# Inspect tape +print("Operations:", tape.operations) +print("Observables:", tape.observables) + +# Transform tape +expanded_tape = transforms.expand_tape(tape) +optimized_tape = transforms.cancel_inverses(tape) +``` + +### Decomposition + +```python +# Decompose operations into native gate set +@qml.qnode(dev) +def circuit(): + qml.U3(0.1, 0.2, 0.3, wires=0) # Arbitrary single-qubit gate + return qml.expval(qml.PauliZ(0)) + +# Decompose U3 into RZ, RY +decomposed = qml.transforms.decompose(circuit, gate_set={qml.RZ, qml.RY, qml.CNOT}) +``` + +## Pulse Programming + +### Pulse-Level Control + +```python +from pennylane import pulse + +# Define pulse envelope +def gaussian_pulse(t, amplitude, sigma): + return amplitude * np.exp(-(t**2) / (2 * sigma**2)) + +# Create pulse program +dev_pulse = qml.device('default.qubit', wires=2) + +@qml.qnode(dev_pulse) +def pulse_circuit(): + # Apply pulse to qubit + pulse.drive( + amplitude=lambda t: gaussian_pulse(t, 1.0, 0.5), + phase=0.0, + freq=5.0, + wires=0, + duration=2.0 + ) + + return qml.expval(qml.PauliZ(0)) +``` + +### Pulse Sequences + +```python +@qml.qnode(dev_pulse) +def pulse_sequence(): + # Sequence of pulses + duration = 1.0 + + # X pulse + pulse.drive( + amplitude=lambda t: np.sin(np.pi * t / duration), + phase=0.0, + freq=5.0, + wires=0, + duration=duration + ) + + # Y pulse + pulse.drive( + amplitude=lambda t: np.sin(np.pi * t / duration), + phase=np.pi/2, + freq=5.0, + wires=0, + duration=duration + ) + + return qml.expval(qml.PauliZ(0)) +``` + +### Optimal Control + +```python +def optimize_pulse(target_gate): + """Optimize pulse to implement target gate.""" + + def pulse_fn(t, params): + # Parameterized pulse + return params[0] * np.sin(params[1] * t + params[2]) + + @qml.qnode(dev_pulse) + def pulse_circuit(params): + pulse.drive( + amplitude=lambda t: pulse_fn(t, params), + phase=0.0, + freq=5.0, + wires=0, + duration=2.0 + ) + return qml.expval(qml.PauliZ(0)) + + # Cost: fidelity with target + def cost(params): + result_state = pulse_circuit(params) + target_state = target_gate() + return 1 - np.abs(np.vdot(result_state, target_state))**2 + + # Optimize + opt = qml.AdamOptimizer(stepsize=0.01) + params = np.random.random(3, requires_grad=True) + + for i in range(100): + params = opt.step(cost, params) + + return params +``` + +## Catalyst and JIT Compilation + +### Basic JIT Compilation + +```python +from catalyst import qjit + +dev = qml.device('lightning.qubit', wires=4) + +@qjit # Just-in-time compile +@qml.qnode(dev) +def compiled_circuit(x): + qml.RX(x, wires=0) + qml.Hadamard(wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# First call compiles, subsequent calls are fast +result = compiled_circuit(0.5) +``` + +### Compiled Control Flow + +```python +@qjit +@qml.qnode(dev) +def circuit_with_loops(n): + qml.Hadamard(wires=0) + + # Compiled for loop + @qml.for_loop(0, n, 1) + def loop_body(i): + qml.RX(0.1 * i, wires=0) + + loop_body() + + return qml.expval(qml.PauliZ(0)) + +result = circuit_with_loops(10) +``` + +### Compiled While Loops + +```python +@qjit +@qml.qnode(dev) +def circuit_while(): + qml.Hadamard(wires=0) + + # Compiled while loop + @qml.while_loop(lambda i: i < 10) + def loop_body(i): + qml.RX(0.1, wires=0) + return i + 1 + + loop_body(0) + + return qml.expval(qml.PauliZ(0)) +``` + +### Autodiff with JIT + +```python +@qjit +@qml.qnode(dev) +def circuit(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + return qml.expval(qml.PauliZ(0)) + +# Compiled gradient +grad_fn = qjit(qml.grad(circuit)) + +params = np.array([0.1, 0.2]) +gradients = grad_fn(params) +``` + +## Adaptive Circuits + +### Mid-Circuit Measurements with Feedback + +```python +dev = qml.device('default.qubit', wires=3) + +@qml.qnode(dev) +def adaptive_circuit(): + # Prepare state + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + + # Mid-circuit measurement + m0 = qml.measure(0) + + # Conditional operation based on measurement + qml.cond(m0, qml.PauliX)(wires=2) + + # Another measurement + m1 = qml.measure(1) + + # More complex conditional + qml.cond(m0 & m1, qml.Hadamard)(wires=2) + + return qml.expval(qml.PauliZ(2)) +``` + +### Dynamic Circuit Depth + +```python +@qml.qnode(dev) +def dynamic_depth_circuit(max_depth): + qml.Hadamard(wires=0) + + converged = False + depth = 0 + + while not converged and depth < max_depth: + # Apply layer + qml.RX(0.1 * depth, wires=0) + + # Check convergence via measurement + m = qml.measure(0, reset=True) + + if m == 1: + converged = True + + depth += 1 + + return qml.expval(qml.PauliZ(0)) +``` + +### Quantum Error Correction + +```python +def bit_flip_code(): + """3-qubit bit flip error correction.""" + + @qml.qnode(dev) + def circuit(): + # Encode logical qubit + qml.CNOT(wires=[0, 1]) + qml.CNOT(wires=[0, 2]) + + # Simulate error + qml.PauliX(wires=1) # Bit flip on qubit 1 + + # Syndrome measurement + qml.CNOT(wires=[0, 3]) + qml.CNOT(wires=[1, 3]) + s1 = qml.measure(3) + + qml.CNOT(wires=[1, 4]) + qml.CNOT(wires=[2, 4]) + s2 = qml.measure(4) + + # Correction + qml.cond(s1 & ~s2, qml.PauliX)(wires=0) + qml.cond(s1 & s2, qml.PauliX)(wires=1) + qml.cond(~s1 & s2, qml.PauliX)(wires=2) + + return qml.expval(qml.PauliZ(0)) + + return circuit() +``` + +## Noise Models + +### Built-in Noise Channels + +```python +dev_noisy = qml.device('default.mixed', wires=2) + +@qml.qnode(dev_noisy) +def noisy_circuit(): + qml.Hadamard(wires=0) + + # Depolarizing noise + qml.DepolarizingChannel(0.1, wires=0) + + qml.CNOT(wires=[0, 1]) + + # Amplitude damping (energy loss) + qml.AmplitudeDamping(0.05, wires=0) + + # Phase damping (dephasing) + qml.PhaseDamping(0.05, wires=1) + + # Bit flip error + qml.BitFlip(0.01, wires=0) + + # Phase flip error + qml.PhaseFlip(0.01, wires=1) + + return qml.expval(qml.PauliZ(0)) +``` + +### Custom Noise Models + +```python +def custom_noise(p): + """Custom noise channel.""" + # Kraus operators for custom noise + K0 = np.sqrt(1 - p) * np.eye(2) + K1 = np.sqrt(p/3) * np.array([[0, 1], [1, 0]]) # X + K2 = np.sqrt(p/3) * np.array([[0, -1j], [1j, 0]]) # Y + K3 = np.sqrt(p/3) * np.array([[1, 0], [0, -1]]) # Z + + return [K0, K1, K2, K3] + +@qml.qnode(dev_noisy) +def circuit_custom_noise(): + qml.Hadamard(wires=0) + + # Apply custom noise + qml.QubitChannel(custom_noise(0.1), wires=0) + + return qml.expval(qml.PauliZ(0)) +``` + +### Noise-Aware Training + +```python +def train_with_noise(circuit, params, noise_level): + """Train considering hardware noise.""" + + dev_ideal = qml.device('default.qubit', wires=4) + dev_noisy = qml.device('default.mixed', wires=4) + + @qml.qnode(dev_noisy) + def noisy_circuit(p): + circuit(p) + + # Add noise after each gate + for wire in range(4): + qml.DepolarizingChannel(noise_level, wires=wire) + + return qml.expval(qml.PauliZ(0)) + + # Optimize noisy circuit + opt = qml.AdamOptimizer(stepsize=0.01) + + for i in range(100): + params = opt.step(noisy_circuit, params) + + return params +``` + +## Resource Estimation + +### Count Operations + +```python +@qml.qnode(dev) +def circuit(params): + for i, param in enumerate(params): + qml.RY(param, wires=i % 4) + for i in range(3): + qml.CNOT(wires=[i, i+1]) + return qml.expval(qml.PauliZ(0)) + +params = np.random.random(10) + +# Get resource information +specs = qml.specs(circuit)(params) + +print(f"Total gates: {specs['num_operations']}") +print(f"Circuit depth: {specs['depth']}") +print(f"Gate types: {specs['gate_types']}") +print(f"Gate sizes: {specs['gate_sizes']}") +print(f"Trainable params: {specs['num_trainable_params']}") +``` + +### Estimate Execution Time + +```python +import time + +def estimate_runtime(circuit, params, n_runs=10): + """Estimate circuit execution time.""" + + times = [] + for _ in range(n_runs): + start = time.time() + result = circuit(params) + times.append(time.time() - start) + + mean_time = np.mean(times) + std_time = np.std(times) + + print(f"Mean execution time: {mean_time*1000:.2f} ms") + print(f"Std deviation: {std_time*1000:.2f} ms") + + return mean_time +``` + +### Resource Requirements + +```python +def estimate_resources(n_qubits, depth): + """Estimate computational resources.""" + + # Classical simulation cost + state_vector_size = 2**n_qubits * 16 # bytes (complex128) + + # Number of operations + n_operations = depth * n_qubits + + print(f"Qubits: {n_qubits}") + print(f"Circuit depth: {depth}") + print(f"State vector size: {state_vector_size / 1e9:.2f} GB") + print(f"Number of operations: {n_operations}") + + # Approximate simulation time (very rough) + gate_time = 1e-6 # seconds per gate (varies by device) + total_time = n_operations * gate_time * 2**n_qubits + + print(f"Estimated simulation time: {total_time:.4f} seconds") + + return { + 'memory': state_vector_size, + 'operations': n_operations, + 'time': total_time + } + +estimate_resources(n_qubits=20, depth=100) +``` + +## Best Practices + +1. **Use templates** - Leverage built-in templates for common patterns +2. **Apply transforms** - Optimize circuits with transforms before execution +3. **Compile with JIT** - Use Catalyst for performance-critical code +4. **Consider noise** - Include noise models for realistic hardware simulation +5. **Estimate resources** - Profile circuits before running on hardware +6. **Use adaptive circuits** - Implement mid-circuit measurements for flexibility +7. **Optimize pulses** - Fine-tune pulse parameters for hardware control +8. **Cache compilations** - Reuse compiled circuits +9. **Monitor performance** - Track execution times and resource usage +10. **Test thoroughly** - Validate on simulators before hardware deployment diff --git a/scientific-skills/pennylane/references/devices_backends.md b/scientific-skills/pennylane/references/devices_backends.md new file mode 100644 index 0000000..2ff1963 --- /dev/null +++ b/scientific-skills/pennylane/references/devices_backends.md @@ -0,0 +1,596 @@ +# Devices and Backends in PennyLane + +## Table of Contents +1. [Built-in Simulators](#built-in-simulators) +2. [Hardware Plugins](#hardware-plugins) +3. [Device Selection](#device-selection) +4. [Device Configuration](#device-configuration) +5. [Custom Devices](#custom-devices) +6. [Performance Optimization](#performance-optimization) + +## Built-in Simulators + +### default.qubit + +General-purpose state vector simulator: + +```python +import pennylane as qml + +# Basic initialization +dev = qml.device('default.qubit', wires=4) + +# With shots (sampling mode) +dev = qml.device('default.qubit', wires=4, shots=1000) + +# Specify wire labels +dev = qml.device('default.qubit', wires=['a', 'b', 'c', 'd']) +``` + +### default.mixed + +Mixed-state simulator for noisy quantum systems: + +```python +# Supports density matrix simulation +dev = qml.device('default.mixed', wires=2) + +@qml.qnode(dev) +def noisy_circuit(): + qml.Hadamard(wires=0) + + # Apply noise + qml.DepolarizingChannel(0.1, wires=0) + + qml.CNOT(wires=[0, 1]) + + # Amplitude damping + qml.AmplitudeDamping(0.05, wires=1) + + return qml.expval(qml.PauliZ(0)) +``` + +### default.qubit.torch, default.qubit.tf, default.qubit.jax + +Framework-specific simulators with better integration: + +```python +# PyTorch +dev = qml.device('default.qubit.torch', wires=4) + +# TensorFlow +dev = qml.device('default.qubit.tf', wires=4) + +# JAX +dev = qml.device('default.qubit.jax', wires=4) +``` + +### lightning.qubit + +High-performance C++ simulator: + +```python +# Faster than default.qubit +dev = qml.device('lightning.qubit', wires=20) + +# Supports larger systems efficiently +@qml.qnode(dev) +def large_circuit(): + for i in range(20): + qml.Hadamard(wires=i) + + for i in range(19): + qml.CNOT(wires=[i, i+1]) + + return qml.expval(qml.PauliZ(0)) +``` + +### default.clifford + +Efficient simulator for Clifford circuits: + +```python +# Only supports Clifford gates (H, S, CNOT, etc.) +dev = qml.device('default.clifford', wires=100) + +@qml.qnode(dev) +def clifford_circuit(): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + qml.S(wires=1) + # Cannot use RX, RY, RZ, etc. + + return qml.expval(qml.PauliZ(0)) +``` + +## Hardware Plugins + +### IBM Quantum (Qiskit) + +```bash +# Install plugin +uv pip install pennylane-qiskit +``` + +```python +import pennylane as qml + +# Use IBM simulator +dev = qml.device('qiskit.aer', wires=2) + +# Use IBM quantum hardware +dev = qml.device( + 'qiskit.ibmq', + wires=2, + backend='ibmq_manila', # Specify backend + shots=1024 +) + +# With API token +dev = qml.device( + 'qiskit.ibmq', + wires=2, + backend='ibmq_manila', + ibmqx_token='YOUR_API_TOKEN' +) + +@qml.qnode(dev) +def circuit(): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) +``` + +### Amazon Braket + +```bash +# Install plugin +uv pip install amazon-braket-pennylane-plugin +``` + +```python +# Use Braket simulators +dev = qml.device( + 'braket.local.qubit', + wires=2 +) + +# Use AWS simulators +dev = qml.device( + 'braket.aws.qubit', + device_arn='arn:aws:braket:::device/quantum-simulator/amazon/sv1', + wires=4, + s3_destination_folder=('amazon-braket-outputs', 'outputs') +) + +# Use quantum hardware (IonQ, Rigetti, etc.) +dev = qml.device( + 'braket.aws.qubit', + device_arn='arn:aws:braket:us-east-1::device/qpu/ionq/Harmony', + wires=11, + shots=1000, + s3_destination_folder=('amazon-braket-outputs', 'outputs') +) +``` + +### Google Cirq + +```bash +# Install plugin +uv pip install pennylane-cirq +``` + +```python +# Use Cirq simulator +dev = qml.device('cirq.simulator', wires=2) + +# Use Cirq with qsim (faster) +dev = qml.device('cirq.qsim', wires=20) + +# Use Google quantum hardware (if you have access) +dev = qml.device( + 'cirq.pasqal', + wires=2, + device='rainbow', + shots=1000 +) +``` + +### Rigetti Forest + +```bash +# Install plugin +uv pip install pennylane-rigetti +``` + +```python +# Use QVM (Quantum Virtual Machine) +dev = qml.device('rigetti.qvm', device='4q-qvm', shots=1000) + +# Use Rigetti QPU +dev = qml.device('rigetti.qpu', device='Aspen-M-3', shots=1000) +``` + +### Microsoft Azure Quantum + +```bash +# Install plugin +uv pip install pennylane-azure +``` + +```python +# Use Azure simulators +dev = qml.device( + 'azure.simulator', + wires=4, + workspace='your-workspace', + resource_group='your-resource-group', + subscription_id='your-subscription-id' +) + +# Use IonQ on Azure +dev = qml.device( + 'azure.ionq', + wires=11, + workspace='your-workspace', + resource_group='your-resource-group', + subscription_id='your-subscription-id', + shots=500 +) +``` + +### IonQ + +```bash +# Install plugin +uv pip install pennylane-ionq +``` + +```python +# Use IonQ hardware +dev = qml.device( + 'ionq.simulator', # or 'ionq.qpu' + wires=11, + shots=1024, + api_key='your_api_key' +) +``` + +### Xanadu Hardware (Borealis) + +```python +# Photonic quantum computer +dev = qml.device( + 'strawberryfields.remote', + backend='borealis', + shots=10000 +) +``` + +## Device Selection + +### Choosing the Right Device + +```python +def select_device(n_qubits, use_hardware=False, noise_model=None): + """Select appropriate device based on requirements.""" + + if use_hardware: + # Use real quantum hardware + if n_qubits <= 11: + return qml.device('ionq.qpu', wires=n_qubits, shots=1000) + elif n_qubits <= 127: + return qml.device('qiskit.ibmq', wires=n_qubits, shots=1024) + else: + raise ValueError(f"No hardware available for {n_qubits} qubits") + + elif noise_model: + # Use noisy simulator + return qml.device('default.mixed', wires=n_qubits) + + else: + # Use ideal simulator + if n_qubits <= 20: + return qml.device('lightning.qubit', wires=n_qubits) + else: + return qml.device('default.qubit', wires=n_qubits) + +# Usage +dev = select_device(n_qubits=10, use_hardware=False) +``` + +### Device Capabilities + +```python +# Check device capabilities +dev = qml.device('default.qubit', wires=4) + +print("Device name:", dev.name) +print("Number of wires:", dev.num_wires) +print("Supports shots:", dev.shots is not None) + +# Check supported operations +print("Supported gates:", dev.operations) + +# Check supported observables +print("Supported observables:", dev.observables) +``` + +## Device Configuration + +### Setting Shots + +```python +# Exact simulation (no shots) +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev) +def exact_circuit(): + qml.Hadamard(wires=0) + return qml.expval(qml.PauliZ(0)) + +result = exact_circuit() # Returns exact expectation + +# Sampling mode (with shots) +dev_sampled = qml.device('default.qubit', wires=2, shots=1000) + +@qml.qnode(dev_sampled) +def sampled_circuit(): + qml.Hadamard(wires=0) + return qml.expval(qml.PauliZ(0)) + +result = sampled_circuit() # Estimated from samples +``` + +### Dynamic Shots + +```python +# Change shots per execution +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev) +def circuit(): + qml.Hadamard(wires=0) + return qml.expval(qml.PauliZ(0)) + +# Different shot numbers +result_100 = circuit(shots=100) +result_1000 = circuit(shots=1000) +result_exact = circuit(shots=None) # Exact +``` + +### Analytic Mode vs Finite Shots + +```python +# Compare analytic vs sampled +dev_analytic = qml.device('default.qubit', wires=2) +dev_sampled = qml.device('default.qubit', wires=2, shots=1000) + +@qml.qnode(dev_analytic) +def circuit_analytic(x): + qml.RX(x, wires=0) + return qml.expval(qml.PauliZ(0)) + +@qml.qnode(dev_sampled) +def circuit_sampled(x): + qml.RX(x, wires=0) + return qml.expval(qml.PauliZ(0)) + +import numpy as np +x = np.pi / 4 + +print(f"Analytic: {circuit_analytic(x)}") +print(f"Sampled: {circuit_sampled(x)}") +print(f"Exact value: {np.cos(x)}") +``` + +### Seed for Reproducibility + +```python +# Set random seed +dev = qml.device('default.qubit', wires=2, shots=1000, seed=42) + +@qml.qnode(dev) +def circuit(): + qml.Hadamard(wires=0) + return qml.sample(qml.PauliZ(0)) + +# Reproducible results +samples1 = circuit() +samples2 = circuit() # Same as samples1 if seed is set +``` + +## Custom Devices + +### Creating a Custom Device + +```python +from pennylane.devices import DefaultQubit + +class CustomDevice(DefaultQubit): + """Custom quantum device with additional features.""" + + name = 'Custom device' + short_name = 'custom' + pennylane_requires = '>=0.30.0' + version = '0.1.0' + author = 'Your Name' + + def __init__(self, wires, shots=None, **kwargs): + super().__init__(wires=wires, shots=shots) + # Custom initialization + + def apply(self, operations, **kwargs): + """Apply operations with custom logic.""" + # Custom operation handling + for op in operations: + # Log or modify operations + print(f"Applying: {op.name}") + + # Call parent implementation + super().apply(operations, **kwargs) + +# Use custom device +dev = CustomDevice(wires=4) +``` + +### Plugin Development + +```python +# Define custom plugin operations +class CustomGate(qml.operation.Operation): + """Custom quantum gate.""" + + num_wires = 1 + num_params = 1 + par_domain = 'R' + + def decomposition(self): + """Decompose into standard gates.""" + theta = self.parameters[0] + wires = self.wires + + return [ + qml.RY(theta / 2, wires=wires), + qml.RZ(theta, wires=wires), + qml.RY(-theta / 2, wires=wires) + ] + +# Register with device +qml.ops.CustomGate = CustomGate +``` + +## Performance Optimization + +### Batch Execution + +```python +# Execute multiple parameter sets efficiently +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev) +def circuit(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# Batch parameters +params_batch = np.random.random((100, 2)) + +# Vectorized execution (faster) +results = [circuit(p) for p in params_batch] +``` + +### Device Caching + +```python +# Cache device for reuse +_device_cache = {} + +def get_device(n_qubits, device_type='default.qubit'): + """Get or create cached device.""" + key = (device_type, n_qubits) + + if key not in _device_cache: + _device_cache[key] = qml.device(device_type, wires=n_qubits) + + return _device_cache[key] + +# Reuse devices +dev1 = get_device(4) +dev2 = get_device(4) # Returns same device +``` + +### JIT Compilation with Catalyst + +```python +# Install Catalyst +# uv pip install pennylane-catalyst + +import pennylane as qml +from catalyst import qjit + +dev = qml.device('lightning.qubit', wires=4) + +@qjit # Just-in-time compilation +@qml.qnode(dev) +def compiled_circuit(x): + qml.RX(x, wires=0) + qml.Hadamard(wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# First call compiles, subsequent calls are fast +result = compiled_circuit(0.5) +``` + +### Parallel Execution + +```python +from multiprocessing import Pool + +def run_circuit(params): + """Run circuit with given parameters.""" + dev = qml.device('default.qubit', wires=4) + + @qml.qnode(dev) + def circuit(p): + # Circuit definition + return qml.expval(qml.PauliZ(0)) + + return circuit(params) + +# Parallel execution +param_list = [np.random.random(10) for _ in range(100)] + +with Pool(processes=4) as pool: + results = pool.map(run_circuit, param_list) +``` + +### GPU Acceleration + +```python +# Use GPU-accelerated devices if available +try: + dev = qml.device('lightning.gpu', wires=20) +except: + dev = qml.device('lightning.qubit', wires=20) + +@qml.qnode(dev) +def gpu_circuit(): + # Large circuit benefits from GPU + for i in range(20): + qml.Hadamard(wires=i) + + for i in range(19): + qml.CNOT(wires=[i, i+1]) + + return [qml.expval(qml.PauliZ(i)) for i in range(20)] +``` + +## Best Practices + +1. **Start with simulators** - Test on `default.qubit` before hardware +2. **Use lightning for speed** - Switch to `lightning.qubit` for larger circuits +3. **Match device to task** - Use `default.mixed` for noise studies +4. **Cache devices** - Reuse device objects to avoid initialization overhead +5. **Set appropriate shots** - Balance accuracy vs speed +6. **Check capabilities** - Verify device supports required operations +7. **Handle hardware errors** - Implement retries and error mitigation +8. **Monitor costs** - Track hardware usage and costs +9. **Use JIT when possible** - Compile circuits with Catalyst for speedup +10. **Test locally first** - Validate on simulators before submitting to hardware + +## Device Comparison + +| Device | Type | Max Qubits | Speed | Noise | Use Case | +|--------|------|-----------|-------|-------|----------| +| default.qubit | Simulator | ~25 | Medium | No | General purpose | +| lightning.qubit | Simulator | ~30 | Fast | No | Large circuits | +| default.mixed | Simulator | ~15 | Slow | Yes | Noise studies | +| default.clifford | Simulator | 100+ | Very fast | No | Clifford circuits | +| IBM Quantum | Hardware | 127 | Slow | Yes | Real experiments | +| IonQ | Hardware | 11 | Slow | Low | High fidelity | +| Rigetti | Hardware | 80 | Slow | Yes | Research | +| Borealis | Hardware | 216 | Slow | Yes | Photonic QC | diff --git a/scientific-skills/pennylane/references/getting_started.md b/scientific-skills/pennylane/references/getting_started.md new file mode 100644 index 0000000..f487715 --- /dev/null +++ b/scientific-skills/pennylane/references/getting_started.md @@ -0,0 +1,227 @@ +# Getting Started with PennyLane + +## What is PennyLane? + +PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. It enables training quantum computers like neural networks through automatic differentiation and seamless integration with classical machine learning frameworks. + +## Installation + +Install PennyLane using uv: + +```bash +uv pip install pennylane +``` + +For specific device plugins (IBM, Amazon Braket, Google, Rigetti, etc.): + +```bash +# IBM Qiskit +uv pip install pennylane-qiskit + +# Amazon Braket +uv pip install amazon-braket-pennylane-plugin + +# Google Cirq +uv pip install pennylane-cirq + +# Rigetti +uv pip install pennylane-rigetti +``` + +## Core Concepts + +### Quantum Nodes (QNodes) + +A QNode is a quantum function that can be evaluated on a quantum device. It combines a quantum circuit definition with a device: + +```python +import pennylane as qml + +# Define a device +dev = qml.device('default.qubit', wires=2) + +# Create a QNode +@qml.qnode(dev) +def circuit(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) +``` + +### Devices + +Devices execute quantum circuits. PennyLane supports: +- **Simulators**: `default.qubit`, `default.mixed`, `lightning.qubit` +- **Hardware**: Access through plugins (IBM, Amazon Braket, Rigetti, etc.) + +```python +# Local simulator +dev = qml.device('default.qubit', wires=4) + +# Lightning high-performance simulator +dev = qml.device('lightning.qubit', wires=10) +``` + +### Measurements + +PennyLane supports various measurement types: + +```python +@qml.qnode(dev) +def measure_circuit(): + qml.Hadamard(wires=0) + # Expectation value + return qml.expval(qml.PauliZ(0)) + +@qml.qnode(dev) +def measure_probs(): + qml.Hadamard(wires=0) + # Probability distribution + return qml.probs(wires=[0, 1]) + +@qml.qnode(dev) +def measure_samples(): + qml.Hadamard(wires=0) + # Sample measurements + return qml.sample(qml.PauliZ(0)) +``` + +## Basic Workflow + +### 1. Build a Circuit + +```python +import pennylane as qml +import numpy as np + +dev = qml.device('default.qubit', wires=3) + +@qml.qnode(dev) +def quantum_circuit(weights): + # Apply gates + qml.RX(weights[0], wires=0) + qml.RY(weights[1], wires=1) + qml.CNOT(wires=[0, 1]) + qml.RZ(weights[2], wires=2) + + # Measure + return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) +``` + +### 2. Compute Gradients + +```python +# Automatic differentiation +grad_fn = qml.grad(quantum_circuit) +weights = np.array([0.1, 0.2, 0.3]) +gradients = grad_fn(weights) +``` + +### 3. Optimize Parameters + +```python +from pennylane import numpy as np + +# Define optimizer +opt = qml.GradientDescentOptimizer(stepsize=0.1) + +# Optimization loop +weights = np.array([0.1, 0.2, 0.3], requires_grad=True) +for i in range(100): + weights = opt.step(quantum_circuit, weights) + if i % 20 == 0: + print(f"Step {i}: Cost = {quantum_circuit(weights)}") +``` + +## Device-Independent Programming + +Write circuits once, run anywhere: + +```python +# Same circuit, different backends +@qml.qnode(qml.device('default.qubit', wires=2)) +def circuit_simulator(x): + qml.RX(x, wires=0) + return qml.expval(qml.PauliZ(0)) + +# Switch to hardware (if available) +@qml.qnode(qml.device('qiskit.ibmq', wires=2)) +def circuit_hardware(x): + qml.RX(x, wires=0) + return qml.expval(qml.PauliZ(0)) +``` + +## Common Patterns + +### Parameterized Circuits + +```python +@qml.qnode(dev) +def parameterized_circuit(params, x): + # Encode data + qml.RX(x, wires=0) + + # Apply parameterized layers + for param in params: + qml.RY(param, wires=0) + qml.CNOT(wires=[0, 1]) + + return qml.expval(qml.PauliZ(0)) +``` + +### Circuit Templates + +Use built-in templates for common patterns: + +```python +from pennylane.templates import StronglyEntanglingLayers + +@qml.qnode(dev) +def template_circuit(weights): + StronglyEntanglingLayers(weights, wires=range(3)) + return qml.expval(qml.PauliZ(0)) + +# Generate random weights for template +n_layers = 2 +n_wires = 3 +shape = StronglyEntanglingLayers.shape(n_layers, n_wires) +weights = np.random.random(shape) +``` + +## Debugging and Visualization + +### Print Circuit Structure + +```python +print(qml.draw(circuit)(params)) +print(qml.draw_mpl(circuit)(params)) # Matplotlib visualization +``` + +### Inspect Operations + +```python +with qml.tape.QuantumTape() as tape: + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + +print(tape.operations) +print(tape.measurements) +``` + +## Next Steps + +For detailed information on specific topics: +- **Building circuits**: See `references/quantum_circuits.md` +- **Quantum ML**: See `references/quantum_ml.md` +- **Chemistry applications**: See `references/quantum_chemistry.md` +- **Device management**: See `references/devices_backends.md` +- **Optimization**: See `references/optimization.md` +- **Advanced features**: See `references/advanced_features.md` + +## Resources + +- Official docs: https://docs.pennylane.ai +- Codebook: https://pennylane.ai/codebook +- QML demos: https://pennylane.ai/qml/demonstrations +- Community forum: https://discuss.pennylane.ai diff --git a/scientific-skills/pennylane/references/optimization.md b/scientific-skills/pennylane/references/optimization.md new file mode 100644 index 0000000..939f94a --- /dev/null +++ b/scientific-skills/pennylane/references/optimization.md @@ -0,0 +1,671 @@ +# Optimization in PennyLane + +## Table of Contents +1. [Built-in Optimizers](#built-in-optimizers) +2. [Gradient Computation](#gradient-computation) +3. [Variational Algorithms](#variational-algorithms) +4. [QAOA](#qaoa-quantum-approximate-optimization-algorithm) +5. [Training Strategies](#training-strategies) +6. [Optimization Challenges](#optimization-challenges) + +## Built-in Optimizers + +### Gradient Descent Optimizer + +```python +import pennylane as qml +from pennylane import numpy as np + +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev) +def cost_function(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) + +# Initialize optimizer +opt = qml.GradientDescentOptimizer(stepsize=0.1) + +# Initialize parameters +params = np.array([0.1, 0.2], requires_grad=True) + +# Training loop +for i in range(100): + params = opt.step(cost_function, params) + + if i % 10 == 0: + print(f"Step {i}: Cost = {cost_function(params):.6f}") +``` + +### Adam Optimizer + +```python +# Adaptive learning rate optimizer +opt = qml.AdamOptimizer(stepsize=0.01, beta1=0.9, beta2=0.999) + +params = np.random.random(10, requires_grad=True) + +for i in range(100): + params, cost = opt.step_and_cost(cost_function, params) + + if i % 10 == 0: + print(f"Step {i}: Cost = {cost:.6f}") +``` + +### Momentum Optimizer + +```python +# Gradient descent with momentum +opt = qml.MomentumOptimizer(stepsize=0.01, momentum=0.9) + +params = np.random.random(5, requires_grad=True) + +for i in range(100): + params = opt.step(cost_function, params) +``` + +### AdaGrad Optimizer + +```python +# Adaptive gradient algorithm +opt = qml.AdagradOptimizer(stepsize=0.1) + +params = np.random.random(8, requires_grad=True) + +for i in range(100): + params = opt.step(cost_function, params) +``` + +### RMSProp Optimizer + +```python +# Root mean square propagation +opt = qml.RMSPropOptimizer(stepsize=0.01, decay=0.9, eps=1e-8) + +params = np.random.random(6, requires_grad=True) + +for i in range(100): + params = opt.step(cost_function, params) +``` + +### Nesterov Momentum Optimizer + +```python +# Nesterov accelerated gradient +opt = qml.NesterovMomentumOptimizer(stepsize=0.01, momentum=0.9) + +params = np.random.random(4, requires_grad=True) + +for i in range(100): + params = opt.step(cost_function, params) +``` + +## Gradient Computation + +### Automatic Differentiation + +```python +# Backpropagation (for simulators) +@qml.qnode(dev, diff_method='backprop') +def circuit_backprop(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + return qml.expval(qml.PauliZ(0)) + +# Compute gradient +grad_fn = qml.grad(circuit_backprop) +params = np.array([0.1, 0.2], requires_grad=True) +gradients = grad_fn(params) +``` + +### Parameter-Shift Rule + +```python +# Hardware-compatible gradient method +@qml.qnode(dev, diff_method='parameter-shift') +def circuit_param_shift(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# Works on quantum hardware +grad_fn = qml.grad(circuit_param_shift) +gradients = grad_fn(params) +``` + +### Finite Differences + +```python +# Numerical gradient approximation +@qml.qnode(dev, diff_method='finite-diff') +def circuit_finite_diff(params): + qml.RX(params[0], wires=0) + return qml.expval(qml.PauliZ(0)) + +grad_fn = qml.grad(circuit_finite_diff) +gradients = grad_fn(params) +``` + +### Adjoint Method + +```python +# Efficient gradient for state vector simulators +@qml.qnode(dev, diff_method='adjoint') +def circuit_adjoint(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + return qml.expval(qml.PauliZ(0)) + +grad_fn = qml.grad(circuit_adjoint) +gradients = grad_fn(params) +``` + +### Custom Gradients + +```python +@qml.qnode(dev, diff_method='parameter-shift') +def circuit(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + return qml.expval(qml.PauliZ(0)) + +# Compute Hessian +hessian_fn = qml.jacobian(qml.grad(circuit)) +hessian = hessian_fn(params) +``` + +### Stochastic Parameter-Shift + +```python +# For circuits with many parameters +@qml.qnode(dev, diff_method='spsa') # Simultaneous Perturbation Stochastic Approximation +def large_circuit(params): + for i, param in enumerate(params): + qml.RY(param, wires=i % 4) + return qml.expval(qml.PauliZ(0)) + +# Efficient for high-dimensional parameter spaces +opt = qml.SPSAOptimizer(maxiter=100) +params = np.random.random(100, requires_grad=True) +params = opt.minimize(large_circuit, params) +``` + +## Variational Algorithms + +### Variational Quantum Eigensolver (VQE) + +```python +# Ground state energy calculation +def vqe(hamiltonian, ansatz, n_qubits): + """VQE implementation.""" + + dev = qml.device('default.qubit', wires=n_qubits) + + @qml.qnode(dev) + def cost_fn(params): + ansatz(params, wires=range(n_qubits)) + return qml.expval(hamiltonian) + + # Initialize parameters + n_params = 10 # Depends on ansatz + params = np.random.random(n_params, requires_grad=True) + + # Optimize + opt = qml.AdamOptimizer(stepsize=0.1) + + energies = [] + for i in range(100): + params, energy = opt.step_and_cost(cost_fn, params) + energies.append(energy) + + if i % 10 == 0: + print(f"Step {i}: Energy = {energy:.6f}") + + return params, energy, energies + +# Example usage +from pennylane import qchem + +symbols = ['H', 'H'] +coords = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.74]) +H, n_qubits = qchem.molecular_hamiltonian(symbols, coords) + +def simple_ansatz(params, wires): + qml.BasisState(qchem.hf_state(2, n_qubits), wires=wires) + for i, param in enumerate(params): + qml.RY(param, wires=i % len(wires)) + for i in range(len(wires)-1): + qml.CNOT(wires=[i, i+1]) + +params, energy, history = vqe(H, simple_ansatz, n_qubits) +``` + +### Quantum Natural Gradient + +```python +# More efficient optimization for variational circuits +@qml.qnode(dev) +def circuit(params): + for i, param in enumerate(params): + qml.RY(param, wires=i) + return qml.expval(qml.PauliZ(0)) + +# Use quantum natural gradient +opt = qml.QNGOptimizer(stepsize=0.01) +params = np.random.random(4, requires_grad=True) + +for i in range(100): + params, cost = opt.step_and_cost(circuit, params) +``` + +### Rotosolve + +```python +# Analytical parameter update +opt = qml.RotosolveOptimizer() + +@qml.qnode(dev) +def cost_fn(params): + qml.RX(params[0], wires=0) + qml.RY(params[1], wires=1) + return qml.expval(qml.PauliZ(0)) + +params = np.array([0.1, 0.2], requires_grad=True) + +for i in range(20): # Converges quickly + params = opt.step(cost_fn, params) +``` + +### Quantum Analytic Descent + +```python +# Hybrid quantum-classical optimization +opt = qml.QNSPSAOptimizer(stepsize=0.01) + +params = np.random.random(10, requires_grad=True) + +for i in range(100): + params = opt.step(cost_function, params) +``` + +## QAOA (Quantum Approximate Optimization Algorithm) + +### Basic QAOA + +```python +from pennylane import qaoa + +# Define problem: MaxCut on a graph +edges = [(0, 1), (1, 2), (2, 0)] +graph = [(edge[0], edge[1], 1.0) for edge in edges] + +# Cost Hamiltonian +cost_h = qaoa.maxcut(graph) + +# Mixer Hamiltonian +mixer_h = qaoa.x_mixer(range(3)) + +# QAOA circuit +def qaoa_layer(gamma, alpha): + """Single QAOA layer.""" + qaoa.cost_layer(gamma, cost_h) + qaoa.mixer_layer(alpha, mixer_h) + +@qml.qnode(dev) +def qaoa_circuit(params, depth): + """Full QAOA circuit.""" + # Initialize in superposition + for wire in range(3): + qml.Hadamard(wires=wire) + + # Apply QAOA layers + for i in range(depth): + gamma = params[i] + alpha = params[depth + i] + qaoa_layer(gamma, alpha) + + # Measure in computational basis + return qml.expval(cost_h) + +# Optimize +depth = 3 +params = np.random.uniform(0, 2*np.pi, 2*depth, requires_grad=True) + +opt = qml.AdamOptimizer(stepsize=0.1) + +for i in range(100): + params = opt.step(lambda p: -qaoa_circuit(p, depth), params) # Minimize negative = maximize + + if i % 10 == 0: + print(f"Step {i}: Cost = {-qaoa_circuit(params, depth):.4f}") +``` + +### QAOA for MaxCut + +```python +import networkx as nx + +# Create graph +G = nx.cycle_graph(4) + +# Generate cost Hamiltonian +cost_h, mixer_h = qaoa.maxcut(G, constrained=False) + +n_wires = len(G.nodes) +dev = qml.device('default.qubit', wires=n_wires) + +def qaoa_maxcut(params, depth): + """QAOA for MaxCut problem.""" + + @qml.qnode(dev) + def circuit(gammas, betas): + # Initialize + for wire in range(n_wires): + qml.Hadamard(wires=wire) + + # QAOA layers + for gamma, beta in zip(gammas, betas): + # Cost layer + for edge in G.edges: + wire1, wire2 = edge + qml.CNOT(wires=[wire1, wire2]) + qml.RZ(gamma, wires=wire2) + qml.CNOT(wires=[wire1, wire2]) + + # Mixer layer + for wire in range(n_wires): + qml.RX(2 * beta, wires=wire) + + return qml.expval(cost_h) + + gammas = params[:depth] + betas = params[depth:] + return circuit(gammas, betas) + +# Optimize +depth = 3 +params = np.random.uniform(0, 2*np.pi, 2*depth, requires_grad=True) + +opt = qml.AdamOptimizer(0.1) +for i in range(100): + params = opt.step(lambda p: -qaoa_maxcut(p, depth), params) +``` + +### QAOA for QUBO + +```python +def qaoa_qubo(Q, depth): + """QAOA for Quadratic Unconstrained Binary Optimization.""" + + n = len(Q) + dev = qml.device('default.qubit', wires=n) + + # Build cost Hamiltonian from QUBO matrix + coeffs = [] + obs = [] + + for i in range(n): + for j in range(i, n): + if Q[i][j] != 0: + if i == j: + coeffs.append(-Q[i][j] / 2) + obs.append(qml.PauliZ(i)) + else: + coeffs.append(-Q[i][j] / 4) + obs.append(qml.PauliZ(i) @ qml.PauliZ(j)) + + cost_h = qml.Hamiltonian(coeffs, obs) + + @qml.qnode(dev) + def circuit(params): + # Initialize + for wire in range(n): + qml.Hadamard(wires=wire) + + # QAOA layers + for i in range(depth): + gamma = params[i] + beta = params[depth + i] + + # Cost layer + for coeff, op in zip(coeffs, obs): + qml.exp(op, -1j * gamma * coeff) + + # Mixer layer + for wire in range(n): + qml.RX(2 * beta, wires=wire) + + return qml.expval(cost_h) + + return circuit + +# Example QUBO +Q = np.array([[1, -2], [-2, 1]]) +circuit = qaoa_qubo(Q, depth=2) + +params = np.random.random(4, requires_grad=True) +opt = qml.AdamOptimizer(0.1) + +for i in range(100): + params = opt.step(circuit, params) +``` + +## Training Strategies + +### Learning Rate Scheduling + +```python +def train_with_schedule(circuit, initial_params, n_epochs): + """Train with learning rate decay.""" + + params = initial_params + base_lr = 0.1 + decay_rate = 0.95 + decay_steps = 10 + + for epoch in range(n_epochs): + # Update learning rate + lr = base_lr * (decay_rate ** (epoch // decay_steps)) + opt = qml.GradientDescentOptimizer(stepsize=lr) + + # Training step + params = opt.step(circuit, params) + + if epoch % 10 == 0: + print(f"Epoch {epoch}: LR = {lr:.4f}, Cost = {circuit(params):.4f}") + + return params +``` + +### Mini-Batch Training + +```python +def minibatch_train(circuit, X, y, batch_size=32, n_epochs=100): + """Mini-batch training for quantum circuits.""" + + params = np.random.random(10, requires_grad=True) + opt = qml.AdamOptimizer(stepsize=0.01) + + n_samples = len(X) + + for epoch in range(n_epochs): + # Shuffle data + indices = np.random.permutation(n_samples) + X_shuffled = X[indices] + y_shuffled = y[indices] + + # Mini-batch updates + for i in range(0, n_samples, batch_size): + X_batch = X_shuffled[i:i+batch_size] + y_batch = y_shuffled[i:i+batch_size] + + # Compute batch cost + def batch_cost(p): + predictions = np.array([circuit(x, p) for x in X_batch]) + return np.mean((predictions - y_batch) ** 2) + + params = opt.step(batch_cost, params) + + if epoch % 10 == 0: + loss = batch_cost(params) + print(f"Epoch {epoch}: Loss = {loss:.4f}") + + return params +``` + +### Early Stopping + +```python +def train_with_early_stopping(circuit, params, X_train, X_val, patience=10): + """Train with early stopping based on validation loss.""" + + opt = qml.AdamOptimizer(stepsize=0.01) + + best_val_loss = float('inf') + patience_counter = 0 + best_params = params.copy() + + for epoch in range(1000): + # Training step + params = opt.step(lambda p: cost_fn(p, X_train), params) + + # Validation + val_loss = cost_fn(params, X_val) + + if val_loss < best_val_loss: + best_val_loss = val_loss + best_params = params.copy() + patience_counter = 0 + else: + patience_counter += 1 + + if patience_counter >= patience: + print(f"Early stopping at epoch {epoch}") + break + + return best_params +``` + +### Gradient Clipping + +```python +def train_with_gradient_clipping(circuit, params, max_norm=1.0): + """Train with gradient clipping to prevent exploding gradients.""" + + opt = qml.GradientDescentOptimizer(stepsize=0.1) + + for i in range(100): + # Compute gradients + grad_fn = qml.grad(circuit) + grads = grad_fn(params) + + # Clip gradients + grad_norm = np.linalg.norm(grads) + if grad_norm > max_norm: + grads = grads * (max_norm / grad_norm) + + # Manual update with clipped gradients + params = params - opt.stepsize * grads + + if i % 10 == 0: + print(f"Step {i}: Grad norm = {grad_norm:.4f}") + + return params +``` + +## Optimization Challenges + +### Barren Plateaus + +```python +def detect_barren_plateau(circuit, params, n_samples=100): + """Detect barren plateau by measuring gradient variance.""" + + grad_fn = qml.grad(circuit) + grad_variances = [] + + for _ in range(n_samples): + # Random initialization + random_params = np.random.uniform(-np.pi, np.pi, len(params)) + + # Compute gradient + grads = grad_fn(random_params) + grad_variances.append(np.var(grads)) + + mean_var = np.mean(grad_variances) + + print(f"Mean gradient variance: {mean_var:.6f}") + + if mean_var < 1e-6: + print("Warning: Barren plateau detected!") + + return mean_var +``` + +### Parameter Initialization + +```python +def initialize_params_smart(n_params, strategy='small_random'): + """Smart parameter initialization strategies.""" + + if strategy == 'small_random': + # Small random values + return np.random.uniform(-0.1, 0.1, n_params, requires_grad=True) + + elif strategy == 'xavier': + # Xavier initialization + return np.random.normal(0, 1/np.sqrt(n_params), n_params, requires_grad=True) + + elif strategy == 'identity': + # Start near identity (zeros for rotations) + return np.zeros(n_params, requires_grad=True) + + elif strategy == 'layerwise': + # Layer-dependent initialization + return np.array([0.1 / (i+1) for i in range(n_params)], requires_grad=True) +``` + +### Local Minima Escape + +```python +def train_with_restarts(circuit, n_restarts=5): + """Multiple random restarts to escape local minima.""" + + best_cost = float('inf') + best_params = None + + for restart in range(n_restarts): + # Random initialization + params = np.random.uniform(-np.pi, np.pi, 10, requires_grad=True) + + # Optimize + opt = qml.AdamOptimizer(stepsize=0.1) + for i in range(100): + params = opt.step(circuit, params) + + # Check if better + cost = circuit(params) + if cost < best_cost: + best_cost = cost + best_params = params + + print(f"Restart {restart}: Cost = {cost:.6f}") + + return best_params, best_cost +``` + +## Best Practices + +1. **Choose appropriate optimizer** - Adam for general use, QNG for variational circuits +2. **Use parameter-shift on hardware** - Backprop only works on simulators +3. **Initialize carefully** - Avoid barren plateaus with smart initialization +4. **Monitor gradients** - Check for vanishing/exploding gradients +5. **Use learning rate schedules** - Decay learning rate over time +6. **Try multiple restarts** - Escape local minima +7. **Validate on test set** - Prevent overfitting +8. **Profile optimization** - Identify bottlenecks +9. **Clip gradients** - Prevent instability +10. **Start shallow** - Use fewer layers initially, then grow diff --git a/scientific-skills/pennylane/references/quantum_chemistry.md b/scientific-skills/pennylane/references/quantum_chemistry.md new file mode 100644 index 0000000..062e0d4 --- /dev/null +++ b/scientific-skills/pennylane/references/quantum_chemistry.md @@ -0,0 +1,567 @@ +# Quantum Chemistry with PennyLane + +## Table of Contents +1. [Molecular Hamiltonians](#molecular-hamiltonians) +2. [Variational Quantum Eigensolver (VQE)](#variational-quantum-eigensolver-vqe) +3. [Molecular Structure](#molecular-structure) +4. [Basis Sets and Mapping](#basis-sets-and-mapping) +5. [Excited States](#excited-states) +6. [Quantum Chemistry Workflows](#quantum-chemistry-workflows) + +## Molecular Hamiltonians + +### Building Molecular Hamiltonians + +```python +import pennylane as qml +from pennylane import qchem +import numpy as np + +# Define molecule +symbols = ['H', 'H'] +coordinates = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.74]) # Angstroms + +# Generate Hamiltonian +hamiltonian, n_qubits = qchem.molecular_hamiltonian( + symbols, + coordinates, + charge=0, + mult=1, # Spin multiplicity + basis='sto-3g', + method='dhf' # Dirac-Hartree-Fock +) + +print(f"Hamiltonian: {hamiltonian}") +print(f"Number of qubits needed: {n_qubits}") +``` + +### Jordan-Wigner Transformation + +```python +# Hamiltonian is automatically in qubit form via Jordan-Wigner +# Manual transformation: +from pennylane import fermi + +# Fermionic operators +a_0 = fermi.FermiC(0) # Creation operator +a_1 = fermi.FermiA(1) # Annihilation operator + +# Convert to qubits +qubit_op = qml.qchem.jordan_wigner(a_0 * a_1) +``` + +### Bravyi-Kitaev Transformation + +```python +# Alternative mapping (more efficient for some systems) +from pennylane.qchem import bravyi_kitaev + +# Build Hamiltonian with Bravyi-Kitaev +hamiltonian, n_qubits = qchem.molecular_hamiltonian( + symbols, + coordinates, + mapping='bravyi_kitaev' +) +``` + +### Custom Hamiltonians + +```python +# Build Hamiltonian from coefficients and operators +coeffs = [0.2, -0.8, 0.5] +obs = [ + qml.PauliZ(0), + qml.PauliZ(0) @ qml.PauliZ(1), + qml.PauliX(0) @ qml.PauliX(1) +] + +H = qml.Hamiltonian(coeffs, obs) + +# Or use simplified syntax +H = 0.2 * qml.PauliZ(0) - 0.8 * qml.PauliZ(0) @ qml.PauliZ(1) + 0.5 * qml.PauliX(0) @ qml.PauliX(1) +``` + +## Variational Quantum Eigensolver (VQE) + +### Basic VQE Implementation + +```python +from pennylane import numpy as np + +# Define device +dev = qml.device('default.qubit', wires=n_qubits) + +# Hartree-Fock state preparation +hf_state = qchem.hf_state(electrons=2, orbitals=n_qubits) + +def ansatz(params, wires): + """Variational ansatz.""" + qml.BasisState(hf_state, wires=wires) + + for i in range(len(wires)): + qml.RY(params[i], wires=i) + + for i in range(len(wires)-1): + qml.CNOT(wires=[i, i+1]) + +@qml.qnode(dev) +def vqe_circuit(params): + ansatz(params, wires=range(n_qubits)) + return qml.expval(hamiltonian) + +# Optimize +opt = qml.GradientDescentOptimizer(stepsize=0.4) +params = np.random.normal(0, np.pi, n_qubits, requires_grad=True) + +for n in range(100): + params, energy = opt.step_and_cost(vqe_circuit, params) + + if n % 20 == 0: + print(f"Step {n}: Energy = {energy:.8f} Ha") + +print(f"Final ground state energy: {energy:.8f} Ha") +``` + +### UCCSD Ansatz + +```python +from pennylane.qchem import UCCSD + +# Singles and doubles excitations +singles, doubles = qchem.excitations(electrons=2, orbitals=n_qubits) + +@qml.qnode(dev) +def uccsd_circuit(params): + # Hartree-Fock reference + qml.BasisState(hf_state, wires=range(n_qubits)) + + # UCCSD ansatz + UCCSD(params, wires=range(n_qubits), s_wires=singles, d_wires=doubles) + + return qml.expval(hamiltonian) + +# Initialize parameters +n_params = len(singles) + len(doubles) +params = np.zeros(n_params, requires_grad=True) + +# Optimize +opt = qml.AdamOptimizer(stepsize=0.1) +for n in range(100): + params, energy = opt.step_and_cost(uccsd_circuit, params) +``` + +### Adaptive VQE + +```python +def adaptive_vqe(hamiltonian, n_qubits, max_gates=10): + """Adaptive VQE: Grow ansatz iteratively.""" + dev = qml.device('default.qubit', wires=n_qubits) + + # Start with HF state + operations = [] + params = [] + + hf_state = qchem.hf_state(electrons=2, orbitals=n_qubits) + + @qml.qnode(dev) + def circuit(p): + qml.BasisState(hf_state, wires=range(n_qubits)) + + for op, param in zip(operations, p): + op(param) + + return qml.expval(hamiltonian) + + # Iteratively add gates + for _ in range(max_gates): + # Find best gate to add + best_op = None + best_improvement = 0 + + for candidate_op in generate_candidates(): + # Test adding this operation + test_ops = operations + [candidate_op] + test_params = params + [0.0] + + improvement = evaluate_improvement(test_ops, test_params) + + if improvement > best_improvement: + best_improvement = improvement + best_op = candidate_op + + if best_improvement < threshold: + break + + operations.append(best_op) + params.append(0.0) + + # Optimize current ansatz + opt = qml.AdamOptimizer(stepsize=0.1) + for _ in range(50): + params = opt.step(circuit, params) + + return circuit, params +``` + +## Molecular Structure + +### Defining Molecules + +```python +# Simple diatomic +h2_symbols = ['H', 'H'] +h2_coords = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.74]) + +# Water molecule +h2o_symbols = ['O', 'H', 'H'] +h2o_coords = np.array([ + 0.0, 0.0, 0.0, # O + 0.757, 0.586, 0.0, # H + -0.757, 0.586, 0.0 # H +]) + +# From XYZ format +molecule = qchem.read_structure('molecule.xyz') +symbols, coords = molecule +``` + +### Geometry Optimization + +```python +def optimize_geometry(symbols, initial_coords, basis='sto-3g'): + """Optimize molecular geometry.""" + + def energy_surface(coords): + H, n_qubits = qchem.molecular_hamiltonian( + symbols, coords, basis=basis + ) + + # Run VQE to get energy + energy = run_vqe(H, n_qubits) + return energy + + # Classical optimization of nuclear coordinates + from scipy.optimize import minimize + + result = minimize( + energy_surface, + initial_coords, + method='BFGS', + options={'gtol': 1e-5} + ) + + return result.x, result.fun + +optimized_coords, min_energy = optimize_geometry(h2_symbols, h2_coords) +print(f"Optimized geometry: {optimized_coords}") +print(f"Energy: {min_energy} Ha") +``` + +### Bond Dissociation Curves + +```python +def dissociation_curve(symbols, axis=2, distances=None): + """Calculate potential energy surface.""" + + if distances is None: + distances = np.linspace(0.5, 3.0, 20) + + energies = [] + + for d in distances: + coords = np.zeros(6) + coords[axis] = d # Set bond length + + H, n_qubits = qchem.molecular_hamiltonian( + symbols, coords, basis='sto-3g' + ) + + energy = run_vqe(H, n_qubits) + energies.append(energy) + + print(f"Distance: {d:.2f} Å, Energy: {energy:.6f} Ha") + + return distances, energies + +# H2 dissociation +distances, energies = dissociation_curve(['H', 'H']) + +import matplotlib.pyplot as plt +plt.plot(distances, energies) +plt.xlabel('Bond length (Å)') +plt.ylabel('Energy (Ha)') +plt.title('H2 Dissociation Curve') +plt.show() +``` + +## Basis Sets and Mapping + +### Basis Set Selection + +```python +# Minimal basis (fastest, least accurate) +H_sto3g, n_qubits = qchem.molecular_hamiltonian( + symbols, coords, basis='sto-3g' +) + +# Double-zeta basis +H_631g, n_qubits = qchem.molecular_hamiltonian( + symbols, coords, basis='6-31g' +) + +# Large basis (slower, more accurate) +H_ccpvdz, n_qubits = qchem.molecular_hamiltonian( + symbols, coords, basis='cc-pvdz' +) +``` + +### Active Space Selection + +```python +# Select active orbitals +active_electrons = 2 +active_orbitals = 2 + +H_active, n_qubits = qchem.molecular_hamiltonian( + symbols, + coords, + active_electrons=active_electrons, + active_orbitals=active_orbitals +) + +print(f"Full system: {len(symbols)} electrons") +print(f"Active space: {active_electrons} electrons in {active_orbitals} orbitals") +print(f"Qubits needed: {n_qubits}") +``` + +### Fermion-to-Qubit Mappings + +```python +# Jordan-Wigner (default) +H_jw, n_q_jw = qchem.molecular_hamiltonian( + symbols, coords, mapping='jordan_wigner' +) + +# Bravyi-Kitaev +H_bk, n_q_bk = qchem.molecular_hamiltonian( + symbols, coords, mapping='bravyi_kitaev' +) + +# Parity +H_parity, n_q_parity = qchem.molecular_hamiltonian( + symbols, coords, mapping='parity' +) + +print(f"Jordan-Wigner terms: {len(H_jw.ops)}") +print(f"Bravyi-Kitaev terms: {len(H_bk.ops)}") +``` + +## Excited States + +### Quantum Subspace Expansion + +```python +def quantum_subspace_expansion(hamiltonian, ground_state_params, excitations): + """Calculate excited states via subspace expansion.""" + + @qml.qnode(dev) + def ground_state(): + ansatz(ground_state_params, wires=range(n_qubits)) + return qml.state() + + # Get ground state + psi_0 = ground_state() + + # Generate excited state basis + basis = [psi_0] + + for exc in excitations: + @qml.qnode(dev) + def excited_state(): + ansatz(ground_state_params, wires=range(n_qubits)) + # Apply excitation + apply_excitation(exc) + return qml.state() + + psi_exc = excited_state() + basis.append(psi_exc) + + # Build Hamiltonian matrix in subspace + n_basis = len(basis) + H_matrix = np.zeros((n_basis, n_basis)) + + for i in range(n_basis): + for j in range(n_basis): + H_matrix[i, j] = np.vdot(basis[i], hamiltonian @ basis[j]) + + # Diagonalize + eigenvalues, eigenvectors = np.linalg.eigh(H_matrix) + + return eigenvalues, eigenvectors +``` + +### SSVQE (Subspace-Search VQE) + +```python +def ssvqe(hamiltonian, n_states=3): + """Calculate multiple states simultaneously.""" + + def cost_function(params): + states = [] + + for i in range(n_states): + @qml.qnode(dev) + def state_i(): + ansatz(params[i], wires=range(n_qubits)) + return qml.state() + + states.append(state_i()) + + # Energy expectation + energies = [np.vdot(s, hamiltonian @ s) for s in states] + + # Orthogonality penalty + penalty = 0 + for i in range(n_states): + for j in range(i+1, n_states): + overlap = np.abs(np.vdot(states[i], states[j])) + penalty += overlap ** 2 + + return sum(energies) + 1000 * penalty + + # Initialize parameters for all states + params = [np.random.random(n_params) for _ in range(n_states)] + + opt = qml.AdamOptimizer(stepsize=0.01) + for _ in range(100): + params = opt.step(cost_function, params) + + return params +``` + +## Quantum Chemistry Workflows + +### Full VQE Workflow + +```python +def full_chemistry_workflow(symbols, coords, basis='sto-3g'): + """Complete quantum chemistry calculation.""" + + print("1. Building molecular Hamiltonian...") + H, n_qubits = qchem.molecular_hamiltonian( + symbols, coords, basis=basis + ) + + print(f" Molecule: {' '.join(symbols)}") + print(f" Qubits: {n_qubits}") + print(f" Hamiltonian terms: {len(H.ops)}") + + print("\n2. Preparing Hartree-Fock state...") + n_electrons = sum(qchem.atomic_numbers[s] for s in symbols) + hf_state = qchem.hf_state(n_electrons, n_qubits) + + print("\n3. Running VQE...") + energy, params = run_vqe(H, n_qubits, hf_state) + + print(f"\n4. Results:") + print(f" Ground state energy: {energy:.8f} Ha") + + print("\n5. Computing properties...") + dipole = compute_dipole_moment(symbols, coords, params) + print(f" Dipole moment: {dipole:.4f} D") + + return { + 'energy': energy, + 'params': params, + 'dipole': dipole + } + +results = full_chemistry_workflow(['H', 'H'], h2_coords) +``` + +### Molecular Property Calculation + +```python +def compute_molecular_properties(symbols, coords, vqe_params): + """Calculate molecular properties from VQE solution.""" + + # Energy + H, n_qubits = qchem.molecular_hamiltonian(symbols, coords) + energy = vqe_circuit(vqe_params) + + # Dipole moment + dipole_obs = qchem.dipole_moment(symbols, coords) + + @qml.qnode(dev) + def dipole_circuit(axis): + ansatz(vqe_params, wires=range(n_qubits)) + return qml.expval(dipole_obs[axis]) + + dipole = [dipole_circuit(i) for i in range(3)] + dipole_magnitude = np.linalg.norm(dipole) + + # Particle number (sanity check) + @qml.qnode(dev) + def particle_number(): + ansatz(vqe_params, wires=range(n_qubits)) + N_op = qchem.particle_number(n_qubits) + return qml.expval(N_op) + + n_particles = particle_number() + + return { + 'energy': energy, + 'dipole_moment': dipole_magnitude, + 'dipole_vector': dipole, + 'particle_number': n_particles + } +``` + +### Reaction Energy Calculation + +```python +def reaction_energy(reactants, products): + """Calculate energy of chemical reaction.""" + + # Calculate energies of reactants + E_reactants = 0 + for molecule in reactants: + symbols, coords = molecule + H, n_qubits = qchem.molecular_hamiltonian(symbols, coords) + E_reactants += run_vqe(H, n_qubits) + + # Calculate energies of products + E_products = 0 + for molecule in products: + symbols, coords = molecule + H, n_qubits = qchem.molecular_hamiltonian(symbols, coords) + E_products += run_vqe(H, n_qubits) + + # Reaction energy + delta_E = E_products - E_reactants + + print(f"Reactant energy: {E_reactants:.6f} Ha") + print(f"Product energy: {E_products:.6f} Ha") + print(f"Reaction energy: {delta_E:.6f} Ha ({delta_E * 627.5:.2f} kcal/mol)") + + return delta_E + +# Example: H2 dissociation +reactants = [((['H', 'H'], h2_coords_bonded))] +products = [((['H'], [0, 0, 0]), (['H'], [10, 0, 0]))] # Separated atoms + +delta_E = reaction_energy(reactants, products) +``` + +## Best Practices + +1. **Start with small basis sets** - Use STO-3G for testing, upgrade for production +2. **Use active space** - Reduce qubits by selecting relevant orbitals +3. **Choose appropriate mapping** - Bravyi-Kitaev often reduces circuit depth +4. **Initialize with HF** - Start VQE from Hartree-Fock state +5. **Validate results** - Compare with classical methods (FCI, CCSD) +6. **Consider symmetries** - Exploit molecular symmetries to reduce complexity +7. **Use UCCSD for accuracy** - UCCSD ansatz is chemically motivated +8. **Monitor convergence** - Check gradient norms and energy variance +9. **Account for correlation** - Ensure ansatz captures electron correlation +10. **Benchmark thoroughly** - Test on known systems before novel molecules diff --git a/scientific-skills/pennylane/references/quantum_circuits.md b/scientific-skills/pennylane/references/quantum_circuits.md new file mode 100644 index 0000000..ef84720 --- /dev/null +++ b/scientific-skills/pennylane/references/quantum_circuits.md @@ -0,0 +1,437 @@ +# Quantum Circuits in PennyLane + +## Table of Contents +1. [Basic Gates and Operations](#basic-gates-and-operations) +2. [Multi-Qubit Gates](#multi-qubit-gates) +3. [Controlled Operations](#controlled-operations) +4. [Measurements](#measurements) +5. [Circuit Construction Patterns](#circuit-construction-patterns) +6. [Dynamic Circuits](#dynamic-circuits) +7. [Circuit Inspection](#circuit-inspection) + +## Basic Gates and Operations + +### Single-Qubit Gates + +```python +import pennylane as qml + +# Pauli gates +qml.PauliX(wires=0) # X gate (bit flip) +qml.PauliY(wires=0) # Y gate +qml.PauliZ(wires=0) # Z gate (phase flip) + +# Hadamard gate (superposition) +qml.Hadamard(wires=0) + +# Phase gates +qml.S(wires=0) # S gate (π/2 phase) +qml.T(wires=0) # T gate (π/4 phase) +qml.PhaseShift(phi, wires=0) # Arbitrary phase + +# Rotation gates (parameterized) +qml.RX(theta, wires=0) # Rotation around X-axis +qml.RY(theta, wires=0) # Rotation around Y-axis +qml.RZ(theta, wires=0) # Rotation around Z-axis + +# General single-qubit rotation +qml.Rot(phi, theta, omega, wires=0) + +# Universal gate (any single-qubit unitary) +qml.U3(theta, phi, delta, wires=0) +``` + +### Basis State Preparation + +```python +# Computational basis state +qml.BasisState([1, 0, 1], wires=[0, 1, 2]) # |101⟩ + +# Amplitude encoding +amplitudes = [0.5, 0.5, 0.5, 0.5] # Must be normalized +qml.MottonenStatePreparation(amplitudes, wires=[0, 1]) +``` + +## Multi-Qubit Gates + +### Two-Qubit Gates + +```python +# CNOT (Controlled-NOT) +qml.CNOT(wires=[0, 1]) # control=0, target=1 + +# CZ (Controlled-Z) +qml.CZ(wires=[0, 1]) + +# SWAP gate +qml.SWAP(wires=[0, 1]) + +# Controlled rotations +qml.CRX(theta, wires=[0, 1]) +qml.CRY(theta, wires=[0, 1]) +qml.CRZ(theta, wires=[0, 1]) + +# Ising coupling gates +qml.IsingXX(phi, wires=[0, 1]) +qml.IsingYY(phi, wires=[0, 1]) +qml.IsingZZ(phi, wires=[0, 1]) +``` + +### Multi-Qubit Gates + +```python +# Toffoli gate (CCNOT) +qml.Toffoli(wires=[0, 1, 2]) # control=0,1, target=2 + +# Multi-controlled X +qml.MultiControlledX(control_wires=[0, 1, 2], wires=3) + +# Multi-qubit Pauli rotations +qml.MultiRZ(theta, wires=[0, 1, 2]) +``` + +## Controlled Operations + +### General Controlled Operations + +```python +# Apply controlled version of any operation +qml.ctrl(qml.RX(0.5, wires=1), control=0) + +# Multiple control qubits +qml.ctrl(qml.RY(0.3, wires=2), control=[0, 1]) + +# Negative controls (activate when control is |0⟩) +qml.ctrl(qml.Hadamard(wires=2), control=0, control_values=[0]) +``` + +### Conditional Operations + +```python +@qml.qnode(dev) +def conditional_circuit(): + qml.Hadamard(wires=0) + + # Mid-circuit measurement + m = qml.measure(0) + + # Apply gate conditionally + qml.cond(m, qml.PauliX)(wires=1) + + return qml.expval(qml.PauliZ(1)) +``` + +## Measurements + +### Expectation Values + +```python +@qml.qnode(dev) +def measure_expectation(): + qml.Hadamard(wires=0) + + # Single observable + return qml.expval(qml.PauliZ(0)) + +@qml.qnode(dev) +def measure_tensor(): + qml.Hadamard(wires=0) + qml.Hadamard(wires=1) + + # Tensor product of observables + return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) +``` + +### Probability Distributions + +```python +@qml.qnode(dev) +def measure_probabilities(): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + + # Probabilities of all basis states + return qml.probs(wires=[0, 1]) # Returns [p(|00⟩), p(|01⟩), p(|10⟩), p(|11⟩)] +``` + +### Samples and Counts + +```python +@qml.qnode(dev) +def measure_samples(shots=1000): + qml.Hadamard(wires=0) + + # Raw samples + return qml.sample(qml.PauliZ(0)) + +@qml.qnode(dev) +def measure_counts(shots=1000): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + + # Count occurrences + return qml.counts(wires=[0, 1]) +``` + +### Variance + +```python +@qml.qnode(dev) +def measure_variance(): + qml.RX(0.5, wires=0) + + # Variance of observable + return qml.var(qml.PauliZ(0)) +``` + +### Mid-Circuit Measurements + +```python +@qml.qnode(dev) +def mid_circuit_measure(): + qml.Hadamard(wires=0) + + # Measure qubit 0 during circuit + m0 = qml.measure(0) + + # Use measurement result + qml.cond(m0, qml.PauliX)(wires=1) + + # Final measurement + return qml.expval(qml.PauliZ(1)) +``` + +## Circuit Construction Patterns + +### Layer-Based Construction + +```python +def layer(weights, wires): + """Single layer of parameterized gates.""" + for i, wire in enumerate(wires): + qml.RY(weights[i], wires=wire) + + for wire in wires[:-1]: + qml.CNOT(wires=[wire, wire+1]) + +@qml.qnode(dev) +def layered_circuit(weights): + n_layers = len(weights) + wires = range(4) + + for i in range(n_layers): + layer(weights[i], wires) + + return qml.expval(qml.PauliZ(0)) +``` + +### Data Encoding + +```python +def angle_encoding(x, wires): + """Encode classical data as rotation angles.""" + for i, wire in enumerate(wires): + qml.RX(x[i], wires=wire) + +def amplitude_encoding(x, wires): + """Encode data as quantum state amplitudes.""" + qml.MottonenStatePreparation(x, wires=wires) + +def basis_encoding(x, wires): + """Encode binary data in computational basis.""" + for i, val in enumerate(x): + if val: + qml.PauliX(wires=i) +``` + +### Ansatz Patterns + +```python +# Hardware-efficient ansatz +def hardware_efficient_ansatz(weights, wires): + n_layers = len(weights) // len(wires) + + for layer in range(n_layers): + # Rotation layer + for i, wire in enumerate(wires): + qml.RY(weights[layer * len(wires) + i], wires=wire) + + # Entanglement layer + for wire in wires[:-1]: + qml.CNOT(wires=[wire, wire+1]) + +# Alternating layered ansatz +def alternating_ansatz(weights, wires): + for w in weights: + for wire in wires: + qml.RX(w[wire], wires=wire) + for wire in wires[:-1]: + qml.CNOT(wires=[wire, wire+1]) +``` + +## Dynamic Circuits + +### For Loops + +```python +@qml.qnode(dev) +def dynamic_for_loop(n_iterations): + qml.Hadamard(wires=0) + + # Dynamic for loop + for i in range(n_iterations): + qml.RX(0.1 * i, wires=0) + + return qml.expval(qml.PauliZ(0)) +``` + +### While Loops (with Catalyst) + +```python +@qml.qjit # Just-in-time compilation +@qml.qnode(dev) +def dynamic_while_loop(): + qml.Hadamard(wires=0) + + # Dynamic while loop + @qml.while_loop(lambda i: i < 5) + def loop(i): + qml.RX(0.1, wires=0) + return i + 1 + + loop(0) + return qml.expval(qml.PauliZ(0)) +``` + +### Adaptive Circuits + +```python +@qml.qnode(dev) +def adaptive_circuit(): + qml.Hadamard(wires=0) + + # Measure and adapt + m = qml.measure(0) + + # Different paths based on measurement + if m: + qml.RX(0.5, wires=1) + else: + qml.RY(0.5, wires=1) + + return qml.expval(qml.PauliZ(1)) +``` + +## Circuit Inspection + +### Drawing Circuits + +```python +# Text representation +print(qml.draw(circuit)(params)) + +# ASCII art +print(qml.draw(circuit, wire_order=[0,1,2])(params)) + +# Matplotlib visualization +fig, ax = qml.draw_mpl(circuit)(params) +``` + +### Analyzing Circuit Structure + +```python +# Get circuit specs +specs = qml.specs(circuit)(params) +print(f"Gates: {specs['gate_sizes']}") +print(f"Depth: {specs['depth']}") +print(f"Parameters: {specs['num_trainable_params']}") + +# Resource estimation +resources = qml.resource.resource_estimation(circuit)(params) +print(f"Total gates: {resources['num_gates']}") +``` + +### Tape Inspection + +```python +# Record operations +with qml.tape.QuantumTape() as tape: + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + qml.expval(qml.PauliZ(0)) + +# Inspect tape contents +print("Operations:", tape.operations) +print("Measurements:", tape.measurements) +print("Wires used:", tape.wires) +``` + +### Circuit Transformations + +```python +# Expand composite operations +expanded = qml.transforms.expand_tape(tape) + +# Cancel adjacent operations +optimized = qml.transforms.cancel_inverses(tape) + +# Commute measurements to end +commuted = qml.transforms.commute_controlled(tape) +``` + +## Best Practices + +1. **Use native gates** - Prefer gates supported by target device +2. **Minimize circuit depth** - Reduce decoherence effects +3. **Encode efficiently** - Choose encoding matching data structure +4. **Reuse circuits** - Cache compiled circuits when possible +5. **Validate measurements** - Ensure observables are Hermitian +6. **Check qubit count** - Verify device has sufficient wires +7. **Profile circuits** - Use `qml.specs()` to analyze complexity + +## Common Patterns + +### Bell State Preparation + +```python +@qml.qnode(dev) +def bell_state(): + qml.Hadamard(wires=0) + qml.CNOT(wires=[0, 1]) + return qml.state() # Returns |Φ+⟩ = (|00⟩ + |11⟩)/√2 +``` + +### GHZ State + +```python +@qml.qnode(dev) +def ghz_state(n_qubits): + qml.Hadamard(wires=0) + for i in range(n_qubits-1): + qml.CNOT(wires=[0, i+1]) + return qml.state() +``` + +### Quantum Fourier Transform + +```python +def qft(wires): + """Quantum Fourier Transform.""" + n_wires = len(wires) + for i in range(n_wires): + qml.Hadamard(wires=wires[i]) + for j in range(i+1, n_wires): + qml.CRZ(np.pi / (2**(j-i)), wires=[wires[j], wires[i]]) +``` + +### Inverse QFT + +```python +def inverse_qft(wires): + """Inverse Quantum Fourier Transform.""" + n_wires = len(wires) + for i in range(n_wires-1, -1, -1): + for j in range(n_wires-1, i, -1): + qml.CRZ(-np.pi / (2**(j-i)), wires=[wires[j], wires[i]]) + qml.Hadamard(wires=wires[i]) +``` diff --git a/scientific-skills/pennylane/references/quantum_ml.md b/scientific-skills/pennylane/references/quantum_ml.md new file mode 100644 index 0000000..0a5bac4 --- /dev/null +++ b/scientific-skills/pennylane/references/quantum_ml.md @@ -0,0 +1,571 @@ +# Quantum Machine Learning with PennyLane + +## Table of Contents +1. [Hybrid Quantum-Classical Models](#hybrid-quantum-classical-models) +2. [Framework Integration](#framework-integration) +3. [Quantum Neural Networks](#quantum-neural-networks) +4. [Variational Classifiers](#variational-classifiers) +5. [Training and Optimization](#training-and-optimization) +6. [Data Encoding Strategies](#data-encoding-strategies) +7. [Transfer Learning](#transfer-learning) + +## Hybrid Quantum-Classical Models + +### Basic Hybrid Model + +```python +import pennylane as qml +import numpy as np + +dev = qml.device('default.qubit', wires=4) + +@qml.qnode(dev) +def quantum_layer(inputs, weights): + # Encode classical data + for i, inp in enumerate(inputs): + qml.RY(inp, wires=i) + + # Parameterized quantum circuit + for wire in range(4): + qml.RX(weights[wire], wires=wire) + + for wire in range(3): + qml.CNOT(wires=[wire, wire+1]) + + # Measure + return [qml.expval(qml.PauliZ(i)) for i in range(4)] + +# Use in classical workflow +inputs = np.array([0.1, 0.2, 0.3, 0.4]) +weights = np.random.random(4) +output = quantum_layer(inputs, weights) +``` + +### Quantum-Classical Pipeline + +```python +def hybrid_model(x, quantum_weights, classical_weights): + # Classical preprocessing + x_preprocessed = np.tanh(classical_weights['pre'] @ x) + + # Quantum layer + quantum_out = quantum_layer(x_preprocessed, quantum_weights) + + # Classical postprocessing + output = classical_weights['post'] @ quantum_out + + return output +``` + +## Framework Integration + +### PyTorch Integration + +```python +import torch +import pennylane as qml + +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev, interface='torch') +def quantum_circuit(inputs, weights): + qml.RY(inputs[0], wires=0) + qml.RY(inputs[1], wires=1) + qml.RX(weights[0], wires=0) + qml.RX(weights[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# Create PyTorch layer +class QuantumLayer(torch.nn.Module): + def __init__(self, n_qubits): + super().__init__() + self.n_qubits = n_qubits + self.weights = torch.nn.Parameter(torch.randn(n_qubits)) + + def forward(self, x): + return torch.stack([quantum_circuit(xi, self.weights) for xi in x]) + +# Use in PyTorch model +class HybridModel(torch.nn.Module): + def __init__(self): + super().__init__() + self.classical_1 = torch.nn.Linear(10, 2) + self.quantum = QuantumLayer(2) + self.classical_2 = torch.nn.Linear(1, 2) + + def forward(self, x): + x = torch.relu(self.classical_1(x)) + x = self.quantum(x) + x = self.classical_2(x.unsqueeze(1)) + return x + +# Training loop +model = HybridModel() +optimizer = torch.optim.Adam(model.parameters(), lr=0.01) +criterion = torch.nn.CrossEntropyLoss() + +for epoch in range(100): + optimizer.zero_grad() + outputs = model(inputs) + loss = criterion(outputs, labels) + loss.backward() + optimizer.step() +``` + +### JAX Integration + +```python +import jax +import jax.numpy as jnp +import pennylane as qml + +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev, interface='jax') +def quantum_circuit(inputs, weights): + qml.RY(inputs[0], wires=0) + qml.RY(inputs[1], wires=1) + qml.RX(weights[0], wires=0) + qml.RX(weights[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# JAX-compatible training +@jax.jit +def loss_fn(weights, x, y): + predictions = quantum_circuit(x, weights) + return jnp.mean((predictions - y) ** 2) + +# Compute gradients with JAX +grad_fn = jax.grad(loss_fn) + +# Training +weights = jnp.array([0.1, 0.2]) +for i in range(100): + grads = grad_fn(weights, x_train, y_train) + weights = weights - 0.01 * grads +``` + +### TensorFlow Integration + +```python +import tensorflow as tf +import pennylane as qml + +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev, interface='tf') +def quantum_circuit(inputs, weights): + qml.RY(inputs[0], wires=0) + qml.RY(inputs[1], wires=1) + qml.RX(weights[0], wires=0) + qml.RX(weights[1], wires=1) + qml.CNOT(wires=[0, 1]) + return qml.expval(qml.PauliZ(0)) + +# Keras layer +class QuantumLayer(tf.keras.layers.Layer): + def __init__(self, n_qubits): + super().__init__() + self.n_qubits = n_qubits + weight_init = tf.random_uniform_initializer() + self.weights = tf.Variable( + initial_value=weight_init(shape=(n_qubits,), dtype=tf.float32), + trainable=True + ) + + def call(self, inputs): + return tf.stack([quantum_circuit(x, self.weights) for x in inputs]) + +# Keras model +model = tf.keras.Sequential([ + tf.keras.layers.Dense(2, activation='relu'), + QuantumLayer(2), + tf.keras.layers.Dense(2, activation='softmax') +]) + +model.compile( + optimizer=tf.keras.optimizers.Adam(0.01), + loss='sparse_categorical_crossentropy', + metrics=['accuracy'] +) + +model.fit(x_train, y_train, epochs=100, batch_size=32) +``` + +## Quantum Neural Networks + +### Variational Quantum Circuit (VQC) + +```python +from pennylane import numpy as np + +dev = qml.device('default.qubit', wires=4) + +def variational_block(weights, wires): + """Single layer of variational circuit.""" + for i, wire in enumerate(wires): + qml.RY(weights[i, 0], wires=wire) + qml.RZ(weights[i, 1], wires=wire) + + for i in range(len(wires)-1): + qml.CNOT(wires=[wires[i], wires[i+1]]) + +@qml.qnode(dev) +def quantum_neural_network(inputs, weights): + # Encode inputs + for i, inp in enumerate(inputs): + qml.RY(inp, wires=i) + + # Apply variational layers + n_layers = len(weights) + for layer_weights in weights: + variational_block(layer_weights, wires=range(4)) + + return qml.expval(qml.PauliZ(0)) + +# Initialize weights +n_layers = 3 +n_wires = 4 +weights_shape = (n_layers, n_wires, 2) +weights = np.random.random(weights_shape, requires_grad=True) +``` + +### Quantum Convolutional Neural Network + +```python +def conv_layer(weights, wires): + """Quantum convolutional layer.""" + n_wires = len(wires) + + # Apply local unitaries + for i in range(n_wires): + qml.RY(weights[i], wires=wires[i]) + + # Nearest-neighbor entanglement + for i in range(0, n_wires-1, 2): + qml.CNOT(wires=[wires[i], wires[i+1]]) + +def pooling_layer(wires): + """Quantum pooling (measure and discard).""" + measurements = [] + for i in range(0, len(wires), 2): + measurements.append(qml.measure(wires[i])) + return measurements + +@qml.qnode(dev) +def qcnn(inputs, weights): + # Encode image data + for i, pixel in enumerate(inputs): + qml.RY(pixel, wires=i) + + # Convolutional layers + conv_layer(weights[0], wires=range(8)) + pooling_layer(wires=range(0, 8, 2)) + + conv_layer(weights[1], wires=range(1, 8, 2)) + pooling_layer(wires=range(1, 8, 4)) + + return qml.expval(qml.PauliZ(1)) +``` + +### Quantum Recurrent Neural Network + +```python +def qrnn_cell(x, hidden, weights): + """Single QRNN cell.""" + @qml.qnode(dev) + def cell(x, h, w): + # Encode input and hidden state + qml.RY(x, wires=0) + qml.RY(h, wires=1) + + # Apply recurrent transformation + qml.RX(w[0], wires=0) + qml.RX(w[1], wires=1) + qml.CNOT(wires=[0, 1]) + qml.RY(w[2], wires=1) + + return qml.expval(qml.PauliZ(1)) + + return cell(x, hidden, weights) + +def qrnn_sequence(sequence, weights): + """Process sequence with QRNN.""" + hidden = 0.0 + outputs = [] + + for x in sequence: + hidden = qrnn_cell(x, hidden, weights) + outputs.append(hidden) + + return outputs +``` + +## Variational Classifiers + +### Binary Classification + +```python +dev = qml.device('default.qubit', wires=2) + +@qml.qnode(dev) +def variational_classifier(x, weights): + # Feature map + qml.RY(x[0], wires=0) + qml.RY(x[1], wires=1) + + # Variational layers + for w in weights: + qml.RX(w[0], wires=0) + qml.RX(w[1], wires=1) + qml.CNOT(wires=[0, 1]) + qml.RY(w[2], wires=0) + qml.RY(w[3], wires=1) + + return qml.expval(qml.PauliZ(0)) + +def cost_function(weights, X, y): + """Binary cross-entropy loss.""" + predictions = np.array([variational_classifier(x, weights) for x in X]) + predictions = (predictions + 1) / 2 # Map [-1, 1] to [0, 1] + return -np.mean(y * np.log(predictions) + (1 - y) * np.log(1 - predictions)) + +# Training +n_layers = 2 +n_params_per_layer = 4 +weights = np.random.random((n_layers, n_params_per_layer), requires_grad=True) + +opt = qml.GradientDescentOptimizer(stepsize=0.1) +for i in range(100): + weights = opt.step(lambda w: cost_function(w, X_train, y_train), weights) +``` + +### Multi-Class Classification + +```python +@qml.qnode(dev) +def multiclass_circuit(x, weights): + # Encode input + for i, val in enumerate(x): + qml.RY(val, wires=i) + + # Variational circuit + for layer_weights in weights: + for i, w in enumerate(layer_weights): + qml.RY(w, wires=i) + for i in range(len(x)-1): + qml.CNOT(wires=[i, i+1]) + + # Multiple outputs for classes + return [qml.expval(qml.PauliZ(i)) for i in range(3)] + +def softmax(x): + exp_x = np.exp(x - np.max(x)) + return exp_x / exp_x.sum() + +def predict_class(x, weights): + logits = multiclass_circuit(x, weights) + return softmax(logits) +``` + +## Training and Optimization + +### Gradient-Based Training + +```python +# Automatic differentiation +@qml.qnode(dev, diff_method='backprop') +def circuit_backprop(x, weights): + # ... circuit definition + return qml.expval(qml.PauliZ(0)) + +# Parameter shift rule +@qml.qnode(dev, diff_method='parameter-shift') +def circuit_param_shift(x, weights): + # ... circuit definition + return qml.expval(qml.PauliZ(0)) + +# Finite differences +@qml.qnode(dev, diff_method='finite-diff') +def circuit_finite_diff(x, weights): + # ... circuit definition + return qml.expval(qml.PauliZ(0)) +``` + +### Mini-Batch Training + +```python +def batch_cost(weights, X_batch, y_batch): + predictions = np.array([variational_classifier(x, weights) for x in X_batch]) + return np.mean((predictions - y_batch) ** 2) + +# Mini-batch training +batch_size = 32 +n_epochs = 100 + +for epoch in range(n_epochs): + for i in range(0, len(X_train), batch_size): + X_batch = X_train[i:i+batch_size] + y_batch = y_train[i:i+batch_size] + + weights = opt.step(lambda w: batch_cost(w, X_batch, y_batch), weights) +``` + +### Learning Rate Scheduling + +```python +def train_with_schedule(weights, X, y, n_epochs): + initial_lr = 0.1 + decay = 0.95 + + for epoch in range(n_epochs): + lr = initial_lr * (decay ** epoch) + opt = qml.GradientDescentOptimizer(stepsize=lr) + + weights = opt.step(lambda w: cost_function(w, X, y), weights) + + if epoch % 10 == 0: + print(f"Epoch {epoch}, Loss: {cost_function(weights, X, y)}") + + return weights +``` + +## Data Encoding Strategies + +### Angle Encoding + +```python +def angle_encoding(x, wires): + """Encode features as rotation angles.""" + for i, feature in enumerate(x): + qml.RY(feature, wires=wires[i]) +``` + +### Amplitude Encoding + +```python +def amplitude_encoding(x, wires): + """Encode features as state amplitudes.""" + # Normalize + x_norm = x / np.linalg.norm(x) + qml.MottonenStatePreparation(x_norm, wires=wires) +``` + +### Basis Encoding + +```python +def basis_encoding(x, wires): + """Encode binary features in computational basis.""" + for i, bit in enumerate(x): + if bit: + qml.PauliX(wires=wires[i]) +``` + +### IQP Encoding + +```python +def iqp_encoding(x, wires): + """Instantaneous Quantum Polynomial encoding.""" + # Hadamard layer + for wire in wires: + qml.Hadamard(wires=wire) + + # Encode features + for i, feature in enumerate(x): + qml.RZ(feature, wires=wires[i]) + + # Entanglement + for i in range(len(wires)-1): + qml.IsingZZ(x[i] * x[i+1], wires=[wires[i], wires[i+1]]) +``` + +### Hamiltonian Encoding + +```python +def hamiltonian_encoding(x, wires, time=1.0): + """Encode via Hamiltonian evolution.""" + # Build Hamiltonian from features + coeffs = x + obs = [qml.PauliZ(i) for i in wires] + + H = qml.Hamiltonian(coeffs, obs) + + # Apply time evolution + qml.ApproxTimeEvolution(H, time, n=10) +``` + +## Transfer Learning + +### Pre-trained Quantum Model + +```python +# Train on large dataset +pretrained_weights = train_quantum_model(large_dataset) + +# Fine-tune on specific task +def fine_tune(pretrained_weights, small_dataset, n_epochs=50): + # Freeze early layers + frozen_weights = pretrained_weights[:-1] # All but last layer + trainable_weights = pretrained_weights[-1:] # Only last layer + + @qml.qnode(dev) + def transfer_circuit(x, trainable): + # Apply frozen layers + for layer_w in frozen_weights: + variational_block(layer_w, wires=range(4)) + + # Apply trainable layer + variational_block(trainable, wires=range(4)) + + return qml.expval(qml.PauliZ(0)) + + # Train only last layer + opt = qml.AdamOptimizer(stepsize=0.01) + for epoch in range(n_epochs): + trainable_weights = opt.step( + lambda w: cost_function(w, small_dataset), + trainable_weights + ) + + return np.concatenate([frozen_weights, trainable_weights]) +``` + +### Classical-to-Quantum Transfer + +```python +# Use classical network for feature extraction +import torch.nn as nn + +classical_extractor = nn.Sequential( + nn.Conv2d(3, 16, 3), + nn.ReLU(), + nn.MaxPool2d(2), + nn.Flatten(), + nn.Linear(16*13*13, 4) # Output 4 features for quantum circuit +) + +# Quantum classifier +@qml.qnode(dev) +def quantum_classifier(features, weights): + angle_encoding(features, wires=range(4)) + variational_block(weights, wires=range(4)) + return qml.expval(qml.PauliZ(0)) + +# Combined model +def hybrid_transfer_model(image, classical_weights, quantum_weights): + features = classical_extractor(image) + return quantum_classifier(features, quantum_weights) +``` + +## Best Practices + +1. **Start simple** - Begin with small circuits and scale up +2. **Choose encoding wisely** - Match encoding to data structure +3. **Use appropriate interfaces** - Select interface matching your ML framework +4. **Monitor gradients** - Check for vanishing/exploding gradients (barren plateaus) +5. **Regularize** - Add L2 regularization to prevent overfitting +6. **Validate hardware compatibility** - Test on simulators before hardware +7. **Batch efficiently** - Use vectorization when possible +8. **Cache compilations** - Reuse compiled circuits for inference