CUDA-Q Solvers Library
Overview
The CUDA-Q Solvers library provides high-level quantum-classical hybrid algorithms and supporting infrastructure for quantum chemistry and optimization problems. It features implementations of VQE, ADAPT-VQE, and supporting utilities for Hamiltonian generation and operator pool management.
Core Components
Variational Algorithms:
Variational Quantum Eigensolver (VQE)
Adaptive Derivative-Assembled Pseudo-Trotter VQE (ADAPT-VQE)
Quantum Chemistry Tools:
Molecular Hamiltonian Generation
One-Particle Operator Creation
Geometry Management
Operator Infrastructure:
Operator Pool Generation
Fermion-to-Qubit Mappings
Gradient Computation
Operator Infrastructure
Molecular Hamiltonian Options
The molecule_options
structure provides extensive configuration for molecular calculations in CUDA-QX.
Option |
Type |
Default |
Description |
---|---|---|---|
driver |
string |
“RESTPySCFDriver” |
Quantum chemistry driver backend |
fermion_to_spin |
string |
“jordan_wigner” |
Fermionic to qubit operator mapping |
type |
string |
“gas_phase” |
Type of molecular system |
symmetry |
bool |
false |
Use molecular symmetry |
memory |
double |
4000.0 |
Memory allocation (MB) |
cycles |
size_t |
100 |
Maximum SCF cycles |
initguess |
string |
“minao” |
Initial SCF guess method |
UR |
bool |
false |
Enable unrestricted calculations |
nele_cas |
optional <size_t> |
nullopt |
Number of electrons in active space |
norb_cas |
optional <size_t> |
nullopt |
Number of spatial orbitals in in active space |
MP2 |
bool |
false |
Enable MP2 calculations |
natorb |
bool |
false |
Use natural orbitals |
casci |
bool |
false |
Perform CASCI calculations |
ccsd |
bool |
false |
Perform CCSD calculations |
casscf |
bool |
false |
Perform CASSCF calculations |
integrals_natorb |
bool |
false |
Use natural orbitals for integrals |
integrals_casscf |
bool |
false |
Use CASSCF orbitals for integrals |
potfile |
optional <string> |
nullopt |
Path to external potential file |
verbose |
bool |
false |
Enable detailed output logging |
Example Usage
import cudaq_solvers as solvers
# Configure molecular options
options = {
'fermion_to_spin': 'jordan_wigner',
'casci': True,
'memory': 8000.0,
'verbose': True
}
# Create molecular Hamiltonian
molecule = solvers.create_molecule(
geometry=[('H', (0., 0., 0.)),
('H', (0., 0., 0.7474))],
basis='sto-3g',
spin=0,
charge=0,
**options
)
using namespace cudaq::solvers;
// Configure molecular options
molecule_options options;
options.fermion_to_spin = "jordan_wigner";
options.casci = true;
options.memory = 8000.0;
options.verbose = true;
// Create molecular geometry
auto geometry = molecular_geometry({
atom{"H", {0.0, 0.0, 0.0}},
atom{"H", {0.0, 0.0, 0.7474}}
});
// Create molecular Hamiltonian
auto molecule = create_molecule(
geometry,
"sto-3g",
0, // spin
0, // charge
options
);
Variational Quantum Eigensolver (VQE)
The VQE algorithm finds the minimum eigenvalue of a Hamiltonian using a hybrid quantum-classical approach.
VQE Examples
The VQE implementation supports multiple usage patterns with different levels of customization.
Basic Usage
import cudaq
from cudaq import spin
import cudaq_solvers as solvers
# Define quantum kernel (ansatz)
@cudaq.kernel
def ansatz(theta: float):
q = cudaq.qvector(2)
x(q[0])
ry(theta, q[1])
x.ctrl(q[1], q[0])
# Define Hamiltonian
H = 5.907 - 2.1433 * spin.x(0) * spin.x(1) - \
2.1433 * spin.y(0) * spin.y(1) + \
0.21829 * spin.z(0) - 6.125 * spin.z(1)
# Run VQE with defaults (cobyla optimizer)
energy, parameters, data = solvers.vqe(
lambda thetas: ansatz(thetas[0]),
H,
initial_parameters=[0.0],
verbose=True
)
print(f"Ground state energy: {energy}")
#include "cudaq.h"
#include "cudaq/solvers/operators.h"
#include "cudaq/solvers/vqe.h"
// Define quantum kernel
struct ansatz {
void operator()(std::vector<double> theta) __qpu__ {
cudaq::qvector q(2);
x(q[0]);
ry(theta[0], q[1]);
x<cudaq::ctrl>(q[1], q[0]);
}
};
// Create Hamiltonian
auto H = 5.907 - 2.1433 * x(0) * x(1) -
2.1433 * y(0) * y(1) +
0.21829 * z(0) - 6.125 * z(1);
// Run VQE with default optimizer
auto result = cudaq::solvers::vqe(
ansatz{},
H,
{0.0}, // Initial parameters
{{"verbose", true}}
);
printf("Ground state energy: %lf\n", result.energy);
Custom Optimization
# Using L-BFGS-B optimizer with parameter-shift gradients
energy, parameters, data = solvers.vqe(
lambda thetas: ansatz(thetas[0]),
H,
initial_parameters=[0.0],
optimizer='lbfgs',
gradient='parameter_shift',
verbose=True
)
# Using SciPy optimizer directly
from scipy.optimize import minimize
def callback(xk):
exp_val = cudaq.observe(ansatz, H, xk[0]).expectation()
print(f"Energy at iteration: {exp_val}")
energy, parameters, data = solvers.vqe(
lambda thetas: ansatz(thetas[0]),
H,
initial_parameters=[0.0],
optimizer=minimize,
callback=callback,
method='L-BFGS-B',
jac='3-point',
tol=1e-4,
options={'disp': True}
)
// Using L-BFGS optimizer with central difference gradients
auto optimizer = cudaq::optim::optimizer::get("lbfgs");
auto gradient = cudaq::observe_gradient::get(
"central_difference",
ansatz{},
H
);
auto result = cudaq::solvers::vqe(
ansatz{},
H,
*optimizer,
*gradient,
{0.0}, // Initial parameters
{{"verbose", true}}
);
Shot-based Simulation
# Run VQE with finite shots
energy, parameters, data = solvers.vqe(
lambda thetas: ansatz(thetas[0]),
H,
initial_parameters=[0.0],
shots=10000,
max_iterations=10,
verbose=True
)
# Analyze measurement data
for iteration in data:
counts = iteration.result.counts()
print("\nMeasurement counts:")
print("XX basis:", counts.get_register_counts('XX'))
print("YY basis:", counts.get_register_counts('YY'))
print("ZI basis:", counts.get_register_counts('ZI'))
print("IZ basis:", counts.get_register_counts('IZ'))
// Run VQE with finite shots
auto optimizer = cudaq::optim::optimizer::get("lbfgs");
auto gradient = cudaq::observe_gradient::get(
"parameter_shift",
ansatz{},
H
);
auto result = cudaq::solvers::vqe(
ansatz{},
H,
*optimizer,
*gradient,
{0.0},
{
{"shots", 10000},
{"verbose", true}
}
);
// Analyze measurement data
for (auto& iteration : result.iteration_data) {
std::cout << "Iteration type: "
<< (iteration.type == observe_execution_type::gradient
? "gradient" : "function")
<< "\n";
iteration.result.dump();
}
ADAPT-VQE
The Adaptive Derivative-Assembled Pseudo-Trotter Variational Quantum Eigensolver (ADAPT-VQE) is an advanced quantum algorithm that dynamically builds a problem-tailored ansatz based on operator gradients.
Key Features
Dynamic ansatz construction
Gradient-based operator selection
Automatic termination criteria
Support for various operator pools
Compatible with multiple optimizers
Basic Usage
import cudaq
import cudaq_solvers as solvers
# Define molecular geometry
geometry = [
('H', (0., 0., 0.)),
('H', (0., 0., 0.7474))
]
# Create molecular Hamiltonian
molecule = solvers.create_molecule(
geometry,
'sto-3g',
spin=0,
charge=0,
casci=True
)
# Generate operator pool
operators = solvers.get_operator_pool(
"spin_complement_gsd",
num_orbitals=molecule.n_orbitals
)
numElectrons = molecule.n_electrons
# Define initial state preparation
@cudaq.kernel
def initial_state(q: cudaq.qview):
for i in range(numElectrons):
x(q[i])
# Run ADAPT-VQE
energy, parameters, operators = solvers.adapt_vqe(
initial_state,
molecule.hamiltonian,
operators,
verbose=True
)
print(f"Ground state energy: {energy}")
#include "cudaq/solvers/adapt.h"
#include "cudaq/solvers/operators.h"
// compile with
// nvq++ adaptEx.cpp --enable-mlir -lcudaq-solvers
// ./a.out
int main() {
// Define initial state preparation
auto initial_state = [](cudaq::qvector<>& q) __qpu__ {
for (std::size_t i = 0; i < 2; ++i)
x(q[i]);
};
// Create Hamiltonian (H2 molecule example)
cudaq::solvers::molecular_geometry geometry{{"H", {0., 0., 0.}},
{"H", {0., 0., .7474}}};
auto molecule = cudaq::solvers::create_molecule(
geometry, "sto-3g", 0, 0, {.casci = true, .verbose = true});
auto h = molecule.hamiltonian;
// Generate operator pool
auto pool = cudaq::solvers::operator_pool::get(
"spin_complement_gsd");
auto operators = pool->generate({
{"num-orbitals", h.num_qubits() / 2}
});
// Run ADAPT-VQE
auto [energy, parameters, selected_ops] =
cudaq::solvers::adapt_vqe(
initial_state,
h,
operators,
{
{"grad_norm_tolerance", 1e-3},
{"verbose", true}
}
);
}
Advanced Usage
Custom Optimization Settings
# Using L-BFGS-B optimizer with central difference gradients
energy, parameters, operators = solvers.adapt_vqe(
initial_state,
molecule.hamiltonian,
operators,
optimizer='lbfgs',
gradient='central_difference',
verbose=True
)
# Using SciPy optimizer directly
from scipy.optimize import minimize
energy, parameters, operators = solvers.adapt_vqe(
initial_state,
molecule.hamiltonian,
operators,
optimizer=minimize,
method='L-BFGS-B',
jac='3-point',
tol=1e-8,
options={'disp': True}
)
// Using L-BFGS optimizer with central difference gradients
auto optimizer = cudaq::optim::optimizer::get("lbfgs");
auto [energy, parameters, operators] =
cudaq::solvers::adapt_vqe(
initial_state{},
h,
operators,
*optimizer,
"central_difference",
{
{"grad_norm_tolerance", 1e-3},
{"verbose", true}
}
);
Available Operator Pools
CUDA-QX provides several pre-built operator pools for ADAPT-VQE:
spin_complement_gsd: Spin-complemented generalized singles and doubles
uccsd: UCCSD operators
qaoa: QAOA mixer excitation operators
# Generate different operator pools
gsd_ops = solvers.get_operator_pool(
"spin_complement_gsd",
num_orbitals=molecule.n_orbitals
)
uccsd_ops = solvers.get_operator_pool(
"uccsd",
num_orbitals=molecule.n_orbitals,
num_electrons=molecule.n_electrons
)
Algorithm Parameters
ADAPT-VQE supports various configuration options:
grad_norm_tolerance: Convergence threshold for operator gradients
max_iterations: Maximum number of ADAPT iterations
verbose: Enable detailed output
shots: Number of measurements for shot-based simulation
energy, parameters, operators = solvers.adapt_vqe(
initial_state,
hamiltonian,
operators,
grad_norm_tolerance=1e-3,
max_iterations=20,
verbose=True,
shots=10000
)
Results Analysis
The algorithm returns three components:
energy: Final ground state energy
parameters: Optimized parameters for each selected operator
operators: List of selected operators in order of application
# Analyze results
print(f"Final energy: {energy}")
print("\nSelected operators and parameters:")
for param, op in zip(parameters, operators):
print(f"θ = {param:.6f} : {op}")