#实数 #整数 #代数 #数值

nightly no-std maths-traits

一个简单易用且抽象的数学特性系统,适用于Rust语言

4个版本

0.2.1 2019年9月16日
0.2.0 2019年9月16日
0.1.3 2019年5月21日
0.1.2 2019年4月23日

#410 in 数学


4 crates中使用

MIT许可证

160KB
1K SLoC

数学特性

一个简单易用且抽象的数学特性系统,适用于Rust语言

设计

此crate的目的是提供一个系统,用于以尽可能抽象的方式与数学对象一起工作,同时使用简单,使得使用数学泛型和高度抽象几乎与围绕特定类型进行设计一样简单。

该系统可以用于从将实数算法无缝应用于不同精度等简单情况到简化向量系统,使得使用多项式作为坐标与切换到环模块一样容易,甚至像将环的不同属性与整数分开,使得像多项式这样的对象可以适应为后者设计的通用算法。

为了实现这个目标,提供的框架是根据一些设计考虑因素构建的

  • 为了使用和实现方便,包含的系统尽可能使用标准Rust或已建立的库,如num_traits,而不是创建新的系统。
  • 实现者只需考虑结构体的单个属性,而用户只需使用所需的数学对象的单个特性即可
  • 系统已命名和整理,尽可能符合数学惯例,以增加使用清晰度,同时提高通用性和灵活性

使用方法

此框架中的特性分为两个集合,一个是用于单个属性和操作的特性集合,另一个是具有这些属性的数学结构的特性别名集合。这样,为了支持系统,结构体只需实现每个相关属性,而要使用系统,可以简单地通过适合其需求的数学结构别名来约束泛型。

例如,要创建一个通用的Rational类型,你需要实现标准的CloneAddSubMulDivNegInvZeroOne特性和它们的赋值变体,就像正常实现一样。然后,通过实现新的特性AddCommutativeAddAssociativeMulCommutativeMulCommutativeDistributive,所有使用这些操作的别名(如RingMulMonoid)将自动实现并可用于该类型。

use maths_traits::algebra::*;

#[derive(Clone)] //necessary to auto-implement Ring and MulMonoid
#[derive(Copy, PartialEq, Eq, Debug)] //for convenience and displaying output
pub struct Rational {
    n: i32,
    d: i32
}

impl Rational {
    pub fn new(numerator:i32, denominator:i32) -> Self {
        let gcd = numerator.gcd(denominator);
        Rational{n: numerator/gcd, d: denominator/gcd}
    }
}

//Unary Operations from std::ops and num_traits

impl Neg for Rational {
    type Output = Self;
    fn neg(self) -> Self { Rational::new(-self.n, self.d) }
}

impl Inv for Rational {
    type Output = Self;
    fn inv(self) -> Self { Rational::new(self.d, self.n) }
}

//Binary Operations from std::ops

impl Add for Rational {
    type Output = Self;
    fn add(self, rhs:Self) -> Self {
        Rational::new(self.n*rhs.d + rhs.n*self.d, self.d*rhs.d)
    }
}

impl AddAssign for Rational {
    fn add_assign(&mut self, rhs:Self) {*self = *self+rhs;}
}

impl Sub for Rational {
    type Output = Self;
    fn sub(self, rhs:Self) -> Self {
        Rational::new(self.n*rhs.d - rhs.n*self.d, self.d*rhs.d)
    }
}

impl SubAssign for Rational {
    fn sub_assign(&mut self, rhs:Self) {*self = *self-rhs;}
}

impl Mul for Rational {
    type Output = Self;
    fn mul(self, rhs:Self) -> Self { Rational::new(self.n*rhs.n, self.d*rhs.d) }
}

impl MulAssign for Rational {
    fn mul_assign(&mut self, rhs:Self) {*self = *self*rhs;}
}

impl Div for Rational {
    type Output = Self;
    fn div(self, rhs:Self) -> Self { Rational::new(self.n*rhs.d, self.d*rhs.n) }
}

impl DivAssign for Rational {
    fn div_assign(&mut self, rhs:Self) {*self = *self/rhs;}
}

//Identities from num_traits

impl Zero for Rational {
    fn zero() -> Self {Rational::new(0,1)}
    fn is_zero(&self) -> bool {self.n==0}
}

impl One for Rational {
    fn one() -> Self {Rational::new(1, 1)}
    fn is_one(&self) -> bool {self.n==1 && self.d==1}
}

//algebraic properties from math_traits::algebra

impl AddAssociative for Rational {}
impl AddCommutative for Rational {}
impl MulAssociative for Rational {}
impl MulCommutative for Rational {}
impl Distributive for Rational {}

//Now, Ring and MulMonoid are automatically implemented for us

fn mul_add<R:Ring>(a:R, b:R, c:R) -> R { a*b + c }
use maths_traits::algebra::group_like::repeated_squaring;

let half = Rational::new(1, 2);
let two_thirds = Rational::new(2, 3);
let sixth = Rational::new(1, 6);

assert_eq!(mul_add(half, two_thirds, sixth), half);
assert_eq!(repeated_squaring(half, 7u32), Rational::new(1, 128));

此外,通过使用更抽象的IntegerGCDDomain约束,我们可以进行很大的泛化,以便有更多选择用于分子和分母,包括所有原始整数精度、各种大整数类型,甚至是多项式或函数等结构。

use maths_traits::algebra::*;

//Using a GCDDomain here means we can use more integral types, polynomials, and other types
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Rational<T:GCDDomain> {
    n:T, d:T
}

