Concepts — Algebraic Concepts

Overview

The calx::concepts namespace defines the major structures of abstract algebra as C++20 concepts. Groups, rings, fields, vector spaces, Hilbert spaces, and more can be verified at compile time, enabling explicit type constraints on template arguments.

  • Compile-time type verification — clear error messages when template arguments do not satisfy algebraic requirements
  • Hierarchical design — AdditiveMonoid → AdditiveGroup → Ring → Field mirrors the mathematical inclusion hierarchy
  • Over 40 concepts — covering basic arithmetic, algebraic structures, vector spaces, linear maps, numeric types, matrices/tensors, and algebras/modules
  • Header-only — no linking required; just #include
  • C++23 extensions — additional concepts like SparseMatrixOf, BandMatrixOf, RealField, ComplexField (C++23 mode)

Mathematical axioms (associativity, commutativity, distributivity, etc.) cannot be verified at compile time. Only syntactic requirements (operator existence, return type compatibility) are checked. Semantic correctness is the user's responsibility.

Basic Arithmetic Concepts

ConceptRequirementsExamples
HasBasicArithmetic<T> +, -, *, /, unary - defined double, float, Int, Float, Rational, Complex<double>
Comparable<T> ==, !=, <, <=, >, >= defined double, int, Int, Float, Rational
EqualityComparable<T> ==, != only Complex<double>, Quaternion<double>, all numeric types
HasMathConstants<T> T::pi(), T::e() defined Float. float/double do not have static methods pi()/e() and therefore do not satisfy this concept. Intended for custom types such as calx::Float

Algebraic Structures

The following inclusion relationships hold:

$$\text{AdditiveMonoid} \subset \text{AdditiveGroup} \subset \text{AdditiveAbelianGroup}$$ $$\text{Ring} = \text{AdditiveAbelianGroup} \cap \text{MultiplicativeMonoid}$$ $$\text{Ring} \subset \text{CommutativeRing} \subset \text{IntegralDomain} \subset \text{Field} \subset \text{OrderedField}$$
ConceptRequirementsExamples
AdditiveMonoid<T> a + b, T{0} (additive identity) int, float, double, unsigned int, Int, Float
AdditiveGroup<T> AdditiveMonoid + unary -a, a - b int, float, double, Int, Float
AdditiveAbelianGroup<T> AdditiveGroup (commutativity is a semantic marker) int, float, double, Int, Float
MultiplicativeMonoid<T> a * b, T{1} (multiplicative identity) int, float, double, Int, Float
MultiplicativeGroup<T> MultiplicativeMonoid + T{1} / a (multiplicative inverse) float, double, Float, Rational, Complex<double>
MultiplicativeAbelianGroup<T> MultiplicativeGroup (commutativity is a semantic marker) float, double, Float, Rational
Ring<T> AdditiveAbelianGroup + MultiplicativeMonoid int, float, double, Int, Float, Rational
CommutativeRing<T> Ring (multiplicative commutativity is a semantic marker) int, float, double, Int, Float, Rational
IntegralDomain<T> CommutativeRing (no zero divisors is a semantic marker) int, float, double, Int, Float, Rational
DivisionRing<T> Ring + MultiplicativeGroup (includes non-commutative division rings) float, double, Quaternion<double>, Complex<double>
Field<T> +, -, *, /, unary -, T(0), T(1) double, float, Float, Rational, Complex<double>
OrderedField<T> Field + <, <=, >, >= double, float, Float, Rational
Scalar<T> Field<T> || std::integral<T> int, double, Float, Rational

Note on unsigned types: Unsigned integer types such as unsigned int satisfy AdditiveMonoid, but the additive inverse (-a) is not mathematically closed for unsigned types. Therefore, they do not satisfy AdditiveGroup or any higher algebraic structure (Ring, Field, etc.).

About "semantic markers": Mathematical properties such as commutativity, completeness, and the absence of zero divisors cannot be verified at compile time. These concepts function as declarations of intent (markers). No runtime checks are performed; it is the user's responsibility to ensure the type satisfies the stated property.

Vector Spaces

Vector spaces and their extensions. The scalar type S defaults to double.

ConceptRequirementsExamples
VectorSpace<V, S> v + w, v - w, s * v, v * s, -v Vector<double>, std::array<double, N>
InnerProductSpace<V, S> VectorSpace + inner_product(v, w) → S Vector<double> (when inner_product is defined)
NormedVectorSpace<V, S> VectorSpace + norm(v) → S Vector<double> (when norm is defined)
BanachSpace<V, S> NormedVectorSpace (completeness is a semantic marker — completeness cannot be verified at compile time, so this concept serves as a declaration of intent. No runtime checks are performed) $\mathbb{R}^n$, $\ell^p$ spaces
HilbertSpace<V, S> InnerProductSpace (completeness is a semantic marker — completeness cannot be verified at compile time, so this concept serves as a declaration of intent. No runtime checks are performed) $\mathbb{R}^n$, $\ell^2$ spaces
FiniteDimensionalVectorSpace<V, S> VectorSpace + v.size() → size_t Vector<double>

Linear Maps

