Swift Numerics

repository·main·Indexed 23 days ago

https://github.com/apple/swift-numerics

A collection of specialized modules for numerical computing in Swift, providing support for complex numbers, real number protocols, and integer utilities. It includes the RealModule for generic mathematical programming via protocols like ElementaryFunctions and Real, the ComplexModule for complex number arithmetic and transcendental functions, and IntegerUtilities for operations such as GCD, saturating arithmetic, and Euclidean division. The library can be accessed via the umbrella Numerics module or as individual fine-grained modules.

Tokens
6.2K
Snippets
16
Records
54
Agent score
82%

What's inside swift-numerics

  1. Use the ElementaryFunctions protocol for basic math functions

    main

    The ElementaryFunctions protocol provides access to a wide range of mathematical building blocks, including exponential, logarithmic, trigonometric, hyperbolic, and power/root functions. Any type conforming to ElementaryFunctions makes these functions available as static methods.

    Note that ElementaryFunctions conformance implies AdditiveArithmetic, so standard addition, subtraction, and the zero property are also available on these types.

    let x: Float = 1
    let y = Float.sin(x) // 0.84147096
  2. Understand the purpose of Numerics Shims

    main

    The NumericsShims module is an internal implementation detail used by other Swift Numerics modules. It provides access to Swift builtins and assembly by wrapping them in static inline C functions.

    Note: This module provides no stable Swift API and is not intended for direct use by end-users.

  3. Understand the RealModule protocol hierarchy

    main

    The RealModule provides a hierarchy of protocols to enable generic mathematical programming.

    • ElementaryFunctions: The most general protocol for basic math. It provides exponential (exp, expMinusOne), logarithmic (log, log(onePlus:)), trigonometric (cos, sin, tan), inverse trigonometric (acos, asin, atan), hyperbolic (cosh, sinh, tanh), inverse hyperbolic (acosh, asinh, atanh), and power/root functions (pow, sqrt, root). It refines AdditiveArithmetic.
    • RealFunctions: Refines ElementaryFunctions. It adds operations specific to real numbers like atan2(y:x:), hypot, error functions (erf, erfc), base-2/base-10 functions (exp2, exp10, log2, log10), and Gamma functions (gamma, logGamma, signGamma).
    • Real: The primary protocol for most users. It refines RealFunctions and describes a floating-point type equipped with the full set of basic math functions. Use this for writing generic numeric code.
    • AlgebraicField: Refines Real. It adds /, /=, and a reciprocal property. Use this when writing code that must be generic over both real and complex types.
  4. Understand complex division implementation in Swift Numerics

    main

    Swift Numerics implements complex division (/) using a two-path approach to balance performance and numerical stability.

    The Fast Path

    When the divisor w is 'well-scaled' (meaning its squared length w.lengthSquared is a normal floating-point number), the library uses a simplified formula: z * w.conjugate / w.lengthSquared. This is computationally efficient and performs similarly to complex multiplication.

    The Slow Path

    If the divisor is not well-scaled (risking overflow or underflow in intermediate steps), the library switches to a scaling algorithm based on Doug Priest's 'Efficient Scaling for Complex Division'. It scales both the numerator and denominator by a factor s (a power of the radix) to ensure the reciprocal can be computed without losing precision or triggering premature overflow/underflow. This ensures that the division is as robust as multiplication.

  5. Accuracy of Complex multiplication and division

    main

    The library prioritizes robust division and multiplication with small relative error in a complex norm, rather than guaranteeing small componentwise errors.

    This approach allows the use of fast, naive formulas for the common case, with additional checks to prevent spurious overflow or underflow. While componentwise error bounds are not guaranteed, the implementation is designed to be as accurate as standard real-number multiplication.

  6. Avoid overflow and underflow when calculating length

    main

    When working with very large or very small complex numbers, a naive implementation of the Euclidean norm (sqrt(x*x + y*y)) can fail. For example, with Float, a value like 1e20 would cause x*x to overflow to infinity, even though the square root of the sum is representable.

    Swift Numerics' .length property handles this automatically by using a robust two-step algorithm that avoids these spurious errors.

    let z = Complex<Float>(1e20, 1e20)
    z.length // 1.41421358E+20
    
    let w = Complex<Float>(1e-24, 1e-24)
    w.length // 1.41421362E-24
  7. Understand Complex number arithmetic and type inference

    main

    The Complex module does not provide heterogeneous arithmetic operators (e.g., adding a RealType to a Complex number like z + x).

    This design choice avoids ambiguous type inference. For example, in a context where Complex is expected, an expression like 2 * a (where a is a RealType) could be interpreted as either a RealType or a Complex number, leading to unexpected behavior. To perform arithmetic between real and complex numbers, you should explicitly convert the real value to a Complex type.

  8. What are Relaxed operations and when to use them

    main

    Floating-point arithmetic is not associative due to rounding and special values like infinity and NaN. This prevents compilers from reordering operations for optimizations like vectorization or instruction-level parallelism.

    Relaxed operations provide a way to tell the compiler that the specific order of arithmetic operations is incidental to your algorithm. Using these operations can unlock significant performance optimizations (such as 8x speedups on certain hardware) by allowing the compiler to reassociate arithmetic and use vector instructions, without requiring unsafe code or special compiler flags.

    // Standard non-associative behavior
    let ε = Double.leastNormalMagnitude
    let sumLeft  = (-1 + 1) + ε  //  0 + ε = ε
    let sumRight =  -1 + (1 + ε) // -1 + 1 = 0
    
    let ∞ = Double.infinity
    let productLeft  = (ε * ε) * ∞  // 0 * ∞ = .nan
    let productRight =  ε * (ε * ∞) // ε * ∞ = ∞