C++ reference

Include apxchol.h and link apxchol::core. Start with the usage guide or CMake integration. This page lists the existing public controls; the API policy defines their compatibility guarantees.

Reusable solver

Use solver for both CPU and GPU operation. Its backend is selected during construction and stays fixed. To force CPU use, set opts.backend to solve_backend::cpu.

solve checks the returned solution against the original matrix and RHS. Inspect converged; reaching the iteration limit is not an exception. A warm start is an initial solution estimate supplied through x0.

class solver

Reusable solver owning one CPU or GPU backend, selected once at setup. Owns its input data; movable, noncopyable, and not safe for concurrent calls. Every solve verifies the returned residual against the original operator/RHS. Supplied factors use CPU in automatic mode; an explicit GPU request uploads them.

Public Functions

explicit solver(const Eigen::SparseMatrix<double> &A, const solve_options &opts = {}, checkpoint *cp = nullptr)
solver(const Eigen::SparseMatrix<double> &A, factorization F, const solve_options &opts = {}, checkpoint *cp = nullptr)

Adopt F without rebuilding or regularizing it. lump_max_mass still bounds operator validation; factor storage, dropping and solve options apply.

solver(solver&&) noexcept
solver(const solver&) = delete
solve_result solve(const Eigen::VectorXd &b, double tol = -1.0, int max_iter = -1, const Eigen::VectorXd *x0 = nullptr) const

Solve Ax=b. Negative tol/max_iter use constructor defaults. x0 supplies an optional initial guess; nullptr starts from zero. Inspect converged in the result even when the iteration budget is exhausted.

void solve(const Eigen::VectorXd &b, solve_result &result, double tol = -1.0, int max_iter = -1, const Eigen::VectorXd *x0 = nullptr) const

Write into an existing result, preserving its setup timing records.

solve_result solve(const Eigen::VectorXd &b, Eigen::Ref<Eigen::VectorXd> x, double tol = -1.0, int max_iter = -1, const Eigen::VectorXd *x0 = nullptr) const

Write into caller storage; the returned result leaves x empty.

Eigen::VectorXd apply(const Eigen::VectorXd &r) const

Apply the preconditioner to finite scalar or matrix input.

template<class Input, class Output>
inline batch_solve_result solve(const Eigen::MatrixBase<Input> &B, Eigen::MatrixBase<Output> &X, double tol = -1, int max_iter = -1, const const_matrix_view *X0 = nullptr) const

Independent RHS columns, with bounded panel workspace. Output must not overlap the RHS or warm start. tol/max_iter apply to each column.

void apply(const_matrix_view R, matrix_view Z) const
const factorization &factor() const

Factor metadata; L’s row/value arrays require keep_factor_values=true. The returned reference is owned by this solver.

Eigen::Index rows() const

Operator dimension.

Eigen::Index operator_nnz() const

Stored entries in the original operator, after input canonicalization and before preconditioner lumping or dropping.

solve_backend backend() const

Selected route: cpu or gpu, never automatic.

factor_storage_type factor_storage() const

Installed triangular-solve storage: fp16 or fp32, never automatic.

Solve defaults

These values come directly from the native headers. Python and MATLAB/Octave use them when tolerance and iteration-budget arguments are omitted.

constexpr double apxchol::default_tol = 1e-8
constexpr int apxchol::default_max_iter = 200

Solve options

These defaults apply to each solve. A call may override the tolerance and iteration budget without rebuilding the factor.

struct solve_options

Public Members

double tol = default_tol

Default relative residual tolerance; must be finite and positive.

int max_iter = default_max_iter

Nonnegative default iteration budget; zero only checks the initial guess.

bool keep_factor_values = false

Retain FP32 host factor values for export or adoption by another solver. Costs an additional host copy; false keeps the permutation but releases row/value arrays. Python factorize() enables retention by default.

factor_options factor_opts = {}

Factor construction and triangular-solve storage controls.

solve_backend backend = solve_backend::automatic

Select a complete setup/solve route; execution errors never change it.

int num_threads = 0

CPU thread ceiling for setup and every solve/apply. Positive values override the calling OpenMP task’s setting; 0 inherits it. Small workloads may use fewer threads.

bool collect_timings = true

Record timing checkpoints. Final residual verification always runs.

enum class apxchol::solve_backend

Values:

enumerator automatic
enumerator cpu
enumerator gpu

Results

Scalar solves return the solution in x, unless the caller supplied output storage. Matrix solves write into the caller’s output matrix and return one status record per RHS column.

struct solve_result

Public Members

Eigen::VectorXd x
Eigen::Index iterations = 0
double residual = 0.0

Relative residual checked against the original operator and RHS.

bool converged = false

Whether the checked residual is below the requested tolerance.

Eigen::Index lumped_offdiag = 0

