mirror of
https://github.com/K-Dense-AI/claude-scientific-skills.git
synced 2026-03-27 07:09:27 +08:00
Add support for PennyLane: a cross-platform library for quantum computing, quantum machine learning, and quantum chemistry.
This commit is contained in:
571
scientific-skills/pennylane/references/quantum_ml.md
Normal file
571
scientific-skills/pennylane/references/quantum_ml.md
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user