GA-FuL Design-Prinzipien
Inhaltsverzeichnis
- Ăbersicht
- Kern-Design-Intentionen (CDIs)
- Data-Oriented Programming (DOP)
- Implementierung der DOP-Prinzipien
- Praktische Beispiele
Ăbersicht
Das Design von GA-FuL basiert auf zwei Hauptkonzepten:
- Core Design Intentions (CDIs): FĂŒnf Hauptziele, die das System erfĂŒllen soll
- Data-Oriented Programming (DOP): Vier Prinzipien fĂŒr die praktische Umsetzung
Diese Prinzipien entstanden aus der Erfahrung mit dem VorgÀngersystem GMac und adressieren dessen SchwÀchen.
Kern-Design-Intentionen (CDIs)
CDI-1: Abstraktion von Multivektor-Operationen
Ziel: Trennung von Multivektor-Operationen und konkreten Skalar-ReprÀsentationen
Problem:
- Skalare können auf viele Arten reprÀsentiert werden:
- IEEE 754 Gleitkommazahlen (32-bit, 64-bit)
- Beliebig genaue Dezimalzahlen
- Rationale Zahlen (BrĂŒche)
- Symbolische AusdrĂŒcke (fĂŒr CAS)
- Mehrdimensionale Arrays (fĂŒr ML/AI)
- Abgetastete Signale (fĂŒr Signalverarbeitung)
Lösung:
- Generische Implementierung ĂŒber
IScalarProcessor<T> - Alle GA-Operationen unabhÀngig vom Skalartyp
- Einfache Integration neuer Skalartypen
Beispiel:
// Derselbe Code funktioniert mit allen Skalartypen
var processor = XGaProcessor<T>.CreateEuclidean(scalarProcessor);
var v1 = processor.CreateComposer()
.SetVectorTerm(0, a)
.SetVectorTerm(1, b)
.SetVectorTerm(2, c)
.GetVector();
var v2 = processor.CreateComposer()
.SetVectorTerm(0, x)
.SetVectorTerm(1, y)
.SetVectorTerm(2, z)
.GetVector();
var result = v1.Gp(v2); // Geometrisches Produkt
// T kann sein: double, float, decimal, BigDecimal, Expr, etc.
Vorteile:
- â Ein Code fĂŒr alle Skalartypen
- â Einfaches HinzufĂŒgen neuer Skalartypen
- â Maximale Wiederverwendbarkeit
CDI-2: Reduzierung der Speicheranforderungen
Ziel: Effiziente Speicherung von sparse Multivektoren in hochdimensionalen GAs
Problem:
- Ein voller Multivektor in $n$-dimensionaler GA benötigt $2^n$ Skalare
- Ein voller $k$-Vektor benötigt $\binom{n}{k}$ Skalare
- Beispiele (bei 32-bit floats):
- 30D GA, voller Multivektor: 8 GB
- 30D GA, voller 15-Vektor: 1.16 GB
- Dies ist fĂŒr die meisten Anwendungen unpraktisch
RealitÀt:
- Praktische GA-Anwendungen nutzen meist sparse Multivektoren
- Beispiel: 5D-CGA kann 3D-Geometrie mit nur 5 von 32 Skalaren reprÀsentieren
Lösung:
- Dictionary-basierte Speicherung:
IReadOnlyDictionary<IIndexSet, T> - Nur nicht-null Komponenten werden gespeichert
- Automatische Auswahl der effizientesten Datenstruktur
Speicher-Hierarchie:
| Multivektor-Typ | Datenstruktur | Speicher-Effizienz |
|---|---|---|
| Zero-Multivektor | EmptyDictionary |
0 Skalare |
| Single-Komponente | SingleItemDictionary |
1 Skalar + Index |
| Sparse | Dictionary<IIndexSet, T> |
n Skalare + Indices |
| Dense (klein) | Lookup-Tabellen | Optimiert fĂŒr kleine n |
Index-Set-Hierarchie:
| Dimensions-Bereich | IIndexSet-Implementierung | Speicher |
|---|---|---|
| Leere Menge | EmptyIndexSet |
0 Bytes |
| 1 Element | SingleIndexSet |
4 Bytes |
| < 64 Dimensionen | UInt64IndexSet |
8 Bytes |
| Beliebig (dicht) | DenseIndexSet |
Array von ints |
| Beliebig (sparse) | SparseIndexSet |
HashSet |
Beispiel:
// 100D-GA, sparse Multivektor mit 10 Komponenten
var composer = processor.CreateMultivectorComposer();
composer.SetTerm(indexSet1, scalar1);
composer.SetTerm(indexSet2, scalar2);
// ... 10 Terme total
var mv = composer.GetMultivector();
// Speicher: ~10 Skalare + 10 Index-Sets
// Voller Multivektor wĂŒrde 2^100 Skalare benötigen!
Vorteile:
- â Hochdimensionale GAs werden praktikabel
- â Automatische Optimierung
- â Flexible DimensionalitĂ€t
CDI-3: Metaprogrammierung-FĂ€higkeiten
Ziel: Code-Generierung aus GA-AusdrĂŒcken fĂŒr optimierte Performance
Problem:
- Generische Implementierung ist flexibel aber manchmal langsam
- High-Performance-Anwendungen brauchen optimierten Code
- Manuelles Schreiben von optimiertem Code ist fehleranfÀllig
Lösung:
- Automatische Code-Generierung aus GA-AusdrĂŒcken
- Optimierung durch:
- Konstantenpropagierung
- Common Subexpression Elimination
- Symbolische Vereinfachung
- Genetische Programmierung
- UnterstĂŒtzung fĂŒr mehrere Zielsprachen
Workflow:
High-Level GA-Operationen
â
Expression Tree
â
Optimierung
â
Zielsprachen-Code (C++, C#, CUDA, etc.)
Beispiel:
// 1. Definiere Berechnungen in GA-FuL
var context = new MetaContext();
var x = context.CreateParameter("x");
var y = context.CreateParameter("y");
var scalarProcessor = context.ScalarProcessor;
var processor = XGaProcessor<IMetaExpression>.CreateEuclidean(scalarProcessor);
var v1 = processor.CreateComposer()
.SetVectorTerm(0, x)
.SetVectorTerm(1, y)
.SetVectorTerm(2, 0)
.GetVector();
var v2 = processor.CreateComposer()
.SetVectorTerm(0, 1)
.SetVectorTerm(1, 1)
.SetVectorTerm(2, 1)
.GetVector();
var result = v1.Gp(v2);
// 2. Optimiere
context.Optimize();
// 3. Generiere C++-Code
var cppCode = new CppCodeComposer().Generate(context);
// Resultat: Hochoptimierter C++-Code
AnwendungsfÀlle:
- CUDA-Code fĂŒr GPU-Beschleunigung
- Embedded Systems (C/C++)
- Web-Anwendungen (JavaScript)
- Wissenschaftliche Berechnungen (MATLAB/Python)
Vorteile:
- â Beste Performance fĂŒr Production-Code
- â Fehlerfreie Code-Generierung
- â Automatische Optimierung
- â Mehrere Zielsprachen
CDI-4: Geschichtetes System-Design
Ziel: Organisation der KomplexitĂ€t durch Schichten fĂŒr verschiedene Benutzergruppen
Problem:
- CDI-1 bis CDI-3 fĂŒhren zu hoher KomplexitĂ€t
- Verschiedene Benutzer haben verschiedene BedĂŒrfnisse
- Manche brauchen abstrakte High-Level-API
- Andere brauchen Low-Level-Kontrolle fĂŒr Performance
Lösung: Vier Schichten mit zunehmender Abstraktion:
Abstraktionsebene
â
â ââââââââââââââââââââââââââââââ
Hoch â â Metaprogrammierungs-Layer â
â ââââââââââââââââââââââââââââââ€
â â Modellierungs-Layer â
â ââââââââââââââââââââââââââââââ€
â â Algebra-Layer â
â ââââââââââââââââââââââââââââââ€
Niedrigâ â System-Utilities-Layer â
â ââââââââââââââââââââââââââââââ
Benutzer-Profile:
| Benutzertyp | Bevorzugte Schicht | Anwendungsfall |
|---|---|---|
| Forscher | Modellierung | Prototyping geometrischer Algorithmen |
| Softwareentwickler | Metaprogrammierung | Code-Generierung fĂŒr Production |
| Bibliotheksentwickler | Algebra | Erweiterung der FunktionalitÀt |
| Performance-Ingenieur | Algebra + Utilities | Optimierung kritischer Pfade |
Beispiele:
High-Level (Modellierung):
// Koordinatenfreie, intuitive API
var point = cga.Encode.IpnsRound.Point(x, y, z);
var sphere = cga.Encode.IpnsRound.RealSphere(radius, cx, cy, cz);
var intersection = point.Op(sphere);
Low-Level (Algebra):
// Direkte Kontrolle ĂŒber Skalare und Basis-Blades
var blade = processor.CreateBasisBlade(indexSet);
var scalar = scalarProcessor.Times(a, b);
var term = processor.CreateTerm(blade, scalar);
Vorteile:
- â Jeder Benutzer findet passende Abstraktionsebene
- â Klare Trennung der Verantwortlichkeiten
- â Einfache Wartung und Erweiterung
CDI-5: Unified, Generic, Extensible API
Ziel: Einheitliche API fĂŒr verschiedene Anwendungsklassen
Anforderungen:
- Unified: Konsistente Namenskonventionen und Muster
- Generic: Funktioniert mit allen Skalartypen und GA-Metriken
- Extensible: Einfaches HinzufĂŒgen neuer Features
Implementierung:
1. Konsistente Namenskonventionen:
// Alle Prozessoren folgen demselben Muster
IScalarProcessor<T>
XGaProcessor<T>
XGaConformalSpace<T>
// Alle Composer folgen demselben Muster
XGaMultivectorComposer<T>
XGaKVectorComposer<T>
2. Generische Typen:
// Ein Template fĂŒr alle Typen
XGaMultivector<double>
XGaMultivector<float>
XGaMultivector<Expr> // Symbolisch
XGaMultivector<BigDecimal>
3. Extension Methods:
// Einheitliche API durch Extension Methods
multivector.Gp(other) // Geometric Product
multivector.Op(other) // Outer Product
multivector.Lcp(other) // Left Contraction Product
multivector.Rcp(other) // Right Contraction Product
multivector.Sp(other) // Scalar Product
multivector.Reverse() // Reverse
multivector.GradeInvolution() // Grade Involution
4. Builder-Pattern:
// Fluent API fĂŒr Konstruktion
var mv = processor.CreateMultivectorComposer()
.SetTerm(index1, scalar1)
.SetTerm(index2, scalar2)
.AddTerm(index3, scalar3)
.GetMultivector();
UnterstĂŒtzte Anwendungsklassen:
- Numerische Berechnungen
- Symbolische Mathematik
- Signalverarbeitung
- Computer Vision
- Robotik
- Visualisierung
- Code-Generierung
Vorteile:
- â Leicht zu lernen
- â Konsistent ĂŒber alle Module
- â IntelliSense-freundlich
- â Einfach zu erweitern
Data-Oriented Programming (DOP)
Traditionelles OOP fĂŒhrte zu zu hoher KomplexitĂ€t in GA-FuL. DOP bietet eine bessere Alternative.
Warum DOP statt klassischem OOP?
Probleme mit klassischem OOP:
- â Tiefe Kopplung von Daten und Verhalten
- â Komplexe Vererbungshierarchien
- â Schwierige Wartung bei groĂen Systemen
- â Einkapselung kann zu InflexibilitĂ€t fĂŒhren
Vorteile von DOP:
- â Lesbarerer Code
- â Wartbarerer Code
- â Erweiterbarer Code
- â Bessere Testbarkeit
DOP-Prinzip 1: Trennung von Verhalten und Daten
Regel: Verhaltenscode und Daten getrennt halten
In OOP:
// Klassisches OOP: Daten und Verhalten gekoppelt
class Multivector {
private Dictionary<IIndexSet, T> data;
public Multivector Gp(Multivector other) {
// Verhalten im selben Objekt
}
}
In GA-FuL (DOP):
// DOP: Daten in thin wrapper
class XGaMultivector<T> {
public IReadOnlyDictionary<int, XGaKVector<T>> Data { get; }
}
// Verhalten in statischen Extension Methods
static class XGaMultivectorExtensions {
public static XGaMultivector<T> Gp<T>(
this XGaMultivector<T> mv1,
XGaMultivector<T> mv2
) {
// Verhalten getrennt von Daten
}
}
Vorteile:
- â Daten können von mehreren Verhaltens-Modulen verwendet werden
- â Verhalten kann unabhĂ€ngig getestet werden
- â Einfacher Code-Reviews
DOP-Prinzip 2: Generische Datenstrukturen
Regel: Verwende generelle Datenstrukturen statt spezielle Klassen
Bevorzugte Strukturen:
- Arrays/Listen fĂŒr sequentielle Daten
- Dictionaries/Maps fĂŒr Key-Value-Paare
- Sets fĂŒr eindeutige Sammlungen
- Queues fĂŒr FIFO
- Trees fĂŒr hierarchische Daten
In GA-FuL:
Multivektoren:
// NICHT eine spezielle Klasse mit interner Array-Struktur
// SONDERN generisches Dictionary
IReadOnlyDictionary<int, XGaKVector<T>>
k-Vektoren:
IReadOnlyDictionary<IIndexSet, T>
Beispiel:
// Zugriff ist einfach und transparent
var grade2Part = multivector[2]; // Dictionary-Zugriff
var scalar = kVector[indexSet]; // Dictionary-Zugriff
// Keine versteckten internen Strukturen
Vorteile:
- â Transparente Datenstrukturen
- â Standard-Operationen verfĂŒgbar
- â Einfach zu debuggen
- â Vertraut fĂŒr alle Entwickler
DOP-Prinzip 3: UnverÀnderliche Daten
Regel: Daten niemals direkt Àndern, sondern neue Versionen erstellen
Warum UnverÀnderlichkeit?
- Thread-Sicherheit
- Vorhersagbares Verhalten
- Einfacheres Debugging
- Funktionale Programmierung-Stil
Implementierung in GA-FuL:
Problem: Wie erstellt man neue Multivektoren ohne Mutation?
Lösung: Composer-Klassen
// 1. Composer fĂŒr Konstruktion (Mutable wĂ€hrend Build)
var composer = processor.CreateMultivectorComposer();
composer.SetTerm(index1, scalar1);
composer.SetTerm(index2, scalar2);
composer.AddTerm(index3, scalar3);
// 2. Finaler Multivektor (Immutable)
var multivector = composer.GetMultivector();
// 3. Multivector selbst ist read-only
// multivector.Data ist IReadOnlyDictionary<...>
Pattern:
Mutable Composer â Build â Immutable Data Structure
Klassen:
XGaMultivectorComposer<T>: Erstellt MultivektorenXGaKVectorComposer<T>: Erstellt k-VektorenXGaScalarComposer<T>: Erstellt Skalare
Vorteile:
- â Keine unerwarteten Seiteneffekte
- â Thread-sicher
- â Einfacher zu verstehen
- â Optimierungsmöglichkeiten durch Compiler
DOP-Prinzip 4: Trennung von DatenreprÀsentation und Schema
Regel: Datenschema getrennt von tatsÀchlichen Daten speichern
Implementierung:
Durch Interfaces und abstrakte Klassen:
// Schema: Interface definiert die Form
interface IIndexSet {
int Count { get; }
bool Contains(int index);
IEnumerator<int> GetEnumerator();
}
// ReprÀsentation: Verschiedene Implementierungen
class EmptyIndexSet : IIndexSet { }
class SingleIndexSet : IIndexSet { }
class UInt64IndexSet : IIndexSet { }
class DenseIndexSet : IIndexSet { }
class SparseIndexSet : IIndexSet { }
Verwendung:
// Code verwendet nur das Schema (Interface)
void ProcessBlade(IIndexSet indexSet) {
// Funktioniert mit allen Implementierungen
foreach (var index in indexSet) {
// ...
}
}
// Automatische Auswahl der besten Implementierung
var indexSet = IndexSetFactory.Create(indices);
// Gibt EmptyIndexSet, SingleIndexSet, UInt64IndexSet, etc. zurĂŒck
Weitere Beispiele:
Multivektoren:
// Schema
IReadOnlyDictionary<IIndexSet, T>
// Implementierungen
class EmptyDictionary<K, V> : IReadOnlyDictionary<K, V>
class SingleItemDictionary<K, V> : IReadOnlyDictionary<K, V>
class Dictionary<K, V> : IReadOnlyDictionary<K, V>
Vorteile:
- â FlexibilitĂ€t bei der Implementierung
- â Optimierung zur Laufzeit
- â Einfaches Austauschen von Implementierungen
- â Interface-basierte Tests
Implementierung der DOP-Prinzipien
Beispiel: Index-Sets
VollstÀndige Implementierung der vier DOP-Prinzipien:
DOP-4 (Schema):
// Interface als Schema
interface IIndexSet {
int Count { get; }
bool Contains(int index);
IEnumerable<int> GetIndices();
}
DOP-2 (Generische Strukturen) + DOP-3 (UnverÀnderlich):
// Implementierung mit read-only internen Strukturen
class UInt64IndexSet : IIndexSet {
private readonly ulong _bitmap; // Immutable
public UInt64IndexSet(ulong bitmap) {
_bitmap = bitmap; // Set once, never changed
}
}
class SparseIndexSet : IIndexSet {
private readonly ImmutableHashSet<int> _indices; // Immutable
public SparseIndexSet(IEnumerable<int> indices) {
_indices = indices.ToImmutableHashSet();
}
}
DOP-1 (Trennung von Verhalten):
// Thin wrapper
class XGaBasisBlade {
public IIndexSet IndexSet { get; } // Just holds data
public XGaBasisBlade(IIndexSet indexSet) {
IndexSet = indexSet;
}
}
// Verhalten in Extension Methods
static class XGaBasisBladeExtensions {
public static XGaBasisBlade Op(
this XGaBasisBlade blade1,
XGaBasisBlade blade2,
XGaMetric metric
) {
// Outer product logic here
}
}
Beispiel: Multivektoren
VollstÀndige DOP-Implementierung:
DOP-4 + DOP-2:
// Schema: Generic Dictionary
IReadOnlyDictionary<IIndexSet, T>
// Thin Wrapper mit Schema
class XGaKVector<T> {
public IReadOnlyDictionary<IIndexSet, T> ScalarDictionary { get; }
public XGaProcessor<T> Processor { get; }
}
class XGaMultivector<T> {
public IReadOnlyDictionary<int, XGaKVector<T>> KVectorDictionary { get; }
public XGaProcessor<T> Processor { get; }
}
DOP-3 (Immutability via Composer):
// Mutable Builder
class XGaMultivectorComposer<T> {
private Dictionary<IIndexSet, T> _terms = new();
public XGaMultivectorComposer<T> SetTerm(IIndexSet id, T scalar) {
_terms[id] = scalar;
return this;
}
public XGaMultivector<T> GetMultivector() {
// Select most efficient implementation
if (_terms.Count == 0)
return new ZeroMultivector<T>();
if (_terms.Count == 1)
return new SingleTermMultivector<T>(_terms.First());
return new SparseMultivector<T>(_terms.ToImmutableDictionary());
}
}
DOP-1 (Verhalten getrennt):
// Extension Methods fĂŒr Operationen
static class XGaMultivectorExtensions {
public static XGaMultivector<T> Gp<T>(
this XGaMultivector<T> mv1,
XGaMultivector<T> mv2
) {
var composer = mv1.Processor.CreateMultivectorComposer();
// Geometric product logic
foreach (var (id1, scalar1) in mv1.Terms)
foreach (var (id2, scalar2) in mv2.Terms) {
var product = id1.Gp(id2, mv1.Processor.Metric);
composer.AddTerm(product.IndexSet,
mv1.Processor.ScalarProcessor.Times(
scalar1,
scalar2,
product.Sign
)
);
}
return composer.GetMultivector();
}
}
Praktische Beispiele
Beispiel 1: Neuen Skalartyp hinzufĂŒgen
Dank DOP-Prinzipien ist dies einfach:
// 1. Implementiere IScalarProcessor<T> (DOP-1: Verhalten)
public class MyCustomScalarProcessor : INumericScalarProcessor<MyScalar> {
public MyScalar Add(MyScalar a, MyScalar b) => a + b;
public MyScalar Times(MyScalar a, MyScalar b) => a * b;
// ... weitere Operationen
}
// 2. Verwende es mit GA-FuL (DOP-4: Schema-KompatibilitÀt)
var scalarProcessor = new MyCustomScalarProcessor();
var processor = XGaProcessor<MyScalar>.CreateEuclidean(scalarProcessor);
// 3. Alle GA-Operationen funktionieren automatisch!
var v1 = processor.CreateComposer()
.SetVectorTerm(0, a)
.SetVectorTerm(1, b)
.SetVectorTerm(2, c)
.GetVector();
var v2 = processor.CreateComposer()
.SetVectorTerm(0, x)
.SetVectorTerm(1, y)
.SetVectorTerm(2, z)
.GetVector();
var result = v1.Gp(v2);
Beispiel 2: Neue Index-Set-Implementierung
// 1. Implementiere IIndexSet (DOP-4: Schema)
public class BitVectorIndexSet : IIndexSet {
private readonly BitVector32 _bits; // DOP-3: Immutable
public int Count => _bits.Data.CountBits();
public bool Contains(int index) => _bits[1 << index];
// ...
}
// 2. Verwende es (DOP-2: Generic Structure)
IIndexSet indexSet = new BitVectorIndexSet(bits);
var blade = new XGaBasisBlade(indexSet);
// 3. Alle Operationen funktionieren (DOP-1: Verhalten getrennt)
var result = blade.Op(otherBlade, metric);
Beispiel 3: Neue geometrische Operation
// DOP-1: Verhalten als Extension Method hinzufĂŒgen
public static class MyCustomOperations {
public static XGaMultivector<T> MyCustomProduct<T>(
this XGaMultivector<T> mv1,
XGaMultivector<T> mv2
) {
// DOP-2: Arbeite mit generischen Datenstrukturen
var composer = mv1.Processor.CreateMultivectorComposer();
foreach (var (id1, s1) in mv1.Terms)
foreach (var (id2, s2) in mv2.Terms) {
// Nutze bestehende Strukturen
var id = ComputeResultingId(id1, id2);
var scalar = ComputeResultingScalar(s1, s2);
composer.AddTerm(id, scalar);
}
// DOP-3: Returne immutable Objekt
return composer.GetMultivector();
}
}
// Verwendung
var result = multivector1.MyCustomProduct(multivector2);
Zusammenfassung
Kern-Design-Intentionen
| CDI | Ziel | Nutzen |
|---|---|---|
| CDI-1 | Skalar-Abstraktion | FlexibilitÀt bei Skalartypen |
| CDI-2 | Sparse-Speicherung | Hochdimensionale GAs praktikabel |
| CDI-3 | Metaprogrammierung | Optimierte Code-Generierung |
| CDI-4 | Geschichtetes Design | Verschiedene Abstraktionsebenen |
| CDI-5 | Unified API | Konsistente, erweiterbare Schnittstelle |
DOP-Prinzipien
| DOP | Prinzip | Implementierung in GA-FuL |
|---|---|---|
| DOP-1 | Trennung von Verhalten und Daten | Extension Methods + Thin Wrappers |
| DOP-2 | Generische Datenstrukturen | Dictionary, Array, HashSet |
| DOP-3 | UnverĂ€nderliche Daten | Composer-Pattern fĂŒr Konstruktion |
| DOP-4 | Schema-Trennung | Interfaces + Multiple Implementierungen |
Vorteile der Kombination
â Lesbar: Klare Trennung, einfache Strukturen â Wartbar: Geringe Kopplung, hohe KohĂ€sion â Erweiterbar: Neue Features leicht hinzuzufĂŒgen â Performant: Optimierungsmöglichkeiten auf allen Ebenen â Flexibel: UnterstĂŒtzt verschiedene AnwendungsfĂ€lle â Testbar: Jede Komponente isoliert testbar