# Explore numerical computing in Swift

Description: Meet Swift Numerics: a new Swift package for computational mathematics. Take a tour of the protocols and types available in the package and find out how you can use them to write generic code. We'll also show you how and when to use the new Float16 type to improve performance and reduce memory usage. To get the most out of this session, you should have some familiarity with mathematics like logarithmic functions and real and imaginary numbers. You should also be familiar with generic programming in Swift. For more background, watch “Swift Generics (Expanded)” from WWDC18.

## Example: The Logit function

• This example uses the logit function from statistics.
• Essentially, given a probability `p` in the range 0...1, returns the log of the odds, `log(p / (1 - p))`.

You would implement the function like this:

``````import Darwin

func logit(_ p: Double) -> Double {
log(p) - log1p(-p)
}
``````

There's a small problem: this only supports `Double`s. What about `Float`s or any other floating-point type? Our function should try to be generic.

A reasonable first attempt might look like:

``````import Darwin

func logit<T>(_ p: T) -> T {
log(p) - log1p(-p)
}
``````

But this won't compile because the `log` and `log1p` functions only make sense for a handful of floating-point types.

We need a constraint for our generic type. The Numerics module provides a `Real` protocol, which allows our code to work for any standard floating-point type, current or future. This code calls generic versions of the `log` and `log1p` functions.

``````import Numerics

func logit<NumberType: Real>(_ p: NumberType) -> NumberType {
log(p) - log1p(-p)
}
``````

## The `Real` protocol

• Part of Swift Numerics package

The definition is:

``````public protocol Real: FloatingPoint, AlgebraicField, RealFunctions { ... }
``````

Note that `AlgebraicField` and `RealFunctions` are also new protocols defined in the Numerics package. However, `Real` is the one you should usually use.

### Numeric Protocols in the Swift standard library

These are the protocols already defined in the Swift standard library: This topic focuses on `AdditiveArithmetic`, `SignedNumeric`, and `FloatingPoint`

• `AdditiveArithmetic` applies to types that support addition and subtraction.
• `SignedNumeric` introduces multiplication.
• `FloatingPoint` adds many other operations for floating-point computation. This includes comparison functions, a way to decompose numbers into an exponent and significand, and useful constants like infinity or pi.

### Protocols in the Numerics package

The Numerics package builds on protocols already in the Swift standard library.

• `AlgebraicField` augments `SignedNumeric` by introducing division and reciprocation.
• `ElementaryFunctions` augments `AdditiveArithmetic` by adding a large collection of common floating-point functions, such as logarithms and trigonometric functions.
• `RealFunctions` extends `ElementaryFunctions` even further with some lesser-used functions, such as gamma and error functions.

The `Real` protocol combines all of these protocols into a single, unified concept: ### Implications of the `Real` protocol

• Generics constrained to `Real` support all standard floating-point types.
• Reduces code duplication.
• Simplifies maintenance.
• Makes defining new numerical types easier.

## The `Complex` type

• Part of the Swift Numerics package
• Generic over any `Real` type, so it works for any floating-point type.
``````import Numerics

let z = Complex(1.0, 2.0)  // z = 1 + 2i
``````

`Complex` defaults to `Double`, so the full type-annotated version of the code is:

``````import Numerics

let z: Complex<Double> = Complex(1.0, 2.0)  // z = 1 + 2i
``````

### Generic Numerical Programming

While `Complex` is a type by itself, it also enables generic numerical programming.

``````public struct Complex<NumberType> where NumberType: Real {
/// The real component
public var real: NumberType

/// The imaginary component
public var imaginary: NumberType

/// Construct a complex number with specified real and imaginary parts
public init (_ real: NumberType, imaginary: NumberType) {
self.real = real
self.imaginary = imaginary
}
}
`````` To make complex numbers fully functional, we need to extend them with the `SignedNumeric` protocol:

