sph-ga is a c library for fundamental calculations in euclidean and conformal geometric algebras.
The implementation is designed around compactness, generality, and directness. It aims to stay structurally simple and adaptable, with explicit data structures and straightforward algebraic operations, rather than pushing complexity into elaborate metaprogramming or other aggressive abstraction machinery.
It provides the core operations of geometric algebra in a compact and explicit form:
- geometric, inner, and exterior products
- euclidean and conformal geometric algebra in the same framework
- sparse multivectors with direct blade-level representation
- reverse, involute, conjugate, inverse, and conformal helpers such as points and rotors
String conversion is included so that multivectors can be written concisely in tests and examples.
Here is a project using the javascript version of sph-ga for n-cube rotation.
Build the library and tests:
./exe/compileInstall the library and headers:
./exe/installOr install as symlinks:
./exe/install-symlinkAn optional path prefix can be supplied to install into $prefix/usr/... instead of /usr/....
Include:
#include <sph-ga.h>Then link in the usual way:
cc your_program.c -lsph-gaThe library is centered around a prepared ga_t space instance and explicit ga_mv_t multivectors.
A diagonal euclidean metric in three dimensions:
ga_t r3;
double metric[3];
metric[0] = 1.0;
metric[1] = 1.0;
metric[2] = 1.0;
r3.dimensions = 0;
ga_init_diagonal((&r3), metric, 3, 0);
A conformal space based on the euclidean part [1, 1, 1]:
ga_t c3;
double metric[3];
metric[0] = 1.0;
metric[1] = 1.0;
metric[2] = 1.0;
c3.dimensions = 0;
ga_init_diagonal((&c3), metric, 3, 1);
After use:
ga_uninit((&r3));
ga_uninit((&c3));
A multivector is represented as:
typedef struct {
ga_blade_id_t id;
double coeff;
} ga_term_t;
typedef struct {
ga_term_t *term;
uint64_t term_count;
} ga_mv_t;
Terms are sparse. id is a blade bitset.
A caller provides the storage:
ga_term_t storage[ga_blade_count];
ga_mv_t mv;
mv.term = storage;
mv.term_count = 0;
Scalar and basis blade macros:
ga_term_t storage[ga_blade_count];
ga_mv_t mv;
mv.term = storage;
ga_mv_set_zero((&mv));
ga_mv_set_scalar((&mv), 2.0);
ga_mv_set_basis_blade((&mv), 3, 4.0);
Examples:
- scalar
2 - basis blade
4e3
String conversion is supported for compact construction, tests, and inspection.
ga_term_t storage[ga_blade_count];
ga_mv_t mv;
mv.term = storage;
ga_mv_from_string((&r3), "1 + e2 + 2e1_3", (&mv));
char buffer[ga_mv_string_capacity];
ga_mv_to_string((&r3), (&mv), buffer);
The string layer is trusted-input / trusted-buffer infrastructure. It is not a defensive parser.
Binary operations:
ga_addga_subtractga_ep_onega_gp_onega_ip_one
Unary operations:
ga_reversega_involutega_conjugate
Inverse:
ga_inverse
ga_term_t left_storage[ga_blade_count];
ga_term_t right_storage[ga_blade_count];
ga_term_t out_storage[ga_blade_count];
ga_mv_t left;
ga_mv_t right;
ga_mv_t out;
left.term = left_storage;
right.term = right_storage;
out.term = out_storage;
ga_mv_from_string((&r3), "e1", (&left));
ga_mv_from_string((&r3), "e2", (&right));
ga_gp_one((&r3), (&left), (&right), (&out));
When the space is initialized with is_conformal = 1, these helper functions are available:
ga_noga_niga_pointga_point_euclideanga_rotor
ga_term_t storage[ga_blade_count];
ga_mv_t mv;
char buffer[ga_mv_string_capacity];
mv.term = storage;
ga_no((&c3), 2.0, (&mv));
ga_mv_to_string((&c3), (&mv), buffer); /* 2e4 */
ga_ni((&c3), -3.0, (&mv));
ga_mv_to_string((&c3), (&mv), buffer); /* -3e5 */
ga_term_t storage[ga_blade_count];
ga_mv_t point;
double euclidean[3];
double extracted[3];
char buffer[ga_mv_string_capacity];
point.term = storage;
euclidean[0] = 2.0;
euclidean[1] = 3.0;
euclidean[2] = 4.0;
ga_point((&c3), euclidean, (&point));
ga_mv_to_string((&c3), (&point), buffer); /* 2e1 + 3e2 + 4e3 + e4 + 14.5e5 */
ga_point_euclidean((&c3), (&point), extracted);
For a conformal space based on three euclidean dimensions, the rotor coefficient order is:
- scalar
e1_2e1_3e2_3
Example:
ga_term_t storage[ga_blade_count];
ga_mv_t rotor;
double coeff[4];
char buffer[ga_mv_string_capacity];
rotor.term = storage;
coeff[0] = 2.0;
coeff[1] = 3.0;
coeff[2] = 4.0;
coeff[3] = 5.0;
ga_rotor((&c3), coeff, (&rotor));
ga_mv_to_string((&c3), (&rotor), buffer); /* 2 + 3e1_2 + 4e1_3 + 5e2_3 */
Only functions that can fail return status_t.
The current fallible functions are:
ga_init_fullga_init_diagonalga_addga_subtractga_ep_onega_gp_onega_ip_onega_inverse
Typical pattern:
status_t status;
ga_t r3;
double metric[3];
metric[0] = 1.0;
metric[1] = 1.0;
metric[2] = 1.0;
status = ga_init_diagonal((&r3), metric, 3, 0);
if (status.id) {
return status.id;
}
ga_init_full :: ga metric dimensions is_conformal -> status
ga_init_diagonal :: ga diagonal_metric dimensions is_conformal -> status
ga_uninit :: ga -> void
ga_mv_set_zero :: mv -> macro
ga_mv_set_scalar :: mv value -> macro
ga_mv_set_basis_blade :: mv index value -> macro
ga_mv_from_string :: ga string mv -> void
ga_mv_to_string :: ga mv buffer -> void
ga_add :: ga left right out -> status
ga_subtract :: ga left right out -> status
ga_ep_one :: ga left right out -> status
ga_gp_one :: ga left right out -> status
ga_ip_one :: ga left right out -> status
ga_inverse :: ga in reverse_mv denominator_mv out -> status
ga_reverse :: ga in out -> void
ga_involute :: ga in out -> void
ga_conjugate :: ga in out -> void
ga_no :: ga coeff out -> void
ga_ni :: ga coeff out -> void
ga_point :: ga euclidean_coeff out -> void
ga_point_euclidean :: ga in euclidean_coeff -> void
ga_rotor :: ga coeff out -> void
ga_t exposes these relevant prepared-state fields:
dimensions
is_conformal
is_symmetric
is_diagonal
null_vector_count
null_vector_start
id_null
pseudoscalar_id
no_id
ni_id
no_index
ni_index
metric
Basis indices are 1-based in the human-readable representation:
e1e2e1_3
Blade ids are bitsets:
- scalar:
0 e1:1e2:2e1_2:3
Multivectors are sparse lists of (id, coeff) terms.
With is_conformal = 1, the space extends the euclidean part by two null basis vectors appended at the end.
For euclidean dimension d, the conformal basis order is:
e1, e2, ..., ed, no, ni
The default conformal metric added by the library is:
ei · ei = 1for the euclidean partno · no = 0ni · ni = 0no · ni = -1ni · no = -1
The canonical string form still uses e... names. Input also accepts no and ni as aliases.
Examples of valid components:
3
20.12
e2
20.12e1
e1_4_6
3e1_4_6
no
ni
Examples of valid multivectors:
2 + e1 + e2_3
1 + no + -2ni
Formatting canonicalizes conformal aliases to e... basis names.
The automated tests are in:
src/test/sph-ga.c
They include:
- algebra oracle cases for
gp,ip,ep - involution / reverse / conjugate checks
- parser / formatter roundtrip checks
- conformal helper tests
The implementation is intentionally bounded and explicit:
ga_max_dimensionsfixes the compilation targetga_accumulator_stack_sizebounds internal algebra scratch depthga_tcombines declarative space state with runtime scratch state- concurrent use is achieved by using distinct
ga_tinstances, including copies of prepared instances where desired
This library does not provide:
- operator overloading
- code generation
- symbolic simplification
- graphical functions
- defensive parsing of invalid multivector strings
- automatic diagonalization of conformal expressions into another basis
lgpl3+