1 个不稳定版本

0.1.0 2022年1月24日

#1248 in 数学

Apache-2.0

390KB
7K SLoC

一个鲁棒且通用的 Rust 几何代数库。

什么是几何代数?

几何代数是线性代数的一种扩展,其中向量通过称为“几何积”的乘法运算扩展,它产生新的运算和向量类似的对象,这些对象有助于简化并推广数学和物理学中许多棘手系统。

例如

  • 在物理学中,使用“二元量”几何代数提供了一种在“任何”维度中一致且简洁地表示角速度的方法,这在标准角度和向量系统中通常是不可能的。
  • 几何代数将复数和四元数的旋转作用统一到一个称为“转子”的单个对象中,该对象在所有维度中都可以工作。
  • 使用“单位刀片”,几何代数提供了一种紧凑且高效的方式来表示向量和仿射空间,这比使用基向量集更容易处理。

有关更多信息及深入解释,我强烈推荐 Leo Durst、Daniel Frontijne 和 Stephann Mann 所著的《几何代数计算机科学》。这个库的很多灵感都来自这本书,概念解释得易于理解且易于视觉化,而不会陷入过于抽象的数学。

用法

这个 crate 被拆分成了多个模块,每个模块代表对底层代数的一种不同解释

  • algebra 包含了最原始的几何代数形式,纯粹以代数方式定义。这主要用于作为其他系统的核心,但也对各种物理量(如位置和角速度)和抽象数学对象有用
  • subspacealgebra 中的结构体解释为加权向量空间和实数 n 上的正交变换(如旋转和反射)
  • homogenous(计划中)将 algebra 中的结构体解释为仿射空间及其在实数 n-1 上的运算,使用齐次坐标

此外,模块 wedged::base 包含了所有子系统共有的核心组件。

命名法和约定

在几何代数中,命名可能会有所不同,因此已经选择了特定的约定

  • wedged 中,一个 SimpleBlade 指的是由向量的外积构造的对象,而一个 Blade 指的是特定阶数的任何对象
  • Rotor 专门用于表示旋转的单位范数对象,而 Even 用于表示偶次子代数中的通用对象
  • Multivector 指的是代数中的通用元素
  • 运算符 */Mul^/BitXor%/Rem 分别用于几何、外积和广义点积

此外,这个包中的大多数结构体都支持索引,其中每个组件的顺序是根据为这个包选择的特定基约定排序的。有关更多信息,请参阅 BasisBlade 的文档。

泛型编程

为了将代码泛化到不同的标量类型和维度,wedged 大量使用泛型。该系统基于 nalgebra 中的系统,并且为了提高互操作性,重用了其许多组件。

系统设计得可以支持多个不同级别的抽象

无泛型


use wedged::algebra::*;

let v1 = Vec3::new(1.0, 0.0, 0.0);
let v2 = Vec3::new(0.0, 1.0, 0.0);
let plane = v1 ^ v2;
let cross = plane.dual();

assert_eq!(plane, BiVec3::new(0.0,0.0,1.0));
assert_eq!(cross, Vec3::new(0.0,0.0,1.0));

在不使用泛型时,事物表现得与您期望从向量库中得到的类似。

泛型标量


use wedged::algebra::*;
use wedged::base::ops::*;

fn midpoint<T:Clone+ClosedAdd+ClosedDiv+One>(v1:Vec3<T>, v2:Vec3<T>) -> Vec3<T> {
  let two = T::one() + T::one();
  (v1 + v2) / two
}

fn cross<T:RefRing>(v1:Vec3<T>, v2:Vec3<T>) -> Vec3<T> {
  (v1 ^ v2).dual()
}

let v1 = Vec3::new(1.0, 0.0, 0.0);
let v2 = Vec3::new(0.0, 1.0, 0.0);
assert_eq!(cross(v1,v2), Vec3::new(0.0,0.0,1.0));
assert_eq!(midpoint(v1,v2), Vec3::new(0.5,0.5,0.0));

