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.
Header
#include <math/concepts/algebraic_concepts.hpp>
Header-only. No library linkage required. Internally includes <math/core/traits.hpp>.
Namespace: calx::concepts
Basic Arithmetic Concepts
| Concept | Requirements | Examples |
|---|---|---|
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}$$| Concept | Requirements | Examples |
|---|---|---|
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.
| Concept | Requirements | Examples |
|---|---|---|
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
| Concept | Requirements | Description |
|---|---|---|
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
| Concept | Requirements | Examples |
|---|---|---|
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
| Concept | Requirements | Description |
|---|---|---|
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
| Concept | Requirements | Description |
|---|---|---|
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);
}