# ADR-QE-005: Variational Quantum Eigensolver (VQE) Support **Status**: Proposed **Date**: 2026-02-06 **Authors**: ruv.io, RuVector Team **Deciders**: Architecture Review Board ## Version History | Version | Date | Author | Changes | |---------|------|--------|---------| | 0.1 | 2026-02-06 | ruv.io | Initial VQE architecture proposal | --- ## Context ### The Variational Quantum Eigensolver Problem The Variational Quantum Eigensolver (VQE) is one of the most important near-term quantum algorithms, with direct applications in computational chemistry, materials science, and combinatorial optimization. VQE computes ground-state energies of molecular Hamiltonians by variationally minimizing the expectation value of a Hamiltonian operator with respect to a parameterized quantum state (ansatz). ### Why VQE Matters for ruQu VQE sits at the intersection of quantum simulation and classical optimization, making it a natural fit for ruQu's hybrid classical-quantum architecture: 1. **Chemistry applications**: Drug discovery, catalyst design, battery materials 2. **Optimization**: QUBO problems, portfolio optimization, logistics 3. **Benchmarking**: VQE circuits exercise the full gate set and serve as a representative workload for evaluating simulator performance 4. **Agent integration**: ruVector agents can autonomously explore chemical configuration spaces using VQE as the inner evaluation kernel ### Core Requirements | Requirement | Description | Priority | |-------------|-------------|----------| | Parameterized circuits | Symbolic gate angles resolved at evaluation time | P0 | | Hamiltonian decomposition | Represent H as sum of weighted Pauli strings | P0 | | Exact expectation values | Direct state vector computation (no shot noise) | P0 | | Gradient evaluation | Parameter-shift rule for classical optimizer | P0 | | Shot-based sampling | Optional mode for hardware noise emulation | P1 | | Classical optimizer interface | Trait-based abstraction for multiple optimizers | P1 | | Hardware-efficient ansatz | Pre-built ansatz library for common topologies | P2 | ### Current Limitations Without dedicated VQE support, users must manually: - Construct parameterized circuits with explicit angle substitution per iteration - Decompose Hamiltonians into individual Pauli measurements - Implement gradient computation by duplicating circuit evaluations - Wire up classical optimizers with no standard interface This is error-prone and leaves significant performance on the table, since a state vector simulator can compute exact expectation values in a single pass without sampling overhead. --- ## Decision ### 1. Parameterized Gate Architecture Circuits accept symbolic parameters that are resolved to numeric values per evaluation. This avoids circuit reconstruction on each VQE iteration. ``` ┌──────────────────────────────────────────────────┐ │ Parameterized Circuit │ │ │ │ ┌─────┐ ┌──────────┐ ┌─────┐ ┌──────────┐ │ |0> ─────────┤ │ H ├──┤ Ry(θ[0]) ├──┤ CX ├──┤ Rz(θ[2]) ├──┤─── │ └─────┘ └──────────┘ └──┬──┘ └──────────┘ │ │ │ │ |0> ─────────┤──────────────────────────────●───── Ry(θ[1]) ────┤─── │ │ └──────────────────────────────────────────────────┘ │ ▼ parameters: [θ[0], θ[1], θ[2]] values: [0.54, 1.23, -0.87] ``` **Data model**: ```rust /// A symbolic parameter in a quantum circuit. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Parameter { pub name: String, pub index: usize, } /// A gate that may reference symbolic parameters. pub enum ParameterizedGate { /// Fixed gate (no parameters) Fixed(Gate), /// Rotation gate with a symbolic angle Rx(ParameterExpr), Ry(ParameterExpr), Rz(ParameterExpr), /// Parameterized two-qubit gate Rzz(ParameterExpr, Qubit, Qubit), } /// Expression for a gate parameter (supports linear combinations). pub enum ParameterExpr { /// Direct parameter reference: θ[i] Param(usize), /// Scaled parameter: c * θ[i] Scaled(f64, usize), /// Sum of expressions Sum(Box, Box), /// Constant value Constant(f64), } ``` **Resolution**: When `evaluate(params: &[f64])` is called, each `ParameterExpr` is resolved to a concrete `f64`, and the corresponding unitary matrix is computed. This happens once per VQE iteration and is negligible compared to state vector manipulation. ### 2. Hamiltonian Representation The Hamiltonian is represented as a sum of weighted Pauli strings: ``` H = c_0 * I + c_1 * Z_0 + c_2 * Z_1 + c_3 * Z_0 Z_1 + c_4 * X_0 X_1 + ... ``` where each term is a tensor product of single-qubit Pauli operators {I, X, Y, Z}. ```rust /// A single Pauli operator on one qubit. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Pauli { I, X, Y, Z, } /// A Pauli string: tensor product of single-qubit Paulis. /// Stored as a compact bitfield for n-qubit systems. /// /// Encoding: 2 bits per qubit (00=I, 01=X, 10=Y, 11=Z) /// For n <= 32 qubits, fits in a single u64. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PauliString { /// Packed Pauli operators (2 bits each) pub ops: Vec, /// Number of qubits pub n_qubits: usize, } /// A Hamiltonian as a sum of weighted Pauli strings. /// /// H = sum_j c_j P_j pub struct PauliSum { /// Terms: (coefficient, Pauli string) pub terms: Vec<(Complex64, PauliString)>, /// Number of qubits pub n_qubits: usize, } ``` **Optimization**: Identity terms (all-I Pauli strings) contribute a constant energy offset and require no state vector computation. The implementation detects and separates these before the expectation loop. ### 3. Direct Expectation Value Computation This is the critical performance advantage of state vector simulation over real hardware. On physical quantum computers, expectation values must be estimated via repeated measurement (shot-based sampling), requiring O(1/epsilon^2) shots for epsilon precision. In a state vector simulator, we compute the **exact** expectation value: ``` = sum_j c_j * ``` For each Pauli string P_j, the expectation value is: ``` = sum_k psi_k* (P_j |psi>)_k ``` Since P_j is a tensor product of single-qubit Paulis, its action on a basis state |k> is: - I: |k> -> |k> - X: flips qubit, no phase - Y: flips qubit, phase factor +/- i - Z: no flip, phase factor +/- 1 This means each Pauli string maps each basis state to exactly one other basis state with a phase factor. The expectation value reduces to a sum over 2^n amplitudes. ```rust impl QuantumState { /// Compute the exact expectation value of a PauliSum. /// /// Complexity: O(T * 2^n) where T = number of Pauli terms, n = qubits. /// For a 12-qubit system with 100 Pauli terms: /// 100 * 4096 = 409,600 operations ~ 0.5ms pub fn expectation(&self, hamiltonian: &PauliSum) -> f64 { let mut total = 0.0_f64; for (coeff, pauli) in &hamiltonian.terms { let mut term_val = Complex64::zero(); for k in 0..self.amplitudes.len() { // Compute P_j |k>: determine target index and phase let (target_idx, phase) = pauli.apply_to_basis(k); // = phase * psi[target_idx] // Accumulate psi[k]* * phase * psi[target_idx] term_val += self.amplitudes[k].conj() * phase * self.amplitudes[target_idx]; } total += (coeff * term_val).re; } total } } ``` **Function signature**: `QuantumState::expectation(PauliSum) -> f64` #### Accuracy Advantage Over Sampling | Method | Precision | Evaluations | 12-qubit Cost | |--------|-----------|-------------|---------------| | Shot-based (1000 shots) | ~3% | 1000 circuit runs per term | ~500ms | | Shot-based (10000 shots) | ~1% | 10000 circuit runs per term | ~5s | | Shot-based (1M shots) | ~0.1% | 1M circuit runs per term | ~500s | | **Exact (state vector)** | **Machine epsilon** | **1 pass over state** | **~0.5ms** | For VQE convergence, exact expectation values eliminate the statistical noise floor that plagues hardware-based VQE. Classical optimizers receive clean gradients, leading to: - Faster convergence (fewer iterations) - No barren plateau artifacts from shot noise - Deterministic reproducibility ### 4. Gradient Support via Parameter-Shift Rule The parameter-shift rule provides exact analytic gradients for parameterized quantum gates. For a gate with parameter theta: ``` d/d(theta) = [(theta + pi/2) - (theta - pi/2)] / 2 ``` This requires two circuit evaluations per parameter per gradient component. ```rust /// Compute the gradient of the expectation value with respect to all parameters. /// /// Uses the parameter-shift rule: /// grad_i = [E(theta_i + pi/2) - E(theta_i - pi/2)] / 2 /// /// Complexity: O(2 * n_params * circuit_eval_cost) /// For 12 qubits, 20 parameters, 100 Pauli terms: /// 2 * 20 * (circuit_sim + expectation) ~ 40 * 1ms = 40ms pub fn gradient( circuit: &ParameterizedCircuit, hamiltonian: &PauliSum, params: &[f64], ) -> Vec { let n_params = params.len(); let mut grad = vec![0.0; n_params]; let shift = std::f64::consts::FRAC_PI_2; // pi/2 for i in 0..n_params { // Forward shift let mut params_plus = params.to_vec(); params_plus[i] += shift; let e_plus = evaluate_energy(circuit, hamiltonian, ¶ms_plus); // Backward shift let mut params_minus = params.to_vec(); params_minus[i] -= shift; let e_minus = evaluate_energy(circuit, hamiltonian, ¶ms_minus); grad[i] = (e_plus - e_minus) / 2.0; } grad } ``` ### 5. Classical Optimizer Interface A trait-based abstraction supports plugging in different classical optimizers without changing the VQE loop: ```rust /// Trait for classical optimizers used in the VQE outer loop. pub trait ClassicalOptimizer: Send { /// Initialize the optimizer with the parameter count. fn initialize(&mut self, n_params: usize); /// Propose next parameter values given current energy and optional gradient. fn step( &mut self, params: &[f64], energy: f64, gradient: Option<&[f64]>, ) -> OptimizerResult; /// Check if the optimizer has converged. fn has_converged(&self) -> bool; /// Get optimizer name for logging. fn name(&self) -> &str; } /// Result of an optimizer step. pub struct OptimizerResult { pub new_params: Vec, pub converged: bool, pub iteration: usize, } ``` **Provided implementations**: | Optimizer | Type | Gradient Required | Best For | |-----------|------|-------------------|----------| | `GradientDescent` | Gradient-based | Yes | Simple landscapes | | `Adam` | Adaptive gradient | Yes | Noisy gradients, deep circuits | | `LBFGS` | Quasi-Newton | Yes | Smooth landscapes, fast convergence | | `COBYLA` | Derivative-free | No | Non-differentiable cost functions | | `NelderMead` | Simplex | No | Low-dimensional problems | | `SPSA` | Stochastic | No | Shot-based mode, noisy evaluations | ### 6. VQE Iteration Loop The complete VQE algorithm proceeds as follows: ``` VQE Iteration Loop ================== Input: Hamiltonian H (PauliSum), Ansatz A (ParameterizedCircuit), Optimizer O (ClassicalOptimizer), initial params theta_0 Output: Minimum energy E_min, optimal params theta_opt theta = theta_0 O.initialize(len(theta)) repeat: ┌─────────────────────────────────────────────┐ │ 1. PREPARE STATE │ │ |psi(theta)> = A(theta) |0...0> │ │ [Simulate parameterized circuit] │ │ Cost: O(G * 2^n) where G = gate count │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 2. EVALUATE ENERGY │ │ E = │ │ [Direct state vector expectation] │ │ Cost: O(T * 2^n) where T = Pauli terms │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 3. COMPUTE GRADIENT (if optimizer needs it) │ │ grad = parameter_shift(A, H, theta) │ │ [2 * n_params circuit evaluations] │ │ Cost: O(2P * (G + T) * 2^n) │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 4. CLASSICAL UPDATE │ │ theta_new = O.step(theta, E, grad) │ │ [Pure classical computation] │ │ Cost: O(P^2) for quasi-Newton │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 5. CONVERGENCE CHECK │ │ if |E_new - E_old| < tol: STOP │ │ else: theta = theta_new, continue │ └─────────────────────────────────────────────┘ return (E_min, theta_opt) ``` **Pseudocode**: ```rust pub fn vqe( ansatz: &ParameterizedCircuit, hamiltonian: &PauliSum, optimizer: &mut dyn ClassicalOptimizer, config: &VqeConfig, ) -> VqeResult { let n_params = ansatz.parameter_count(); let mut params = config.initial_params.clone() .unwrap_or_else(|| vec![0.0; n_params]); optimizer.initialize(n_params); let mut best_energy = f64::INFINITY; let mut best_params = params.clone(); let mut history = Vec::new(); for iteration in 0..config.max_iterations { // Step 1+2: Simulate circuit and compute energy let state = ansatz.simulate(¶ms); let energy = state.expectation(hamiltonian); // Track best if energy < best_energy { best_energy = energy; best_params = params.clone(); } // Step 3: Compute gradient if needed let grad = if optimizer.needs_gradient() { Some(gradient(ansatz, hamiltonian, ¶ms)) } else { None }; history.push(VqeIteration { iteration, energy, params: params.clone() }); // Step 4: Classical update let result = optimizer.step(¶ms, energy, grad.as_deref()); params = result.new_params; // Step 5: Convergence check if result.converged || (iteration > 0 && (history[iteration].energy - history[iteration - 1].energy).abs() < config.convergence_threshold) { break; } } VqeResult { energy: best_energy, optimal_params: best_params, iterations: history.len(), history, converged: optimizer.has_converged(), } } ``` ### 7. Optional Shot-Based Sampling Mode For mimicking real hardware behavior and testing noise resilience: ```rust /// Configuration for shot-based VQE mode. pub struct ShotConfig { /// Number of measurement shots per expectation estimation pub shots: usize, /// Random seed for reproducibility pub seed: Option, /// Readout error rate (probability of bit flip on measurement) pub readout_error: f64, } impl QuantumState { /// Estimate expectation value via shot-based sampling. /// /// Samples the state `shots` times in the computational basis, /// then computes the empirical expectation of each Pauli term. pub fn expectation_sampled( &self, hamiltonian: &PauliSum, config: &ShotConfig, ) -> (f64, f64) { // Returns (mean, standard_error) // Standard error = std_dev / sqrt(shots) todo!() } } ``` ### 8. Hardware-Efficient Ansatz Patterns Pre-built ansatz constructors for common use cases: ``` Hardware-Efficient Ansatz (depth d, n qubits): Layer 1..d: ┌─────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┤ Ry ├──┤ Rz ├──┤ CNOT ├──┤ Ry ├── └─────┘ └──────────┘ │ ladder │ └──────────┘ ┌─────┐ ┌──────────┐ │ │ ┌──────────┐ ┤ Ry ├──┤ Rz ├──┤ ├──┤ Ry ├── └─────┘ └──────────┘ └──────────┘ └──────────┘ Parameters per layer: 3n (Ry + Rz + Ry per qubit) Total parameters: 3nd ``` ```rust /// Pre-built ansatz constructors. pub mod ansatz { /// Hardware-efficient ansatz with Ry-Rz layers and linear CNOT entanglement. pub fn hardware_efficient(n_qubits: usize, depth: usize) -> ParameterizedCircuit; /// UCCSD (Unitary Coupled Cluster Singles and Doubles) for chemistry. /// Generates excitation operators based on active space. pub fn uccsd(n_electrons: usize, n_orbitals: usize) -> ParameterizedCircuit; /// Hamiltonian variational ansatz: layers of exp(-i * theta_j * P_j) /// for each term P_j in the Hamiltonian. pub fn hamiltonian_variational( hamiltonian: &PauliSum, depth: usize, ) -> ParameterizedCircuit; /// Symmetry-preserving ansatz that respects particle number conservation. pub fn symmetry_preserving( n_qubits: usize, n_particles: usize, depth: usize, ) -> ParameterizedCircuit; } ``` ### 9. Performance Analysis #### 12-Qubit VQE Performance Estimate | Component | Operations | Time | |-----------|-----------|------| | State vector size | 2^12 = 4,096 complex amplitudes | 64 KB | | Circuit simulation (50 gates) | 50 * 4096 = 204,800 ops | ~0.3ms | | Expectation (100 Pauli terms) | 100 * 4096 = 409,600 ops | ~0.5ms | | Gradient (20 params) | 40 * (0.3 + 0.5) ms | ~32ms | | Classical optimizer step | O(20^2) | ~0.001ms | | **Total per iteration (with gradient)** | | **~33ms** | | **Total per iteration (no gradient)** | | **~0.8ms** | For gradient-free optimizers (COBYLA, Nelder-Mead), a 12-qubit VQE iteration completes in under 1ms. With parameter-shift gradients, the cost scales linearly with parameter count but remains under 50ms for typical chemistry ansatze. **Scaling with qubit count**: | Qubits | State Size | Memory | Energy Eval (100 terms) | Gradient (20 params) | |--------|-----------|--------|------------------------|---------------------| | 8 | 256 | 4 KB | ~0.03ms | ~2ms | | 12 | 4,096 | 64 KB | ~0.5ms | ~33ms | | 16 | 65,536 | 1 MB | ~8ms | ~500ms | | 20 | 1,048,576 | 16 MB | ~130ms | ~8s | | 24 | 16,777,216 | 256 MB | ~2s | ~130s | | 28 | 268,435,456 | 4 GB | ~33s | ~35min | ### 10. Integration with ruVector Agent System ruVector agents can drive autonomous chemistry optimization using VQE as the evaluation kernel: ``` ┌─────────────────────────────────────────────────────────────────┐ │ ruVector Agent Orchestration │ │ │ │ ┌──────────┐ ┌──────────────┐ ┌────────────────────┐ │ │ │ Research │───>│ Architecture │───>│ Chemistry Agent │ │ │ │ Agent │ │ Agent │ │ │ │ │ │ │ │ │ │ - Molecule spec │ │ │ │ Literature│ │ Hamiltonian │ │ - Basis set sel. │ │ │ │ search │ │ generation │ │ - Active space │ │ │ └──────────┘ └──────────────┘ │ - VQE execution │ │ │ │ - Result analysis │ │ │ └────────┬───────────┘ │ │ │ │ │ ┌────────▼───────────┐ │ │ │ ruQu VQE Engine │ │ │ │ │ │ │ │ Parameterized │ │ │ │ Circuit + PauliSum│ │ │ │ + Optimizer │ │ │ └────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` The agent workflow: 1. **Research agent** retrieves molecular structure and prior computational results 2. **Architecture agent** generates the qubit Hamiltonian (Jordan-Wigner or Bravyi-Kitaev transformation from fermionic operators) 3. **Chemistry agent** selects ansatz, optimizer, and runs VQE iterations 4. **Results** are stored in ruVector memory for pattern learning across molecules --- ## Consequences ### Benefits 1. **Exact expectation values** eliminate sampling noise, enabling faster convergence and deterministic reproducibility -- a major advantage over hardware VQE 2. **Symbolic parameterization** avoids circuit reconstruction overhead, reducing per-iteration cost to pure state manipulation 3. **Trait-based optimizer interface** allows users to swap optimizers without touching VQE logic, and supports custom optimizer implementations 4. **Hardware-efficient ansatz library** provides tested, production-quality circuit templates for common use cases 5. **Gradient support** via parameter-shift rule enables modern gradient-based optimization (Adam, L-BFGS) that converges significantly faster than derivative-free methods 6. **Agent integration** enables autonomous, memory-enhanced chemistry exploration that learns from prior VQE runs across molecular configurations ### Risks | Risk | Probability | Impact | Mitigation | |------|------------|--------|------------| | Exponential memory scaling limits qubit count | High | Medium | Tensor network backend for >30 qubits (future ADR) | | Parameter-shift gradient cost scales with parameter count | Medium | Medium | Batched gradient evaluation, simultaneous perturbation (SPSA) fallback | | Hamiltonian term count explosion for large molecules | Medium | High | Pauli grouping (qubit-wise commuting), measurement reduction techniques | | Optimizer convergence to local minima | Medium | Medium | Multi-start strategies, QAOA-inspired initialization | ### Trade-offs | Decision | Advantage | Disadvantage | |----------|-----------|--------------| | Exact expectation over sampling | Machine-precision accuracy | Not representative of real hardware noise | | Parameter-shift over finite-difference | Exact gradients | 2x evaluations per parameter | | Trait-based optimizer | Extensible | Slight abstraction overhead | | Compact PauliString bitfield | Cache-friendly | Complex bit manipulation logic | --- ## References - Peruzzo, A. et al. "A variational eigenvalue solver on a photonic quantum processor." Nature Communications 5, 4213 (2014) - McClean, J.R. et al. "The theory of variational hybrid quantum-classical algorithms." New Journal of Physics 18, 023023 (2016) - Kandala, A. et al. "Hardware-efficient variational quantum eigensolver for small molecules." Nature 549, 242-246 (2017) - Schuld, M. et al. "Evaluating analytic gradients on quantum hardware." Physical Review A 99, 032331 (2019) - ADR-001: ruQu Architecture - Classical Nervous System for Quantum Machines - ADR-QE-001 through ADR-QE-004: Prior quantum engine architecture decisions - ruQu crate: `crates/ruQu/src/` - existing syndrome processing and coherence gate infrastructure - ruVector memory system: pattern storage for cross-molecule VQE learning