ConceptRequirementsDescription
LinearMap<F, V1, V2, S> f(v) → V2, f(v + w) → V2, f(s * v) → V2 $f(\alpha v + \beta w) = \alpha f(v) + \beta f(w)$. V1, V2 must be VectorSpace
ConjugateLinearMap<F, V1, V2, S> Same syntactic requirements as LinearMap $f(\alpha v) = \bar{\alpha} f(v)$. Conjugate homogeneity cannot be verified at compile time

Numeric Type Concepts

ConceptRequirementsExamples
Numeric<T> HasBasicArithmetic + std::abs, std::max, std::min short, int, float, double, unsigned int
IntegerType<T> std::integral<T> && Ring<T>. Ring requires numeric_traits specialization int, long long. short does not satisfy this concept because it lacks a numeric_traits specialization. Unsigned types do not satisfy the AdditiveGroup requirement
NonNegativeIntegerType<T> std::unsigned_integral<T> unsigned int, size_t, uint64_t, etc. Use for array indices, sizes, and counters where non-negativity is guaranteed. Does not satisfy Ring or higher because additive inverse is not closed
FloatingPointType<T> OrderedField<T> && !std::integral<T> float, double, long double, calx::Float
ComplexType<T> T::value_type, std::real, std::imag, std::abs, std::arg, std::conj std::complex<double>, std::complex<float>
QuaternionType<T> T::value_type, q.w, q.x, q.y, q.z, q.conj(), q.norm(), q.inverse() Quaternion<double>, Quaternion<float>
RationalType<T> Field<T> + r.numerator(), r.denominator() Rational

Matrix & Tensor Concepts

ConceptRequirementsDescription
MatrixOf<M, T> M::value_type, m(i,j) → T, m.rows(), m.cols() Any matrix type. Defined in traits.hpp
VectorOf<V, T> V::value_type, v[i] → T, v.size() Any vector type. Defined in traits.hpp
SquareMatrixOf<M, T> MatrixOf + m.is_square() Square matrix
SymmetricMatrixOf<M, T> SquareMatrixOf (symmetry is a semantic marker) Symmetric matrix. Requires runtime verification
OrthogonalMatrixOf<M, T> SquareMatrixOf (orthogonality is a semantic marker) Orthogonal matrix ($M^T M = I$)
TensorOf<T, S> t.rank(), t.shape(i), t.size(), t(i,j) → S Multi-dimensional tensor
SparseMatrixOf<M, T> C++23 MatrixOf + m.non_zeros() Sparse matrix
BandMatrixOf<M, T> C++23 MatrixOf + m.lower_bandwidth(), m.upper_bandwidth() Band matrix

Algebras & Modules

ConceptRequirementsDescription
Module<M, R> AdditiveAbelianGroup<M> + Ring<R> + r * m, m * r Module (generalization of vector space where scalars form a ring, not a field)
Algebra<A, F> VectorSpace<A,F> + Ring<A> + f * (a * b) Algebra (vector space with multiplicative structure)
AssociativeAlgebra<A, F> Algebra (associativity is a semantic marker) Associative algebra, e.g. matrix rings
LieAlgebra<L, F> VectorSpace<L,F> + a * b → L (Lie bracket) Lie algebra, e.g. $\mathfrak{so}(3)$, $\mathfrak{se}(3)$

Examples

Generic function constrained by Field

#include <math/concepts/algebraic_concepts.hpp>
using namespace calx::concepts;

// Inverse computation for any type satisfying Field
template<Field T>
T safe_inverse(const T& x) {
    if (x == T(0)) return T(0);  // avoid division by zero
    return T(1) / x;
}

// Works with double, Float, Rational, Complex<double>
auto inv_d = safe_inverse(3.14);
auto inv_r = safe_inverse(Rational(3, 7));

Vector space algorithm

// Linear combination for any type satisfying VectorSpace
template<typename V, typename S>
    requires VectorSpace<V, S>
V linear_combination(S a, const V& v, S b, const V& w) {
    return a * v + b * w;
}

Polynomial evaluation over a Ring

// Horner's method for any type satisfying Ring
template<Ring T>
T horner(const std::vector<T>& coeffs, const T& x) {
    T result = T{0};
    for (auto it = coeffs.rbegin(); it != coeffs.rend(); ++it) {
        result = result * x + *it;
    }
    return result;
}

// Works with int, double, Int, Float, Rational
std::vector<double> poly = {1.0, -2.0, 3.0}; // 3x^2 - 2x + 1
double val = horner(poly, 2.0); // = 9.0

Quaternion type detection

// Combining QuaternionType and DivisionRing constraints
template<typename T>
    requires QuaternionType<T> && DivisionRing<T>
auto rotation_axis(const T& q) {
    auto v_norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z);
    return std::make_tuple(q.x / v_norm, q.y / v_norm, q.z / v_norm);
}

Concept-based overloading

// OrderedField: field with ordering (double, Float, Rational)
template<OrderedField T>
T clamp(const T& x, const T& lo, const T& hi) {
    if (x < lo) return lo;
    if (x > hi) return hi;
    return x;
}

// Field: includes non-orderable fields (Complex<double>)
template<Field T>
T midpoint(const T& a, const T& b) {
    return (a + b) / T(2);
}