arithmetic API

Luna-Flow/linear-algebra/arithmetic is the scalar-operation layer of the repository. It re-exports the scalar types and traits that linear-algebra code uses from Luna-Flow/luna-generic and Luna-Flow/arithmetic, and adds five small operation traits: Abs, ApproxEq, CheckedDiv, CheckedSqrt and CheckedCompare. An operation trait says that an operation is available; it does not claim algebraic laws.

Source: src/arithmetic/operation_traits.mbt, src/arithmetic/alias.mbt. The reasoning is in the arithmetic design.

Import

The examples on this page use these aliases:

///|
import {
  "Luna-Flow/linear-algebra/arithmetic" @la_arithmetic,
  "Luna-Flow/arithmetic" @lf_arith,
}

Re-exported names

The package re-exports these upstream names with pub using, so @la_arithmetic.Sqrt and @lf_arith.Sqrt denote the same trait. Their behaviour is documented upstream: luna-generic and arithmetic.

NameKindFromMeaning
Zerotraitluna-genericadditive identity zero()
Onetraitluna-genericmultiplicative identity one()
Inversetraitluna-genericmultiplicative inverse inv(x)
Conjugatetraitluna-genericinvolution conjugate(x); the builtin real types have no instance
Sqrttraitarithmeticunchecked sqrt(x)
Cbrttraitarithmeticunchecked cbrt(x)
Powertraitarithmeticunchecked pow(x, y)
Exponentialtraitarithmeticexp(x), exp2(x)
Logarithmictraitarithmeticln(x), log2(x), log10(x)
Constantstraitarithmeticpi(), tau(), e()
SqrtCheckedtraitarithmeticsqrt_checked(x, ctx) returning Result
DivCheckedtraitarithmeticdiv_checked(x, y, ctx) returning Result
CompareCheckedtraitarithmeticcompare_checked(x, y) returning Result[Int, _]
ArithmeticContexttypearithmeticprecision and rounding settings passed to checked operations
ArithmeticErrortypearithmeticstructured scalar error with kind and message
ArithmeticErrorKindtypearithmeticDivisionByZero, DomainError, UnorderedComparison, …
FpClasstypearithmeticFinite, Infinity, NaN
RoundingModetypearithmeticToNearestEven, TowardZero, …

Absolute value

Abs

Abs marks scalar types with an absolute value.

pub(open) trait Abs {
  fn abs(Self) -> Self
}
pub impl Abs for Int
pub impl Abs for Float
pub impl Abs for Double

Abs::abs

Abs::abs returns ∣x∣|x|.

fn Abs::abs(Self) -> Self

The implementations delegate to Num::abs of luna-generic. For Int, ∣−231∣|-2^{31}| wraps to −231-2^{31}, as in two’s-complement arithmetic.

///|
fn[T : @la_arithmetic.Abs] arith_api_magnitude(x : T) -> T {
  @la_arithmetic.Abs::abs(x)
}

///|
test "Abs on integers and doubles" {
  inspect(arith_api_magnitude(-3), content="3")
  inspect(arith_api_magnitude(-2.5), content="2.5")
}

Approximate equality

ApproxEq

ApproxEq marks scalar types with an approximate comparison.

pub(open) trait ApproxEq {
  fn approx_eq(Self, Self) -> Bool
}
pub impl ApproxEq for Int
pub impl ApproxEq for Float
pub impl ApproxEq for Double

ApproxEq::approx_eq

ApproxEq::approx_eq reports whether two values are within a fixed absolute tolerance of each other.

fn ApproxEq::approx_eq(Self, Self) -> Bool
TypeRule
Inta=ba = b
Float∣a−b∣≤10−6\lvert a - b\rvert \le 10^{-6}
Double∣a−b∣≤10−12\lvert a - b\rvert \le 10^{-12}

The tolerance is absolute, so it is too strict for large magnitudes and too loose for tiny ones, and the relation is not transitive. It returns false whenever an operand is NaN. See the design page for the consequences.

///|
test "ApproxEq uses an absolute tolerance" {
  inspect(
    @la_arithmetic.ApproxEq::approx_eq(1.0, 1.0 + 1.0e-13),
    content="true",
  )
  inspect(
    @la_arithmetic.ApproxEq::approx_eq(1.0e20, 1.0e20 + 1.0e5),
    content="false",
  )
  inspect(@la_arithmetic.ApproxEq::approx_eq(3, 3), content="true")
}

