Expand description
$V_{EE}$ – Vector Expression Emitter: Geometric Algebra Code Generator
The goal of this crate is to generate optimized code for geometric algebra flavors. Currently,
this crate implements the symbolic reduction of multivector expressions up to polynomials with
rational coefficients. In contrast, rational polynomials and hence polynomial division is not
required for lower dimensional geometric algebra flavors as the inverse of a multivector is
given by multiplying it with the inverse of its mixed-grade norm realizing a Study number for
dimensions $D < 6$, i.e., a generalized complex number.1 See the contents
where the symbolic expressions are generated in text, code, list, and tree form. For evaluating
symbols as rationals, see the code form in Rust mode example. For
exporting expressions to Scheme or egglog, see the list form in ASCII mode examples.
Currently, the plane-based pistachio flavor – Projective Geometric Algebra (PGA) – is
implemented for $D \equiv N + 1 \le 8$ in all three metrics, i.e., elliptic, hyperbolic, and
parabolic (Euclidean).2
- The PGAs with $
N < 2$ are gated behind therudimentaryfeature as both lack rotations. - The PGAs with $
N > 4$ are gated behind theexploratoryfeature as there are no inverses based on Study numbers.
Both features provide dimension-agnostic insights regarding duality and the choice of basis
blades. Additionally, the latter feature explores grade-preserving conditions among
orthonormalization conditions. The PGA is especially of interest for computer graphics (e.g.,
game and physics engines) as it is the most compact flavor (i.e., a one-up flavor) unifying the
established but scattered frameworks, e.g., homogeneous coordinates, Plücker coordinates, (dual)
quaternions, and screw theory. Even without any knowledge of geometric algebra, an API can be
more intuitive as it unifies the positional and directional aspects of geometric entities (e.g.,
planes, lines, points) and the linear and angular aspects of rigid-body dynamics in a
dimension-agnostic as well as metric-agnostic way with closed-form (i.e., non-iterative)
solutions up to 4D (e.g., PgaP2, PgaP3, PgaP4).3
§Contents
- Symbolic Engine
- Multivector Operators
- Text Form Arguments
- Code Form Arguments
- List Form Arguments
- Tree Form Arguments
§Symbolic Engine
The symbolic engine uses BTreeMap as storage container such that the canonical form of the
Multivector/Polynomial/Monomial hierarchy over Symbol is structurally enforced
by the choice of keys and values. Therefore, the non-volatile BTreeMap fields are public.
The canonical forms of non-zero Rational coefficients and non-zero Integer exponents are
behaviourally enforced by internally finalizing operators with Rational::reduce() and
excluding zero with the None variant of Option<Rational> and
Option<Integer>. Therefore, the volatile numerator/denominator field of
Rational and the volatile i64 field of Integer are private.
§Features
- Zero non-optional dependencies.
- Uniquely reduce symbolic multivector expressions for algebraic and structural equivalence to coincide.
- Generate text form in Unicode/ASCII/$
\LaTeX$ mode. - Generate code form in generic/Rust mode.
- Generate list form (i.e., s-expressions, Scheme, or
egglog) in Unicode/ASCII mode. - Generate tree form (i.e., DOT graphs as in
text/vnd.graphviz) in Unicode/ASCII mode. - Eliminate orthonormalization conditions from expressions using reflection/projection operator by factoring pinned symbols, GCD coefficients, and predominant signs.
- Evaluate symbols as rationals.
- Count operations (i.e., multiplications and additions).
- Define the metric-agnostic basis, i.e., elliptic, hyperbolic, and parabolic (Euclidean)
along with the multivector entities for dimensions $
D \equiv N + 1 \le 8$ of the plane-based pistachio flavor, i.e., projective geometric algebra (PGA).
§Roadmap
- Simplify emitter by flattening expression tree into token stream and perform defer logic during stream iteration rather than tree traversal.
- Explore
egglogto further optimize expressions to reduce operation count by domain-specific common subexpression elimination (CSE) targeting exterior products. Reduce search space by leveraging applicable geometric decomposition, e.g., Euclidean decomposition for parabolic reflection operator. If promising, import expressions back. - Generate expressions in SIMD mode.
- Define other geometric algebra flavors.
§Multivector Operators
Following table lists some common operators shared between flavors. The first three and the
omitted even-grade operators (e.g., square root, exponential, logarithm, power) must be manually
implemented based on Study numbers (i.e., generalized complex numbers) whereas the expressions
for the remaining ones are generated based on Multivector leveraging its respective symbolic
operator implementations. Otherwise, Multivector::norm_squared() is the closest offered and
Multivector::unit() is a marker, affecting only the multi-plane reflection operator (i.e,
the group conjugation or the sandwich product) and the projection operator inclusive rejection.
The table suggests the mixed-grade selection B::from(a) which is applicable whenever the
target implementation supports distinct types B for blades and versors rather than one
multivector type for all. Symbolic grade selection is supported with Multivector::grade()
and Multivector::vector() or with their mixed-grade complements Multivector::grades()
and Multivector::vectors().
\gdef\norm{
\| a \| \equiv \sqrt{a \tilde a}
}
\gdef\unit{
\hat a \equiv \dfrac{a}{\| a \|}
}
\gdef\inv{
a^{-1} \equiv \dfrac{\tilde a}{\| a \|^2}
}
\gdef\rev{
\tilde a \equiv \sum_s (-1)^{s \choose 2} \lang a \rang_s
}
\gdef\pol{
a^{\perp} \equiv a\I
}
\gdef\not{
a^* \equiv \sum_s \lang a \rang_s^*
: \lang a \rang_s^* = \sum_i \alpha_i a_i^*
: a_i a_i^* = \I
}
\gdef\unnot{
a_* \equiv a^{***} \therefore (a^*)_* = a^{****} = a
}
\gdef\neg{
-a \equiv (-1)a
}
\gdef\add{
a + b \equiv \sum_s \lang a \rang_s + \sum_t \lang b \rang_t
}
\gdef\sub{
a - b \equiv \sum_s \lang a \rang_s - \sum_t \lang b \rang_t
}
\gdef\mul{
ab \equiv \sum_{s,t} \lang a \rang_s \lang b \rang_t
}
\gdef\div{
\dfrac{a}{b} \equiv ab^{-1}
}
\gdef\rem{
a \times b \equiv \frac{1}{2}(ab - ba)
}
\gdef\bitor{
a \mid b \equiv \sum_{s,t}
\lang
\lang a \rang_s
\lang b \rang_t
\rang_{|s - t|}
}
\gdef\bitxor{
a \wedge b \equiv \sum_{s,t}
\lang
\lang a \rang_s
\lang b \rang_t
\rang_{s + t}
}
\gdef\bitand{
a \vee b \equiv {(a^* \wedge b^*)}_*
}
\gdef\shl{
a \looparrowleft b
\equiv (-1)^{\prod_{s,t} st} ba \tilde b
}
\gdef\shr{
a \curvearrowright b \equiv (a \mid b) \tilde b
}
\gdef\from{
\lang a \rang_b \equiv \sum_{s \in \{t|b = \sum_t \lang b \rang_t\}} \lang a \rang_s
}| Operator | Name | Formula |
|---|---|---|
a.norm() | Norm (mixed grade) | $\norm$ |
a.unit() | Unit (orthonormal) | $\unit$ |
a.inv() | Inverse | $\inv$ |
a.rev() | Reverse | $\rev$ |
a.pol() | Polarity | $\pol$ |
!a | Dual (right complement) | $\not$ |
!!!a | Undual (left complement) | $\unnot$ |
-a | Negation (orientation) | $\neg$ |
B::from(a) | Selection (mixed grade) | $\from$ |
a + b, a += b | Sum | $\add$ |
a - b, a -= b | Difference | $\sub$ |
a * b, a *= b | Product (geometric) | $\mul$ |
a / b, a /= b | Quotient (geometric) | $\div$ |
a << b, a <<= b | Reflection ($a$ by $b$) | $\shl$ |
a >> b, a >>= b | Projection ($a$ onto $b$) | $\shr$ |
a % b | Commutator | $\rem$ |
a | b | Contraction (symmetric) | $\bitor$ |
a ^ b | Meet (progressive) | $\bitxor$ |
a & b | Join (regressive) | $\bitand$ |
§Text Form in Unicode Mode
Following example generates the expression for rotating and/or translating a point in PgaP3,
i.e., the type alias of Multivector parameterized for the Parabolic (Euclidean) 3D PGA. The
PgaP3::pin() method pins symbols of PgaP3::point() with the combining x below (i.e.,
the Unicode combining diacritical mark "◌͓") to distinguish them from the symbols of
PgaP3::motor(). This isometry (i.e., up to a screw motion) is isomorphic to the
transformation of a homogeneous point by a dual quaternion. When importing type aliases, the
examples rename the multivector type as Vee and its basis blade type as Bee, with the former
being parameterized by the latter. While the examples use the format_eq! macro to serve as
unit tests by asserting against the array concatenation, the standard format! or print!
macros work as well.
use vee::{PgaP3 as Vee, Symbol, format_eq, pga::PgaP3 as Bee};
// Assumes motor is not orthonormalized.
format_eq!(Vee::point().pin() << Vee::motor(), [
"+(+vv+xx+yy+zz)w͓e123",
"+(+(+vv+xx-yy-zz)X͓+2(+vz+xy)Y͓+2(-vy+xz)Z͓+2(-Vx-Xv-Yz+Zy)w͓)e032",
"+(+2(-vz+xy)X͓+(+vv-xx+yy-zz)Y͓+2(+vx+yz)Z͓+2(-Vy+Xz-Yv-Zx)w͓)e013",
"+(+2(+vy+xz)X͓+2(-vx+yz)Y͓+(+vv-xx-yy+zz)Z͓+2(-Vz-Xy+Yx-Zv)w͓)e021",
]);
// Assumes motor is orthonormalized.
format_eq!(Vee::point().pin() << Vee::motor().unit(), [
"+w͓e123",
"+(+(+1-2yy-2zz)X͓+2(+vz+xy)Y͓+2(-vy+xz)Z͓+2(-Vx-Xv-Yz+Zy)w͓)e032",
"+(+2(-vz+xy)X͓+(+1-2xx-2zz)Y͓+2(+vx+yz)Z͓+2(-Vy+Xz-Yv-Zx)w͓)e013",
"+(+2(+vy+xz)X͓+2(-vx+yz)Y͓+(+1-2xx-2yy)Z͓+2(-Vz-Xy+Yx-Zv)w͓)e021",
]);
// Assumes motor and point are (ortho)normalized where point has positive orientation.
format_eq!(Vee::point().eval([(Bee::e123(), 1)]).pin() << Vee::motor().unit(), [
"+e123",
"+(+2(-Vx-Xv-Yz+Zy)+(+1-2yy-2zz)X͓+2(+vz+xy)Y͓+2(-vy+xz)Z͓)e032",
"+(+2(-Vy+Xz-Yv-Zx)+2(-vz+xy)X͓+(+1-2xx-2zz)Y͓+2(+vx+yz)Z͓)e013",
"+(+2(-Vz-Xy+Yx-Zv)+2(+vy+xz)X͓+2(-vx+yz)Y͓+(+1-2xx-2yy)Z͓)e021",
]);
// The `eval()` method accepts `Into<Symbol>` which is implemented for `(Symbol, Bee)`.
assert_eq!(
const { Bee::e123() },
const { (Symbol::new('w', "e123"), Bee::new("e123").unwrap()) },
);The symbols are assigned to basis blades such that lowercase symbols are dual to their
corresponding uppercase symbols. For blades containing $\e_0$, uppercase symbols are used. The
PgaP3::swp() method swaps lowercase and uppercase symbols. This is useful for testing
duality equivalences.
use vee::{PgaP3 as Vee, format_eq};
format_eq!(Vee::plane(), ["+We0", "+xe1", "+ye2", "+ze3"]);
format_eq!(Vee::point(), ["+we123", "+Xe032", "+Ye013", "+Ze021"]);
assert_ne!(!Vee::plane(), Vee::point());
assert_eq!(!Vee::plane(), Vee::point().swp());Optional plus signs are skipped with "{:<}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:<}", Vee::point().pin() << Vee::motor().unit(), [
"w͓e123",
"+((1-2yy-2zz)X͓+2(vz+xy)Y͓+2(-vy+xz)Z͓+2(-Vx-Xv-Yz+Zy)w͓)e032",
"+(2(-vz+xy)X͓+(1-2xx-2zz)Y͓+2(vx+yz)Z͓+2(-Vy+Xz-Yv-Zx)w͓)e013",
"+(2(vy+xz)X͓+2(-vx+yz)Y͓+(1-2xx-2yy)Z͓+2(-Vz-Xy+Yx-Zv)w͓)e021",
]);The predominant sign is factored as well with "{:-}":
use vee::{PgaP3 as Vee, format_eq};
// Unfactored predominant sign.
format_eq!(Vee::point().pin() << Vee::motor(), [
"+(+vv+xx+yy+zz)w͓e123",
"+(+(+vv+xx-yy-zz)X͓+2(+vz+xy)Y͓+2(-vy+xz)Z͓+2(-Vx-Xv-Yz+Zy)w͓)e032",
"+(+2(-vz+xy)X͓+(+vv-xx+yy-zz)Y͓+2(+vx+yz)Z͓+2(-Vy+Xz-Yv-Zx)w͓)e013",
"+(+2(+vy+xz)X͓+2(-vx+yz)Y͓+(+vv-xx-yy+zz)Z͓+2(-Vz-Xy+Yx-Zv)w͓)e021",
// ^^^^^^^^^^^^^^^^^^
]);
// Factored predominant sign.
format_eq!("{:-}", Vee::point().pin() << Vee::motor(), [
"+(+vv+xx+yy+zz)w͓e123",
"+(+(+vv+xx-yy-zz)X͓+2(+vz+xy)Y͓+2(-vy+xz)Z͓-2(+Vx+Xv+Yz-Zy)w͓)e032",
"+(+2(-vz+xy)X͓+(+vv-xx+yy-zz)Y͓+2(+vx+yz)Z͓-2(+Vy-Xz+Yv+Zx)w͓)e013",
"+(+2(+vy+xz)X͓+2(-vx+yz)Y͓+(+vv-xx-yy+zz)Z͓-2(+Vz+Xy-Yx+Zv)w͓)e021",
// ^^^^^^^^^^^^^^^^^^
]);The factorization is skipped with "{:+}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:+}", Vee::point().pin() << Vee::motor(), [
"+(+vvw͓+w͓xx+w͓yy+w͓zz)e123",
"+(-2Vw͓x-2Xvw͓+X͓vv+X͓xx-X͓yy-X͓zz-2Yw͓z+2Y͓vz+2Y͓xy+2Zw͓y-2Z͓vy+2Z͓xz)e032",
"+(-2Vw͓y+2Xw͓z-2X͓vz+2X͓xy-2Yvw͓+Y͓vv-Y͓xx+Y͓yy-Y͓zz-2Zw͓x+2Z͓vx+2Z͓yz)e013",
"+(-2Vw͓z-2Xw͓y+2X͓vy+2X͓xz+2Yw͓x-2Y͓vx+2Y͓yz-2Zvw͓+Z͓vv-Z͓xx-Z͓yy+Z͓zz)e021",
]);§Text Form in ASCII Mode
Alternatively, symbols are labelled after their initially assigned basis blades starting with:
'p'if pinned withPgaP3::pin(),'l'if left-hand side as inPgaP3::lhs(),'r'if right-hand side as inPgaP3::rhs(),'v'otherwise.
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:#}", Vee::point().pin() << Vee::motor().unit(), [
"+p123*e123",
"+(+(+1-2*v31*v31-2*v12*v12)*p032+2*(+v*v12+v23*v31)*p013+2*(-v*v31+v23*v12)*p021\
+2*(-v0123*v23-v01*v-v02*v12+v03*v31)*p123)*e032",
"+(+2*(-v*v12+v23*v31)*p032+(+1-2*v23*v23-2*v12*v12)*p013+2*(+v*v23+v31*v12)*p021\
+2*(-v0123*v31+v01*v12-v02*v-v03*v23)*p123)*e013",
"+(+2*(+v*v31+v23*v12)*p032+2*(-v*v23+v31*v12)*p013+(+1-2*v23*v23-2*v31*v31)*p021\
+2*(-v0123*v12-v01*v31+v02*v23-v03*v)*p123)*e021",
]);
format_eq!("{:#}", Vee::line().lhs() * Vee::line().rhs(), [
"-l23*r23-l31*r31-l12*r12",
"+(-l02*r12+r02*l12+l03*r31-r03*l31)*e01",
"+(+l01*r12-r01*l12-l03*r23+r03*l23)*e02",
"+(-l01*r31+r01*l31+l02*r23-r02*l23)*e03",
"+(-l31*r12+r31*l12)*e23",
"+(+l23*r12-r23*l12)*e31",
"+(-l23*r31+r23*l31)*e12",
"+(+l01*r23+r01*l23+l02*r31+r02*l31+l03*r12+r03*l12)*I",
]);§Text Form in $\LaTeX$ Mode
Generate $\LaTeX$ documentation as in
\begin{aligned}
(-r_{01} l_1 - r_{02} l_2 - r_{03} l_3) & \boldsymbol{e}_0 \\
+ (-l_2 r_{12} + r_{31} l_3) & \boldsymbol{e}_1 \\
+ (l_1 r_{12} - r_{23} l_3) & \boldsymbol{e}_2 \\
+ (-l_1 r_{31} + r_{23} l_2) & \boldsymbol{e}_3 \\
+ (l_1 r_{23} + l_2 r_{31} + l_3 r_{12}) & \boldsymbol{e}_{123} \\
+ (-l_0 r_{23} - r_{02} l_3 + r_{03} l_2) & \boldsymbol{e}_{032} \\
+ (-l_0 r_{31} + r_{01} l_3 - r_{03} l_1) & \boldsymbol{e}_{013} \\
+ (-l_0 r_{12} - r_{01} l_2 + r_{02} l_1) & \boldsymbol{e}_{021}
\end{aligned}with "{:$>}", using only the standard amsmath package:
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:$>}", Vee::plane().lhs() * Vee::line().rhs(), [
r"\begin{aligned}",
r" (-r_{01} l_1 - r_{02} l_2 - r_{03} l_3) & \boldsymbol{e}_0 \\",
r" + (-l_2 r_{12} + r_{31} l_3) & \boldsymbol{e}_1 \\",
r" + (l_1 r_{12} - r_{23} l_3) & \boldsymbol{e}_2 \\",
r" + (-l_1 r_{31} + r_{23} l_2) & \boldsymbol{e}_3 \\",
r" + (l_1 r_{23} + l_2 r_{31} + l_3 r_{12}) & \boldsymbol{e}_{123} \\",
r" + (-l_0 r_{23} - r_{02} l_3 + r_{03} l_2) & \boldsymbol{e}_{032} \\",
r" + (-l_0 r_{31} + r_{01} l_3 - r_{03} l_1) & \boldsymbol{e}_{013} \\",
r" + (-l_0 r_{12} - r_{01} l_2 + r_{02} l_1) & \boldsymbol{e}_{021}",
r"\end{aligned}",
]);§Code Form in Generic Mode
Generate generic statements with "{:x}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:x}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"e=l*r-l23*r23-l31*r31-l12*r12",
"e23=l*r23+r*l23-l31*r12+r31*l12",
"e31=l*r31+r*l31+l23*r12-r23*l12",
"e12=l*r12+r*l12-l23*r31+r23*l31",
]);§Code Form in Rust Mode
Generate Rust code with "{:#x}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:#x}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"let e = l * r - l23 * r23 - l31 * r31 - l12 * r12;",
"let e23 = l * r23 + r * l23 - l31 * r12 + r31 * l12;",
"let e31 = l * r31 + r * l31 + l23 * r12 - r23 * l12;",
"let e12 = l * r12 + r * l12 - l23 * r31 + r23 * l31;",
]);Either emit or omit zero vectors:
use vee::{PgaP3 as Vee, format_eq, pga::PgaP3 as Bee};
// Emit zero vector as numeric zero.
let plane = Vee::plane()
.eval([(Bee::e0(), 0), (Bee::e1(), 1)])
.eval([(Bee::e2(), (1, 2))]);
format_eq!("{:#x}", plane, [
"let e0 = 0.0;",
"let e1 = 1.0;",
"let e2 = 1.0 / 2.0;",
"let e3 = v3;",
]);
// Omit zero vector as symbolic zero.
let normal = plane.omit();
format_eq!("{:#x}", normal, [
"let e1 = 1.0;",
"let e2 = 1.0 / 2.0;",
"let e3 = v3;",
]);Generate Rust code dereferencing fields with "{:^x}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:^x}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"o.e = l.e * r.e - l.e23 * r.e23 - l.e31 * r.e31 - l.e12 * r.e12;",
"o.e23 = l.e * r.e23 + r.e * l.e23 - l.e31 * r.e12 + r.e31 * l.e12;",
"o.e31 = l.e * r.e31 + r.e * l.e31 + l.e23 * r.e12 - r.e23 * l.e12;",
"o.e12 = l.e * r.e12 + r.e * l.e12 - l.e23 * r.e31 + r.e23 * l.e31;",
]);§List Form in Unicode Mode
Generate list form (i.e., s-expressions, Scheme, or egglog) in Unicode mode with
"{:e}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:e}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"(+ (+ (* v͔ v͕) (* -1 x͔ x͕) (* -1 y͔ y͕) (* -1 z͔ z͕))",
" (* (+ (* v͔ x͕) (* v͕ x͔) (* -1 y͔ z͕) (* y͕ z͔)) e23)",
" (* (+ (* v͔ y͕) (* v͕ y͔) (* x͔ z͕) (* -1 x͕ z͔)) e31)",
" (* (+ (* v͔ z͕) (* v͕ z͔) (* -1 x͔ y͕) (* x͕ y͔)) e12))",
]);§List Form in ASCII Mode
Generate list form (i.e., s-expressions, Scheme, or egglog) in ASCII mode with
"{:#e}":
use vee::{PgaP3 as Vee, format_eq};
// By default, negation is done as in `(* -1 x y)` or `(* -2 x y)`.
format_eq!("{:#e}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"(+ (+ (* l r) (* -1 l23 r23) (* -1 l31 r31) (* -1 l12 r12))",
" (* (+ (* l r23) (* r l23) (* -1 l31 r12) (* r31 l12)) e23)",
" (* (+ (* l r31) (* r l31) (* l23 r12) (* -1 r23 l12)) e31)",
" (* (+ (* l r12) (* r l12) (* -1 l23 r31) (* r23 l31)) e12))",
]);
// Alternatively, negation is done as in `(- (x y))` or `(- (* 2 x y))`.
format_eq!("{:-^#e}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"(+ (+ (* l r) (- (* l23 r23)) (- (* l31 r31)) (- (* l12 r12)))",
" (* (+ (* l r23) (* r l23) (- (* l31 r12)) (* r31 l12)) e23)",
" (* (+ (* l r31) (* r l31) (* l23 r12) (- (* r23 l12))) e31)",
" (* (+ (* l r12) (* r l12) (- (* l23 r31)) (* r23 l31)) e12))",
]);
// Additionally, the predominant sign is factored.
format_eq!("{:-^-#e}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"(+ (- (+ (- (* l r)) (* l23 r23) (* l31 r31) (* l12 r12)))",
" (* (+ (* l r23) (* r l23) (- (* l31 r12)) (* r31 l12)) e23)",
" (* (+ (* l r31) (* r l31) (* l23 r12) (- (* r23 l12))) e31)",
" (* (+ (* l r12) (* r l12) (- (* l23 r31)) (* r23 l31)) e12))",
]);Bind variables using Scheme’s let operator:
use vee::{PgaP3 as Vee, format_eq};
let rotator = Vee::rotator().lhs() * Vee::rotator().rhs();
format_eq!("(let {:<#5e} (process e e23 e31 e12))\n", rotator, [
"(let ((e (+ (* l r) (* -1 l23 r23) (* -1 l31 r31) (* -1 l12 r12)))",
" (e23 (+ (* l r23) (* r l23) (* -1 l31 r12) (* r31 l12)))",
" (e31 (+ (* l r31) (* r l31) (* l23 r12) (* -1 r23 l12)))",
" (e12 (+ (* l r12) (* r l12) (* -1 l23 r31) (* r23 l31))))",
" (process e e23 e31 e12))",
]);Define variables using Scheme’s define-values operator:
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:>#e}", Vee::rotator().lhs() * Vee::rotator().rhs(), [
"(define-values (e",
" e23",
" e31",
" e12)",
" (values (+ (* l r) (* -1 l23 r23) (* -1 l31 r31) (* -1 l12 r12))",
" (+ (* l r23) (* r l23) (* -1 l31 r12) (* r31 l12))",
" (+ (* l r31) (* r l31) (* l23 r12) (* -1 r23 l12))",
" (+ (* l r12) (* r l12) (* -1 l23 r31) (* r23 l31))))",
]);Export expressions to egglog with "{:#E}":
use vee::{PgaP2 as Vee, format_eq};
format_eq!("{:-^-#E}", Vee::point() / -2, [
"(Add (multiset-of (Mul (multiset-of \
(Neg (Mul (multiset-of (Num (rational 1 2)) (Sym \"v12\")))) (Bee \"e12\")))",
" (Mul (multiset-of \
(Neg (Mul (multiset-of (Num (rational 1 2)) (Sym \"v20\")))) (Bee \"e20\")))",
" (Mul (multiset-of \
(Neg (Mul (multiset-of (Num (rational 1 2)) (Sym \"v01\")))) (Bee \"e01\")))))",
]);See List Form Arguments for the egglog data type definition. Alternatively, transform the
data type through Scheme with "{:<#E}" or "{:>#E}" to suit your rewrite rules. Importing
back optimized expressions along with their CSEs might be supported in the future.
§Tree Form in Unicode Mode
Generate DOT graphs (i.e., text/vnd.graphviz) in Unicode mode with "{:o}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:o}", Vee::plane(), [
r#"digraph vee {"#,
r#" n0 [label="∑" shape=box]"#,
r#" n1 [label="∏" shape=box]"#,
r#" n0 -> n1"#,
r#" n2 [label="W" shape=ellipse]"#,
r#" n1 -> n2"#,
r#" n3 [label="e0" shape=diamond]"#,
r#" n1 -> n3"#,
r#" n4 [label="∏" shape=box]"#,
r#" n0 -> n4"#,
r#" n5 [label="x" shape=ellipse]"#,
r#" n4 -> n5"#,
r#" n6 [label="e1" shape=diamond]"#,
r#" n4 -> n6"#,
r#" n7 [label="∏" shape=box]"#,
r#" n0 -> n7"#,
r#" n8 [label="y" shape=ellipse]"#,
r#" n7 -> n8"#,
r#" n9 [label="e2" shape=diamond]"#,
r#" n7 -> n9"#,
r#" n10 [label="∏" shape=box]"#,
r#" n0 -> n10"#,
r#" n11 [label="z" shape=ellipse]"#,
r#" n10 -> n11"#,
r#" n12 [label="e3" shape=diamond]"#,
r#" n10 -> n12"#,
r#"}"#,
]);§Tree Form in ASCII Mode
Generate DOT graphs (i.e., text/vnd.graphviz) in ASCII mode with "{:#o}":
use vee::{PgaP3 as Vee, format_eq};
format_eq!("{:#o}", Vee::plane(), [
r#"digraph vee {"#,
r#" n0 [label="+" shape=box]"#,
r#" n1 [label="*" shape=box]"#,
r#" n0 -> n1"#,
r#" n2 [label="v0" shape=ellipse]"#,
r#" n1 -> n2"#,
r#" n3 [label="e0" shape=diamond]"#,
r#" n1 -> n3"#,
r#" n4 [label="*" shape=box]"#,
r#" n0 -> n4"#,
r#" n5 [label="v1" shape=ellipse]"#,
r#" n4 -> n5"#,
r#" n6 [label="e1" shape=diamond]"#,
r#" n4 -> n6"#,
r#" n7 [label="*" shape=box]"#,
r#" n0 -> n7"#,
r#" n8 [label="v2" shape=ellipse]"#,
r#" n7 -> n8"#,
r#" n9 [label="e2" shape=diamond]"#,
r#" n7 -> n9"#,
r#" n10 [label="*" shape=box]"#,
r#" n0 -> n10"#,
r#" n11 [label="v3" shape=ellipse]"#,
r#" n10 -> n11"#,
r#" n12 [label="e3" shape=diamond]"#,
r#" n10 -> n12"#,
r#"}"#,
]);S. De Keninck and M. Roelfs, “Normalization, square roots, and the exponential and logarithmic maps in geometric algebras of less than 6D”, Mathematical Methods in the Applied Sciences 47, 1425–1441. ↩
M. Roelfs and S. De Keninck, “Graded Symmetry Groups: Plane and Simple”, Advances in Applied Clifford Algebras 33. ↩
L. Dorst and S. De Keninck, “Physical Geometry by Plane-Based Geometric Algebra”, Advanced Computational Applications of Geometric Algebra, 43–76. ↩
Modules§
- pga
- Plane-Based Pistachio Flavor – Projective Geometric Algebra (PGA)
Macros§
Structs§
- Factorization
- Uniquely reduced but volatile form of symbolic polynomial factorization.
- Integer
- 64-bit non-zero integer with solely strict arithmetic.
- Monomial
- Uniquely reduced form of a symbolic monomial expression.
- Multivector
- Uniquely reduced form of a symbolic multivector expression.
- Polynomial
- Uniquely reduced form of a symbolic polynomial expression.
- Rational
- 64-bit non-zero rational in canonical form with solely strict arithmetic.
- Symbol
- Symbol as Unicode character with optional combining diacritical mark.
Enums§
- Tree
- Non-binary algebraic expression tree up to symbolic
Multivectorexpressions.
Traits§
- Algebra
- A geometric algebra defined by a flavor’s basis (i.e., all its basis blades).
- Inv
- The unary inversion operator.
- InvAssign
- The unary inversion assignment operator.
- NegAssign
- The unary negation assignment operator.
- NotAssign
- The unary logical negation assignment operator.
- Rev
- The unary reversion operator.
- RevAssign
- The unary reversion assignment operator.
Functions§
- choose
- The binomial coefficient
noverkup ton = 34for allk.
Type Aliases§
- PgaE0
rudimentary - Multivector for Elliptic 0D PGA.
- PgaE1
rudimentary - Multivector for Elliptic 1D PGA.
- PgaE2
- Multivector for Elliptic 2D PGA.
- PgaE3
- Multivector for Elliptic 3D PGA.
- PgaE4
- Multivector for Elliptic 4D PGA.
- PgaE5
exploratory - Multivector for Elliptic 5D PGA.
- PgaE6
exploratory - Multivector for Elliptic 6D PGA.
- PgaE7
exploratory - Multivector for Elliptic 7D PGA.
- PgaH0
rudimentary - Multivector for Hyperbolic 0D PGA.
- PgaH1
rudimentary - Multivector for Hyperbolic 1D PGA.
- PgaH2
- Multivector for Hyperbolic 2D PGA.
- PgaH3
- Multivector for Hyperbolic 3D PGA.
- PgaH4
- Multivector for Hyperbolic 4D PGA.
- PgaH5
exploratory - Multivector for Hyperbolic 5D PGA.
- PgaH6
exploratory - Multivector for Hyperbolic 6D PGA.
- PgaH7
exploratory - Multivector for Hyperbolic 7D PGA.
- PgaP0
rudimentary - Multivector for Parabolic (Euclidean) 0D PGA.
- PgaP1
rudimentary - Multivector for Parabolic (Euclidean) 1D PGA.
- PgaP2
- Multivector for Parabolic (Euclidean) 2D PGA.
- PgaP3
- Multivector for Parabolic (Euclidean) 3D PGA.
- PgaP4
- Multivector for Parabolic (Euclidean) 4D PGA.
- PgaP5
exploratory - Multivector for Parabolic (Euclidean) 5D PGA.
- PgaP6
exploratory - Multivector for Parabolic (Euclidean) 6D PGA.
- PgaP7
exploratory - Multivector for Parabolic (Euclidean) 7D PGA.