impl<T:GCDDomain> Rational<T> {
    pub fn new(numerator:T, denominator:T) -> Self {
        let gcd = numerator.clone().gcd(denominator.clone());
        Rational{n: numerator.divide(gcd.clone()).unwrap(), d: denominator.divide(gcd).unwrap()}
    }
}

//Standard operations remain basically the same as for the i32 case

impl<T:GCDDomain> Neg for Rational<T> {
    type Output = Self;
    fn neg(self) -> Self { Rational::new(-self.n, self.d) }
}

impl<T:GCDDomain> Inv for Rational<T> {
    type Output = Self;
    fn inv(self) -> Self { Rational::new(self.d, self.n) }
}

impl<T:GCDDomain> Add for Rational<T> {
    type Output = Self;
    fn add(self, rhs:Self) -> Self {
        Rational::new(self.n*rhs.d.clone() + rhs.n*self.d.clone(), self.d*rhs.d)
    }
}

impl<T:GCDDomain> AddAssign for Rational<T> {
    fn add_assign(&mut self, rhs:Self) {*self = self.clone()+rhs;}
}

impl<T:GCDDomain> Sub for Rational<T> {
    type Output = Self;
    fn sub(self, rhs:Self) -> Self {
        Rational::new(self.n*rhs.d.clone() - rhs.n*self.d.clone(), self.d*rhs.d)
    }
}

impl<T:GCDDomain> SubAssign for Rational<T> {
    fn sub_assign(&mut self, rhs:Self) {*self = self.clone()-rhs;}
}

impl<T:GCDDomain> Mul for Rational<T> {
    type Output = Self;
    fn mul(self, rhs:Self) -> Self { Rational::new(self.n*rhs.n, self.d*rhs.d) }
}

impl<T:GCDDomain> MulAssign for Rational<T> {
    fn mul_assign(&mut self, rhs:Self) {*self = self.clone()*rhs;}
}

impl<T:GCDDomain> Div for Rational<T> {
    type Output = Self;
    fn div(self, rhs:Self) -> Self { Rational::new(self.n*rhs.d, self.d*rhs.n) }
}

impl<T:GCDDomain> DivAssign for Rational<T> {
    fn div_assign(&mut self, rhs:Self) {*self = self.clone()/rhs;}
}

impl<T:GCDDomain+PartialEq> Zero for Rational<T> {
    fn zero() -> Self {Rational::new(T::zero(),T::one())}
    fn is_zero(&self) -> bool {self.n.is_zero()}
}

impl<T:GCDDomain+PartialEq> One for Rational<T> {
    fn one() -> Self {Rational::new(T::one(), T::one())}
    fn is_one(&self) -> bool {self.n.is_one() && self.d.is_one()}
}

impl<T:GCDDomain> AddAssociative for Rational<T> {}
impl<T:GCDDomain> AddCommutative for Rational<T> {}
impl<T:GCDDomain> MulAssociative for Rational<T> {}
impl<T:GCDDomain> MulCommutative for Rational<T> {}
impl<T:GCDDomain> Distributive for Rational<T> {}

//Now, we can use both 8-bit integers AND 64 bit integers

let half = Rational::new(1i8, 2i8);
let sixth = Rational::new(1, 6);
let two_thirds = Rational::new(2i64, 3i64);
let one_third = Rational::new(1i64, 3i64);

assert_eq!(half + sixth, Rational::new(2, 3));
assert_eq!(two_thirds + one_third, Rational::new(1, 1));

当前支持的构造

目前,maths_traits支持以下数学结构的特性

  • 类似群的代数结构:幺半群、群、阿贝尔群等
  • 类似环的代数结构:环、域、GCD域、欧几里得域等
  • 类似模的结构:向量空间、代数、双线性型等
  • 整数和自然数
  • 有序代数结构:有序/阿基米德环、域等
  • 实数和复数
  • 集合的度量特性:度量空间、内积、范数等

no_std

可以在不使用标准库的情况下使用maths-traits。要做到这一点,只需在不启用std功能的情况下编译您的项目,在您的Cargo.toml中禁用默认功能。

[dependencies]
maths-traits = {version = "0.2", default-features = false}

但是,请注意,与Real相关的所有特性的实现仅在使用std时才可用,因为浮点三角函数和对数函数仅在链接到标准库时才可用。

可能未来的功能

由于maths_traits仍在开发中,因此有一些功能可能在以后添加

  • 有限维或可数维且具有离散元素的向量空间的特性。这几乎肯定最终会被添加,但由于一些设计约束,目前尚未添加。
  • num crate中其他数学结构的可选默认实现。
  • 类似范畴的结构系统,即具有对其元素部分操作的集合。这相对简单添加,但到目前为止,似乎没有足够的使用案例来抵消添加的代码复杂性。
  • 集合、几何形状和集合测度系统。它们可能在将来被包含,但到目前为止,它们似乎不符合本项目的范围。

当然,如果任何人觉得应该添加任何特定功能,请随时在github存储库中提交问题或做出贡献。由于大多数未实现的功能都是由于有用性考虑而未实现的,如果请求的功能(并且是合理的),它可能在以后某个时候添加(尽管这不能保证)。

发布稳定性

目前,该项目仍在开发中,因此请注意,API将来可能会更改。我只是觉得这个crate的使用还不足以保证任何类型的特性冻结,因此我希望将其留在这个可能性范围内。

然而,就目前的情况来看,相当一部分子系统在我的个人使用中已经相对稳定了一段时间,因此我特别期望大多数的变化会发生在模块化、度量系统和整数系统上,所以未来不应该会有特别多的破坏性更改。

当然,如果在任何时刻引入了破坏API的更改,它将在语义版本控制中得到反映。

依赖项

~155KB