Positive off-diagonal entries moved onto the preconditioner’s diagonal. The original operator is unchanged, including for residual verification.

checkpoint timings
solve_backend backend = solve_backend::cpu
struct batch_solve_result

Public Members

std::vector<solve_result> columns
checkpoint timings

Factor construction and storage

The controls below belong to solve_options::factor_opts or the opts argument of factorize. Storage precision is separate from construction precision and exported values; see the precision guide.

struct factor_options

Public Members

unsigned seed = 42

Seed for factor construction; see the precision guide for repeatability limits.

double regularization = 0

Nonnegative diagonal regularization, as a fraction of each input diagonal. Zero disables it; only the preconditioner changes.

double lump_max_mass = kDefaultLumpMassCeiling

Allowed positive off-diagonal mass fraction in [0, 1]. Zero rejects it. Accepted positive entries are moved to the preconditioner’s diagonal.

factor_storage_type factor_storage = factor_storage_type::automatic

Triangular-solve storage; construction arithmetic and exports are unchanged. automatic uses the backend default (CPU/Metal fp32, CUDA fp16).

double drop_tolerance = 1e-4

Relative off-diagonal drop threshold in [0, 1]; zero disables dropping.

bool verbose = false

Print setup and solve diagnostics.

partition_options partition = {}

Candidate selection controls for the chosen partitioner.

std::string is_select = "block_greedy"

Built-in partitioner name used by runtime dispatch.

clique_sampler sampler = clique_sampler::gks

Clique-sampling algorithm for factor construction.

enum class apxchol::factor_storage_type

Values:

enumerator automatic
enumerator fp16
enumerator fp32
enum class apxchol::clique_sampler

Values:

enumerator gks
enumerator trace_cycle

Candidate selection

The partitioner chooses independent vertices for elimination. Its degree controls live in factor_options::partition.

struct partition_options

Selection options for built-in partitioners, passed as opts.partition.

Public Members

double degree_quantile = degree_quantile_by_route

Lowest-degree fraction admitted as selection candidates, including ties. Use -1 for the backend default (CPU/Metal 0.2, CUDA 0.5), or a value in [0, 1). Zero uses degree_multiplier instead. Metal requires (0, 1) and rounds the cutoff to its degree-bucket boundary.

double degree_multiplier = 2.0

Positive degree cap multiplier when degree_quantile is zero: admit degrees at most degree_multiplier times the average degree.

bool degree_tiebreak = true

Prefer lower-degree candidates in conflicts. block_greedy breaks degree ties by index; priority_greedy uses its hashed priority to break ties.

Advanced controls

These change scheduling, how factorization finishes the remaining graph, or sampling rules. Keep their defaults unless comparing a specific alternative. GPU construction supports a subset; see backend selection.

cpu_sptrsv_schedule apxchol::factor_options::cpu_schedule = cpu_sptrsv_schedule::automatic

CPU triangular-solve schedule; automatic chooses the installed schedule.

double apxchol::factor_options::min_is_fraction = 0.05

Base selection-yield threshold before switching to Baumann-Kyng rounds. Measured as selected regions divided by degree-eligible vertices; dense residuals may use a higher threshold. Zero disables yield-based handoff, but a round with no progress always hands off.

size_t apxchol::factor_options::omp_threshold = 2000

Work threshold for parallel selection and elimination. Smaller rounds may use fewer threads; changing this can change the constructed factor.

size_t apxchol::factor_options::parallel_residual_threshold = std::numeric_limits<size_t>::max()

Residual size at which Baumann-Kyng rounds switch to residual_peel. SIZE_MAX uses the partitioner’s default: 500 for block_greedy and priority_greedy. A small serial tail limits fill.

residual_peel_strategy apxchol::factor_options::residual_peel = residual_peel_strategy::natural

Serial pivot order: natural visits the supplied order; min_degree uses a lazy heap (not exact current minimum degree); bk_serial samples sqrt(active) vertices with replacement and picks the lowest live degree.

size_t apxchol::factor_options::exact_clique_max_degree = 0

Emit an exact Schur clique for degrees at most this value; zero disables it. Intended for small degrees because it adds d(d-1)/2 edges. GPU factor construction requires zero.

size_t apxchol::factor_options::exact_core_max_h = exact_core_by_route

Trace-cycle exact-clique limit for near-equal heavy neighbors; zero disables it. exact_core_by_route selects 4 on CPU or 0 on GPU. Other CPU samplers ignore it; GPU construction requires zero.

size_t apxchol::factor_options::double_cycle_min_h = 0

Trace-cycle rule: with at least this many near-equal heavy neighbors, emit two half-weight cycles unless the exact-core rule applies. Zero disables it; GPU construction requires zero. Expected pair weights are preserved.

enum class apxchol::cpu_sptrsv_schedule

CPU triangular-solve scheduling: choose automatically or use levels only.