let v1 = Vec3::new(1.0f32, 0.0, 0.0);
let v2 = Vec3::new(0.0f32, 1.0, 0.0);
assert_eq!(cross(v1,v2), Vec3::new(0.0f32,0.0,1.0));
assert_eq!(midpoint(v1,v2), Vec3::new(0.5f32,0.5,0.0));

对于泛型标量,wedged 结构体根据其标量实现实现特性和函数。例如

  • 结构化特性和 IndexAsRef<[T]>Borrow<[T]> 总是得到实现
  • 如果标量实现了它们,则实现了基本特性,如 Clonefmt 特性、PartialEqEqHashDefault
  • 如果标量是 Copy,则实现了 Copy,如果结构体不是 Dynamic 维度
  • 实现了加法运算,如 AddSubNeg,以及标量运算,如 MulDiv。如果标量具有它们,则实现了赋值变体和引用之间的操作。如果标量实现了它们。
  • 如果标量具有加法、减法、负号、零和引用之间的乘法,则实现了几何(Mul)、外积(BitXor)和点积(Rem)特性的特性和操作
  • 涉及范数、构建旋转等的一切都需要 nalgebra::real_field
  • Div 通常具有与 Mul 相同的要求,但仅在某些特定结构体(如 RotorVersor)上实现

为了简化这些需求的管理,在 wedged::base::ops 中包含了一些特性别名,它们将常见操作组合在一起

泛型维度


use wedged::base::*;
use wedged::algebra::*;

fn point_velocity<T,N:Dim>(p:VecN<T,N>, angular_vel: BiVecN<T,N>) -> VecN<T,N> where
  T:AllocBlade<N,U1>+AllocBlade<N,U2>+RefRing
{
  p % angular_vel
}

let p = Vec2::new(2.0,0.0);
let av = BiVec2::new(3.0);
assert_eq!(point_velocity(p, av), Vec2::new(0.0,6.0));

let p = Vec3::new(0.0,2.0,0.0);
let av = BiVec3::new(3.0,0.0,0.0);
assert_eq!(point_velocity(p, av), Vec3::new(0.0,0.0,6.0));

对于泛型维度,对于函数或特性中使用的每个 wedged 结构,标量必须具有该结构的相应 Alloc* 特性约束。这些都在 wedged::base::alloc 中,并且每个几何代数的子集都有一个。

例如,对于标量 T 和维度 N,使用 VecN 需要 T:AllocBlade<N,U1> 约束,使用 Even 需要 T:AllocEven<N> 约束等。

最后,即使使用非泛型标量,这些约束在函数或特性的 where 子句中仍然是必需的。

泛型维度和阶


use wedged::base::*;
use wedged::algebra::*;

fn project<T,N:Dim,G:Dim>(v:VecN<T,N>, b:Blade<T,N,G>) -> VecN<T,N> where
  U1: DimSymSub<G>,
  DimSymDiff<U1,G>: DimSymSub<G,Output=U1>,
  T:AllocBlade<N,U1> + AllocBlade<N,G> + AllocBlade<N,DimSymDiff<U1,G>> + RefDivRing
{
  let l = b.norm_sqrd();
  (v % &b) % b.reverse()/l
}

let v = Vec3::new(1.0, 2.0, 0.0);
let line = Vec3::new(0.0, 1.0, 0.0);
let plane = BiVec3::new(0.0, 1.0, 0.0);

assert_eq!(project(v,line), Vec3::new(0.0, 2.0, 0.0));
assert_eq!(project(v,plane), Vec3::new(1.0, 0.0, 0.0));

使用泛型阶遵循与泛型维度几乎相同的规则,但增加了一个用于阶的泛型。然而,在像楔积或点积这样的阶改变操作中使用时,需要额外的约束以允许编译器添加或减去两个刀片的阶。

例如

  • 楔积 ^ 乘积需要 G1: DimAdd<G2>
  • 点积 % 乘积需要 G1: DimSymSub<G2>

依赖项

~4MB
~91K SLoC