Checked operations

The checked traits return Result[_, ArithmeticError] instead of a NaN or an infinity. The implementations for Float and Double delegate to the upstream DivChecked, SqrtChecked and CompareChecked traits; they accept an ArithmeticContext for interface uniformity but ignore it, because hardware binary floating point has a fixed precision.

CheckedDiv

CheckedDiv marks scalar types with a division that reports invalid operands.

pub(open) trait CheckedDiv {
  fn checked_div(Self, Self, @Luna-Flow/arithmetic.ArithmeticContext) -> Result[Self, @Luna-Flow/arithmetic.ArithmeticError]
}
pub impl CheckedDiv for Float
pub impl CheckedDiv for Double

CheckedDiv::checked_div

CheckedDiv::checked_div(x, y, ctx) returns Ok(x / y) or an error.

fn CheckedDiv::checked_div(Self, Self, ArithmeticContext) -> Result[Self, ArithmeticError]
OperandsResult
0/00 / 0Err, kind DomainError
±∞/±∞\pm\infty / \pm\inftyErr, kind DomainError
x/0x / 0, x≠0x \ne 0Err, kind DivisionByZero
otherwiseOk(x / y), rounded to nearest

CheckedSqrt

CheckedSqrt marks scalar types with a square root that reports domain errors.

pub(open) trait CheckedSqrt {
  fn checked_sqrt(Self, @Luna-Flow/arithmetic.ArithmeticContext) -> Result[Self, @Luna-Flow/arithmetic.ArithmeticError]
}
pub impl CheckedSqrt for Float
pub impl CheckedSqrt for Double

CheckedSqrt::checked_sqrt

CheckedSqrt::checked_sqrt(x, ctx) returns Ok(√x) for x≥0x \ge 0 and an error with kind DomainError for x<0x < 0.

fn CheckedSqrt::checked_sqrt(Self, ArithmeticContext) -> Result[Self, ArithmeticError]

A NaN argument is passed through as Ok(NaN).

CheckedCompare

CheckedCompare marks scalar types with a three-way comparison that reports unordered operands.

pub(open) trait CheckedCompare {
  fn checked_compare(Self, Self) -> Result[Int, @Luna-Flow/arithmetic.ArithmeticError]
}
pub impl CheckedCompare for Float
pub impl CheckedCompare for Double

CheckedCompare::checked_compare

CheckedCompare::checked_compare(x, y) returns Ok(-1), Ok(0) or Ok(1) for x<yx < y, x=yx = y and x>yx > y, and an error with kind UnorderedComparison when either operand is NaN.

fn CheckedCompare::checked_compare(Self, Self) -> Result[Int, ArithmeticError]

The three checked traits together:

///|
test "checked scalar operations" {
  let ctx = @lf_arith.ArithmeticContext::new(53)
  inspect(
    @la_arithmetic.CheckedDiv::checked_div(6.0, 2.0, ctx).unwrap(),
    content="3",
  )
  match @la_arithmetic.CheckedDiv::checked_div(1.0, 0.0, ctx) {
    Err(e) => inspect(e.is_division_by_zero(), content="true")
    Ok(_) => fail("1 / 0 must fail")
  }
  match @la_arithmetic.CheckedSqrt::checked_sqrt(-4.0, ctx) {
    Err(e) => inspect(e.is_domain_error(), content="true")
    Ok(_) => fail("sqrt(-4) must fail")
  }
  inspect(
    @la_arithmetic.CheckedCompare::checked_compare(2.0, 3.0).unwrap(),
    content="-1",
  )
  let nan = 0.0 / 0.0
  inspect(
    @la_arithmetic.CheckedCompare::checked_compare(nan, 1.0) is Err(_),
    content="true",
  )
}

Where the traits are used

The concrete matrix packages take their scalar bounds from luna-generic (Zero, AddMonoid, Semiring, Field, Num) and from Sqrt. The @mutable numerical routines use Sqrt through this re-export and their own Tolerance trait (see the mutable API). The local traits Abs, ApproxEq and the checked traits are building blocks for downstream algorithms; no matrix method of this repository requires them.