Skip to main content

Scientific Computing

This part carries the mathematics of the previous parts through to implementations whose cost and accuracy are both stated.

What distinguishes this part​

The entries in Mathematics state what is to be computed; the entries here state how, at what cost, and with what error. Each develops a kernel to the point where its operation count, its memory traffic, its arithmetic intensity and its backward error bound are all available, and where the interface it presents is the one used by the rest of the reference.

Two questions recur and are answered explicitly in every entry.

The first is whether the kernel is compute-bound or memory-bound, since this determines what optimisation is worth attempting. Dense factorizations restructured around matrix–matrix products are compute-bound; sparse operator applications and explicit time stepping are memory-bound at every size, and no arrangement of the arithmetic changes that.

The second is what the computed result approximates. A backward-stable factorization solves a nearby problem exactly, and the accuracy of the answer is that fact multiplied by the conditioning. Reporting a residual as though it were an error is the standard mistake, and these entries state which is which.

Entries​

EntrySubject
Numerical Linear AlgebraLU, Cholesky, QR, eigenvalue and least-squares problems
Sparse ComputationStorage formats, sparse products, ordering, sparse direct methods
Numerical IntegrationQuadrature rules, adaptivity, high-dimensional integration
Differential EquationsInitial-value problems, stability, stiffness, step control
Partial Differential EquationsDiscretisation, stencils, solvers, and their performance characteristics
Automatic DifferentiationForward and reverse mode, cost, implementation by operator overloading
OptimizationImplementation of descent, quasi-Newton and trust-region methods

The common interface​

Every kernel in this part takes non-owning views and caller-supplied workspace:

#include <mdspan>
#include <span>

namespace hpc {

// Storage is the caller's; the routine allocates nothing.
// Layout is a compile-time parameter; extents are runtime values.
template <class Layout>
void factorize(std::mdspan<double, std::dextents<std::size_t, 2>, Layout> A,
std::span<std::size_t> pivots,
std::span<double> workspace) noexcept;

} // namespace hpc

The convention has three consequences: a routine can be called on a sub-block without copying, it can be used where allocation is prohibited, and its working-set size appears in its signature rather than in its documentation.