Values:

enumerator automatic
enumerator levels
enum class apxchol::residual_peel_strategy

Pivot order for the serial tail after parallel elimination and BK handoff.

Values:

enumerator natural
enumerator min_degree
enumerator bk_serial

Constructing and reusing a factor

factorize(A, opts) constructs a factor on CPU and retains its host arrays. Custom partitioner and eliminator overloads are described in the extension guide.

factorization apxchol::factorize(const Eigen::SparseMatrix<double> &L, const factor_options &opts = {}, checkpoint *cp = nullptr)

Matrix-level factorization using the built-in AoS graph and named partitioner.

struct factorization

Approximate Cholesky preconditioner: P^T L L^T P approximates the operator used for construction, including any lumping or regularization.

Public Members

sparse_csc L
std::vector<node_index> perm
bool sddm = false
std::vector<round_stats> rounds

L stores FP32 lower-triangular columns in compressed sparse column (CSC) form, with each diagonal first. perm[original_vertex] gives its elimination position. Keep L, the permutation, operator kind and round metadata together when handing a factor to another solver.

A solver can also retain a factor built on CUDA or Metal: enable keep_factor_values before construction, then read solver.factor(). Pass a copy to solver(A, F, opts) to reuse it. Automatic adoption uses CPU; set opts.backend = solve_backend::gpu to upload it to a GPU. The original matrix A is still needed for PCG and residual checks. For cross-process reuse, save the operator and factor together.

void apxchol::save_solver(const std::filesystem::path &path, const solver &owner)

Save the solver’s original operator and retained FP32 factor in a portable binary file. Creates a new file; an existing destination is an error. Factor retention must have been enabled at construction. No extra matrix is kept between saves.

solver apxchol::load_solver(const std::filesystem::path &path, const solve_options &options = {})

Restore a solver using the saved operator and factor. Backend, storage and solve options apply as with solver(A, F, options); automatic adoption uses CPU.

Eigen integration

apxchol::apx_cholesky is apxchol’s CPU preconditioner for Eigen solvers:

using SpMat = Eigen::SparseMatrix<double>;
Eigen::ConjugateGradient<SpMat, Eigen::Lower, apxchol::apx_cholesky> cg;
cg.compute(A);
Eigen::VectorXd x = cg.solve(b);

Eigen controls the outer iteration and its stopping test. Use this integration when an Eigen solver is needed; use apxchol::solver for apxchol’s complete CPU/GPU solve and final residual verification.

Preconditioner operation

Purpose

set_options(opts)

Set factor construction/storage options before setup

compute(A)

Construct and prepare a CPU factor

set_factor(F)

Prepare an existing host factor without reconstructing it

solve(b)

Apply the prepared preconditioner, not a complete PCG solve

set_keep_factor(true)

Retain exportable values; call before compute or set_factor

factor()

Read the retained factor and metadata

rows(), cols(), info()

Query dimensions and Eigen setup status

CPU-specific owner

cpu_solver remains available for code that needs its direct preconditioner() accessor. It provides the same solve/apply operations as solver, but always runs on CPU. Prefer solver(A, opts) with opts.backend = solve_backend::cpu for ordinary use.

class cpu_solver

CPU-specific reusable solver, also used by the generic solver’s CPU route. Prefer solver with solve_backend::cpu unless direct preconditioner access is needed. Setup and solves stay on CPU, including in GPU-enabled builds. The optional checkpoint receives setup timings and is not retained.

Public Functions

explicit cpu_solver(const Eigen::SparseMatrix<double> &L, const solve_options &opts = {}, checkpoint *cp = nullptr)
cpu_solver(const Eigen::SparseMatrix<double> &L, factorization F, const solve_options &opts = {}, checkpoint *cp = nullptr)

Adopt an externally computed factorization (e.g. from a factorize() call with a custom eliminator or partitioner) instead of factorizing internally. L is still required — it is the PCG operator. Construction options (including regularization/lumping) are not reapplied; factor storage, dropping, scheduling and solve options still apply. L is interpreted through its lower triangle. The caller must ensure it is a valid operator compatible with F; use solver(L, F) for validation.

inline const apx_cholesky &preconditioner() const

One-shot solve and availability

solve_result apxchol::solve(const Eigen::SparseMatrix<double> &L, const Eigen::VectorXd &b, const solve_options &opts = {})

One-shot solve: factorize L, then solve Lx = b via PCG. Automatic selects GPU when the compiled backend is available and compatible, otherwise CPU. Explicit GPU selects device-owned CUDA or Metal setup and solve, according to the build. Unsupported inputs and execution failures are errors, never a CPU retry.

bool apxchol::backend_available(solve_backend backend) noexcept

CPU/automatic are always available without querying a device. GPU requires a usable device and compatible compiled kernels; options may still be unsupported.