``````extension Complex: SignedNumeric {
/// The sum of 'z' and 'w'
public static func +(z: Complex, w: Complex) -> Complex {
Complex(z.real + w.real, z.imaginary + w.imaginary)
}

/// The difference of 'z' and ' w'
public static func -(z: Complex, w: Complex) -> Complex {
Complex (z.real - w.real, z.imaginary - w.imaginary)
}

/// The product of 'z' and
public static func *(z: Complex, w: Complex) -> Complex {
Complex(
z.real * w.real - z.imaginary * w.imaginary,
z.real * w.imaginary + z.imaginary * w.real
)
}
}
`````` Complex numbers are often expressed in polar coordinates (length + phase angle):

``````extension Complex {
/// The Euclidean norm (a.k.a. 2-norm) of the number.
public var length: NumberType {
.hypot(real, imaginary)
}

/// The phase (angle, or "argument").
///
/// Returns the angle (measured above the real axis) in radians.
public var phase: NumberType {
.atan2(y: imaginary, x: real)
}

/// A complex value with specified polar coordinates.
public init(length: Number Type, phase: Number Type) {
self = Complex (.cos(phase), .sin(phase)).multiplied(by: length)
}
}
`````` ### Compatibility with C and C++

`Complex` is a plain struct with 2 floating-point values, so its memory layout precisely matches the complex number types of C and C++. The following are all the same in memory:

• `Complex<Double>` (Swift)
• `_Complex double` (C)
• `std::complex<double>` (C++)

As a result, Swift code can exchange buffers of complex numbers with C/C++ APIs. You can just pass a pointer to a Swift `Complex` struct to a C/C++ library that also expects a complex type.

#### Example: Using Accelerate's BLAS functions

BLAS = Basic Linear Algebra Subroutines. Apple's implementation is written in C. Notice how the function accepts a pointer to the array of `Complex<Double>` numbers using the ampersand (`&`) operator.

``````import Numerics
import Accelerate

// Array of 100 random `Complex<Double>` numbers.
let z = (0 ..< 100).map {
Complex(length: 1.0, phase: Double.random(in: -.pi ... .pi))
}

// Compute the Euclidean norm of `z`.
let norm = cblas_dznrm2(z.count, &z, 1)
``````

#### One Caveat

The Numerics package treats complex infinity and NaN differently than in C or C++. This can affect code ported from C or C++.

However, Swift's treatment of these values is simpler and significantly more performant for multiplication and division. Below is a benchmark comparing Swift to C: ## Numerics is a work in progress

• Specialized handling for integer powers.
• Approximate equality.

Numerics is developed as a community on GitHub. Some of the projects being discussed include:

• Arbitrary-precision integers.
• Shaped arrays.
• Decimal floating point.

## `Float16`

Is a new floating-point type in the Swift standard library.

• IEEE 754 standard format.
• Calling convention for x86 is not yet finalized (working with Intel to fix this).

Is a normal floating-point type.

• Conforms to `BinaryFloatingPoint` and `SIMDScalar`.
• Conforms to `Real` from Swift Numerics.
• Supports all of the standard floating-point functions.

If you implement the `Real` protocol in your code, you will automatically get support for `Float16`.

Pros:

• Significant performance gains because you can fit more of them into a SIMD register or a page of memory.
• Interoperates with C/Objective-C `__fp16` type.

Cons:

• Low precision, small range.

### Size and Constraints

Be mindful of these constraints when porting code that was originally written with `Float` or `Double` in mind: ### Hardware Support

• Supported (and preferred) by Apple's GPUs.
• Supported by Apple's CPUs starting with A11 Bionic.
• Scalar performance identical to `Float` or `Double`.
• SIMD performance is 2x that of `Float`.
• `Float16` is simulated using `Float` operations on older hardware. The results are exactly the same, but without the speedup gains.

Here's a benchmark of `Float16` against `Float`: # Written by #### Sarthak Khillon

User-focused Software Engineer with a love for mobile platforms.