RealVectorFramework
Zero Overhead Abstractions for Vector Algorithms
Install / Use
/learn @sandialabs/RealVectorFrameworkREADME
RealVectorFramework (RVF)
A header-only, generic C++20 framework for vector operations and numerical algorithms built on TInCuP customization points. RVF provides a unified interface for linear algebra operations that works with any vector-like type, enabling high-performance numerical computing with pluggable backends.
Key Features
- 🔧 Generic by Design: Works with any vector type (
std::vector, custom types, GPU vectors) - ⚡ Zero-Overhead Abstractions: Compile-time polymorphism via C++20 concepts
- 🧩 Pluggable Backends: Easy integration with specialized libraries (BLAS, CUDA, etc.)
- 📊 Rich Algorithm Suite: Trust region methods, conjugate gradient, optimization algorithms
- 🏗️ Memory Management: Advanced memory arena system for high-performance computing
- 🎯 Concept-Driven: Strong type safety with expressive concepts
Quick Start
#include "rvf.hpp"
#include "operations/std_cpo_impl.hpp" // Enable std::vector support
using namespace rvf;
using Vector = std::vector<double>;
// Create and manipulate vectors using RVF operations
Vector x = {1.0, 2.0, 3.0};
auto y_clone = clone(x); // Clone vector
auto& y = deref_if_needed(y_clone); // Get reference
scale_in_place(y, 2.0); // y *= 2.0
axpy_in_place(y, 1.5, x); // y += 1.5 * x
double dot = inner_product(x, y); // Compute x · y
- Documentation:
- 📚 Complete API Reference - Comprehensive documentation for all RVF components
- 🔧 CPO Integration Guide - Detailed guidance on ADL
tag_invokevstincup::cpo_implspecializations
Core Customization Point Objects (CPOs)
RVF provides a complete set of vector operations through Customization Point Objects that can be specialized for any vector type:
📐 Basic Operations
| CPO | Description | Usage |
|-----|-------------|-------|
| clone(v) | Create a copy of vector v | auto v2 = clone(v1); |
| dimension(v) | Get vector size/dimension | size_t n = dimension(v); |
| inner_product(x, y) | Compute dot product x · y | double dot = inner_product(x, y); |
| deref_if_needed(v) | Dereference wrapper types | auto& ref = deref_if_needed(clone(v)); |
➕ In-Place Operations
| CPO | Description | Mathematical Equivalent |
|-----|-------------|-------------------------|
| scale_in_place(v, α) | Scale vector by scalar | v ← α * v |
| add_in_place(y, x) | Vector addition | y ← y + x |
| axpy_in_place(y, α, x) | Scaled addition | y ← y + α * x |
🔄 Advanced Operations
| CPO | Description | Purpose |
|-----|-------------|---------|
| self_map_c<F, Vec> | Function maps vector to same size space | Concept for operators like Hessians |
| unary_in_place(v, f) | Apply unary function element-wise | Custom transformations |
| binary_in_place(z, x, y, f) | Apply binary function element-wise | Element-wise operations |
| variadic_in_place(v, f, args...) | Variadic element-wise operations | Complex transformations |
🏗️ Memory Arena Integration
| CPO | Description | Use Case |
|-----|-------------|----------|
| arena_integration | Memory pool allocation | High-frequency allocations |
| arena_observers | Memory usage tracking | Performance analysis |
Cloning Idiom (Owner + Reference)
Many RVF operations accept and return either values or smart-pointer-like wrappers. The clone CPO may therefore return either a value (e.g., std::vector<double>) or a wrapper (e.g., std::unique_ptr<std::vector<double>>). Use deref_if_needed to obtain a reference to the underlying vector.
Recommended idiom: keep the owner and the reference adjacent to avoid lifetime issues and to satisfy concept-constrained APIs (which check types before applying conversions).
auto cl = rvf::clone(x); auto& xr = rvf::deref_if_needed(cl);
// use xr as a regular vector; cl owns storage so xr stays valid
Rationale:
- Concept checks run before implicit conversions, so passing temporary wrappers to functions constrained on
real_vector_cwill fail. Binding a reference (xr) avoids this. - Keeping the owner (
cl) and the reference (xr) adjacent makes the code read more mathematically while preventing dangling references.
This idiom is used throughout the algorithms in this repo (e.g., Conjugate Gradient, Sherman–Morrison, gradient descent with bounds).
Algorithms & Solvers
RVF provides a comprehensive suite of numerical algorithms for optimization and linear algebra:
🎯 Optimization Algorithms
Trust Region Methods
TruncatedCG: Steihaug-Toint truncated conjugate gradient for trust region subproblems// Create solver instance (clones objective once) TruncatedCG<Objective, Vector> tcg_solver(objective, x_template); // Solve trust region subproblem: min g^T s + (1/2) s^T H s, ||s|| ≤ δ auto result = tcg_solver.solve(x_current, step, trust_radius, params); // Check termination status switch (result.status) { case TerminationStatus::CONVERGED: /* ... */ case TerminationStatus::NEGATIVE_CURVATURE: /* ... */ case TerminationStatus::TRUST_REGION_BOUNDARY: /* ... */ }
Line Search Methods
gradient_descent_bounds: Projected gradient descent with bound constraints and backtracking line search// Define bound constraints bound_constraints<Vector> bounds{lower, upper}; // Solve: min f(x) subject to lower ≤ x ≤ upper gradient_descent_bounds(objective, bounds, x, grad_tol, step_size, max_iter);
🔢 Linear Algebra Algorithms
Conjugate Gradient
conjugate_gradient: Iterative solver for symmetric positive definite systems// Solve A*x = b using conjugate gradient auto result = conjugate_gradient(A_operator, b, x_initial, tolerance, max_iter);
Sherman-Morrison Formula
sherman_morrison_solve: Efficient rank-1 update to matrix inverse// Solve (A + u*v^T)*x = b efficiently when A^{-1} is known sherman_morrison_solve(A_inv_action, u, v, b, x);
📊 Algorithm Features
| Algorithm | Problem Type | Key Features |
|-----------|-------------|--------------|
| TruncatedCG | Trust region subproblems | Negative curvature handling, boundary detection |
| gradient_descent_bounds | Bound-constrained optimization | Projection, backtracking line search |
| conjugate_gradient | Linear systems (SPD) | Iterative, matrix-free capable |
| sherman_morrison | Low-rank matrix updates | O(n) complexity for rank-1 updates |
Objective Function Framework
RVF provides a concept-driven framework for defining objective functions with automatic differentiation support:
🎯 Core Concepts
// Objective function concepts (output arguments first)
template<typename F, typename Vec>
concept objective_value_c = requires(const F& f, const Vec& x) {
{ f.value(x) } -> std::convertible_to<vector_value_t<Vec>>;
};
template<typename F, typename Vec>
concept objective_gradient_c = requires(const F& f, const Vec& x, Vec& g) {
{ f.gradient(g, x) } -> std::same_as<void>; // g = ∇f(x)
};
template<typename F, typename Vec>
concept objective_hess_vec_c = requires(const F& f, const Vec& x, const Vec& v, Vec& hv) {
{ f.hessVec(hv, v, x) } -> std::same_as<void>; // hv = H*v where H = ∇²f(x)
};
📈 Example: Zakharov Test Function
// f(x) = x^T x + (1/4)(k^T x)^2 + (1/16)(k^T x)^4
auto zakharov = make_zakharov_objective(k_vector);
Vector x = {1.0, 2.0, 3.0};
Vector grad(3), hess_vec_result(3), direction(3);
double f_val = zakharov.value(x); // Evaluate f(x)
zakharov.gradient(grad, x); // ∇f(x) → grad
zakharov.hessVec(hess_vec_result, direction, x); // H*direction → result
Examples & Tutorials
RVF includes comprehensive examples demonstrating various features and use cases:
🚀 Getting Started Examples
examples/main.cpp: Basic RVF usage with generictag_invokeimplementationsexamples/traits_vs_ranges.cpp: Backend specialization usingoperations/std_cpo_impl.hpp
🧮 Algorithm Demonstrations
examples/conjugate_gradient.cpp: Iterative linear system solverexamples/sherman_morrison.cpp: Efficient low-rank matrix updatesexamples/hs1_gradient_descent.cpp: Bound-constrained optimization (Hock-Schittkowski problem)examples/hs1_advanced.cpp: Advanced optimization with iteration callbacks
🎯 Trust Region & Nonlinear Optimization
examples/zakharov_example.cpp: Complete trust region method with truncated CGexamples/zakharov_rvf.cpp: RVF-native implementationexamples/zakharov_truncated_cg.cpp: Detailed truncated CG algorithm showcaseexamples/zakharov_truncated_cg_simple.cpp: Simplified TruncatedCG usage
🏗️ Memory Management
examples/memory_arena_example.cpp: High-performance memory arena usage
💡 Complete Example: Trust Region Optimization
#include "rvf.hpp"
#include "operations/std_cpo_impl.hpp"
using namespace rvf;
using Vector = std::vector<double>;
int main() {
// Define problem size and initial point
const size_t n = 5;
Vector x(n, 3.0); // Start at x = (3, 3, 3, 3, 3)
// Create Zakharov objective function
Vector k(n);
std::iota(k.begin(), k.end(), 1.0); // k = (1, 2, 3, 4, 5)
auto objective = make_zakharov_objective(k);
// Set up TruncatedCG solver (efficient: clones only once)
TruncatedCG<decltype(objective), Vector> tcg_solver(objective, x);
// Trust region parameters
double trust_radius = 1.0;
typename decltype(tcg_solver)::Params tcg_params;
tcg_params.abs_tol = 1e-8;
tcg_params.rel_tol = 1e-2;
tcg_params.max_iter = n;
// Solve trust region subproblem
Vector step(n, 0.0);
auto result = tcg_solver.solve(x, step, trust_radius, t
