I am working on an implementation of geometric algebra's. Here is a minimized example of what I have:
template<int p,int q,int r>
struct geometric_algebra{
static const int d=p+q+r, e=1<<d;
struct multivector {
multivector operator|(multivector w){/*...*/}
multivector operator&(multivector w){/*...*/}
/*...*/
};
template<int g> struct graded_vector : multivector {
template<int h>
graded_vector<g+h> operator|(graded_vector<h>w) {
return {multivector::operator|(w)};
}
template<int h>
graded_vector<g+h-d> operator&(graded_vector<h>w) {
return {multivector::operator&(w)};
}
/*...*/
};
};
As you can see the graded_vector specializes a multivector, but does not reimplement anything. The primary reason for its existence is to have more strict type checking. A graded vector corresponds to a certain dimension of geometric object. And I want to use the type checker as a sanity check on these objects
using pga = geometric_algebra<3,0,1>
using point = pga::graded_vector<1>;
using line = pga::graded_vector<2>;
using plane = pga::graded_vector<3>;
plane a = /*...*/
line l = /*...*/
// the intersection of a plane and line is a point
point z = a & l;
line m = a & l; // this should give a compiler error
In conformal geometric algebra (geometric_algebra<4,1,0>) there is a separate distinction between round and flat objects. I also want this distinction to be type-checked.
If I want to continue my current approach, I would need to add the following classes:
flat_vector and flat_graded_vector.
This introduces two problems:
flat_graded_vector inherits multivector twice via graded_vector and flat_vector.graded_vector and flat_vector, it also needs to be overloaded in flat_graded_vector that implements the type logic of both.Is there a better way to achieve this?
Using composition rather than inheritance (as in Diamond inheritance (C++)) does not seem to fit as a multivector is more than the product of whether it is graded and whether it is flat.
I also don't think mix-ins work as that would require function overloading, which is not possible if the signature is different. Furthermore, graded<g,flat<mv>> is conceptually the same as flat<graded<g, mv>> but the type checker will not understand that.
MWE:
template<int p,int q,int r>
struct geometric_algebra{
static const int d=p+q+r, e=1<<d;
struct multivector {
int data; //stand-in
multivector operator|(multivector w){
return {data | w.data}; //stand-in
}
multivector operator&(multivector w){
return {data & w.data}; //stand-in
}
/*...*/
};
template<int g> struct graded_vector : multivector {
template<int h>
graded_vector<g+h> operator|(graded_vector<h>w) {
return {multivector::operator|(w)};
}
template<int h>
graded_vector<g+h-d> operator&(graded_vector<h>w) {
return {multivector::operator&(w)};
}
/*...*/
};
};
using pga = geometric_algebra<3,0,1>;
using multivector = pga::multivector;
using point = pga::graded_vector<1>;
using line = pga::graded_vector<2>;
using plane = pga::graded_vector<3>;
point make_pt(double x,double y,double z){
return {1}; // stand-in
}
int main() {
point u = make_pt(0,0,1);
point v = make_pt(0,1,0);
point w = make_pt(1,0,0);
plane a = u | v | w;
point x = make_pt(0,0,0);
point y = make_pt(1,1,1);
line l = x | y;
point z = a & l;
}
The crucial insight that solved the problem for me was that I did not need inheritance, I needed conversion.
Thank you to @Sebastian and @Jarod42 for inspiration
For reference, here is a MWE:
template<int p,int q,int r>
struct geometric_algebra{
static const int d=p+q+r, e=1<<d;
struct mvprop {
bool graded = false;
int grade = 0;
bool flat = false;
constexpr bool operator<=(const mvprop other) const {
return (graded <= other.graded) && (flat <= other.flat);
}
constexpr mvprop operator|(mvprop other) const {
return {graded && other.graded, grade + other.grade, flat || other.flat};
}
constexpr mvprop operator&(mvprop other) const {
return {graded && other.graded, grade + other.grade - d, flat && other.flat};
}
};
template<mvprop g>
struct multivector {
int data; //stand-in
template<mvprop h>
multivector<g|h> operator|(multivector<h> w){
return {data | w.data}; //stand-in
}
template<mvprop h>
multivector<g&h> operator&(multivector<h> w){
return {data & w.data}; //stand-in
}
template<mvprop h> requires (h <= g)
operator multivector<h>() {return {data};}
/*...*/
};
};
using cga = geometric_algebra<4,1,0>;
using mv = cga::multivector<{false, 0, false}>;
using flat = cga::multivector<{false, 0, true}>;
using rpoint = cga::multivector<{true, 1, false}>;
using dipole = cga::multivector<{true, 2, false}>;
using circle = cga::multivector<{true, 3, false}>;
using sphere = cga::multivector<{true, 4, false}>;
using rpoint_inf = cga::multivector<{true, 1, true}>;
using point = cga::multivector<{true, 2, true}>;
using line = cga::multivector<{true, 3, true}>;
using plane = cga::multivector<{true, 4, true}>;
rpoint make_rpt(double x,double y,double z, double r=0){
return {1}; // stand-in
}
rpoint_inf make_inf() {
return {1}; // stand-in
}
int main() {
sphere s1 = make_rpt(0,0,1) | make_rpt(0,1,0) | make_rpt(1,0,0) | make_rpt(1,1,1);
sphere s2 = make_rpt(1,0,1) | make_rpt(1,1,0) | make_rpt(2,0,0) | make_rpt(2,1,1);
plane a = make_inf() | make_rpt(0,0,0) | make_rpt(1,0,0) | make_rpt(0,1,0);
line l = make_inf() | make_rpt(0,0,0) | make_rpt(0,0,1);
circle c = s1 & s2;
point r = a & l;
dipole d = c & a;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With