Swiftpack.co - Package - apple/swift-numerics

Swift Numerics

Introduction

Swift Numerics provides a set of modules that support numerical computing in Swift. These modules fall broadly into two categories:

  • API that is too specialized to go into the standard library, but which is sufficiently general to be centralized in a single common package.
  • API that is under active development toward possible future inclusion in the standard library.

There is some overlap between these two categories, and an API that begins in the first category may migrate into the second as it matures and new uses are discovered.

Swift Numerics modules are fine-grained. For example, if you need support for Complex numbers, you can import ComplexModule¹ as a standalone module:

import ComplexModule

let z = Complex<Double>.i

There is also a top-level Numerics module that re-exports the complete public interface of Swift Numerics:

import Numerics

// The entire Swift Numerics API is now available

Swift Numerics modules have minimal dependencies on other projects.

The current modules assume only the availability of the Swift and C standard libraries and the runtime support provided by compiler-rt.

Future expansion may assume the availability of other standard interfaces, such as BLAS (Basic Linear Algebra Subprograms) and LAPACK (Linear Algebra Package), but modules with more specialized dependencies (or dependencies that are not available on all platforms supported by Swift) belong in a separate package.

Because we intend to make it possible to adopt Swift Numerics modules in the standard library at some future point, Swift Numerics uses the same license and contribution guidelines as the Swift project.

Using Swift Numerics in your project

To use Swift Numerics in a SwiftPM project:

  1. Add the following line to the dependencies in your Package.swift file:
.package(url: "https://github.com/apple/swift-numerics", from: "0.0.7"),
  1. Add Numerics as a dependency for your target:
.target(name: "MyTarget", dependencies: [
  .product(name: "Numerics", package: "swift-numerics"),
  "AnotherModule"
]),
  1. Add import Numerics in your source code.

Contributing to Swift Numerics

Swift Numerics is a standalone library that is separate from the core Swift project, but it will sometimes act as a staging ground for APIs that will later be incorporated into the Swift Standard Library. When that happens, such changes will be proposed to the Swift Standard Library using the established evolution process of the Swift project.

Swift Numerics uses GitHub issues to track bugs and features. We use pull requests for development.

How to propose a new module

  1. Raise an issue with the [new module] tag.
  2. Raise a PR with an implementation sketch.
  3. Once you have some consensus, ask an admin to create a feature branch against which PRs can be raised.
  4. When the design has stabilized and is functional enough to be useful, raise a PR to merge the new module to master.

How to propose a new feature for an existing module

  1. Raise an issue with the [enhancement] tag.
  2. Raise a PR with your implementation, and discuss the implementation there.
  3. Once there is a consensus that the new feature is desirable and the design is suitable, it can be merged.

How to fix a bug, or make smaller improvements

  1. Raise a PR with your change.
  2. Make sure to add test coverage for whatever changes you are making.

Forums

Questions about how to use Swift Numerics modules, or issues that are not clearly bugs can be discussed in the "Swift Numerics" section of the Swift forums.

Modules

  1. RealModule
  2. ComplexModule

Future expansion

  1. Large Fixed-Width Integers
  2. Arbitrary-Precision Integers
  3. Shaped Arrays
  4. Decimal Floating-point

Notes

¹ Swift is currently unable to use the fully-qualified name for types when a type and module have the same name (discussion here: https://forums.swift.org/t/pitch-fully-qualified-name-syntax/28482). This would prevent users of Swift Numerics who don't need generic types from doing things such as:

import Complex
// I know I only ever want Complex<Double>, so I shouldn't need the generic parameter.
typealias Complex = Complex.Complex<Double> // This doesn't work, because name lookup fails.

For this reason, modules that would have this ambiguity are suffixed with Module within Swift Numerics:

import ComplexModule
// I know I only ever want Complex<Double>, so I shouldn't need the generic parameter.
typealias Complex = ComplexModule.Complex<Double>
// But I can still refer to the generic type by qualifying the name if I need it occasionally:
let a = ComplexModule.Complex<Float>

The Real module does not contain a Real type, but does contain a Real protocol. Users may want to define their own Real type (and possibly re-export the Real module)--that is why the suffix is also applied there. New modules have to evaluate this decision carefully, but can err on the side of adding the suffix. It's expected that most users will simply import Numerics, so this isn't an issue for them.

Github

link
Stars: 997

Dependencies

Used By

Total: 0

Releases

Xcode 12 support -

This is a quick-fix release to address a bug reported with Float16 availability in Xcode 12 on Catalina. No puns, sorry 😂

This time will be different...iable? -

Hey all, lots of updates in this release:

First, the big features:

  • Differentiable and Float16 support is live in Swift Numerics. These are protected behind swift version checks, so you'll only see them when you're building with a Swift 5.3 or later toolchain (because earlier toolchains don't have the necessary support). For Float16, you need to also be targeting a platform with support in the standard library (i.e. not macOS). For Differentiable, you need to be using a toolchain that was built with differentiable programming support enabled.

  • Approximate equality comparisons have been added for types conforming to Numeric when the associatedtype Magnitude conforms to FloatingPoint. This includes all of the Real types, but also includes Complex, and can be extended to lots of other useful types as well. The most basic use is:

    a.isApproximatelyEqual(to: b)
    

    which performs a comparison with default relative tolerance. You can manually specify an absolute or relative tolerance (or both), as well as a norm to use via overloads and optional arguments. Consult the doc comments for more information now, but I plan to write a more in-depth README for the feature in the coming weeks.

Two implementation details have also been added on Real, which are useful for building out other parts of the library:

  • cosMinusOne(_ x: Self) -> Self This is "just expMinusOne(_:Self), but for cosine". It's a somewhat niche feature (and trivially implementable via the half-angle formula), but it turns out to be useful when building out ElementaryFunctions support for Complex types, so it seems to make good sense to make more broadly available.
  • _mulAdd(_ a: Self, _ b: Self, _ c: Self) -> Self This is intended to use either an FMA or separate multiply and add to evaluate a*b + c as efficiently as possible. This is a much more niche implementation detail, and the name is subject to change (hence the underscore), but it's useful enough to expose for anyone who wants to use it.

Speaking of ElementaryFunctions support for Complex types, that's something that's in progress now (https://github.com/apple/swift-numerics/pull/146). I'll be working through it over the next few weeks, and would love to have contributions. If you'd like to contribute, be sure to read the implementation goals. Simply borrowing from C or C++ implementations will probably not be sufficient for the goals we have and semantics defined for Complex, but you can use them as a starting point if you're willing to iterate with me a bit.

Thanks everyone!

Forums discussion here: https://forums.swift.org/t/0-0-7-release-notes/39680

Fenestration -

There are a bunch of small changes in this release:

  • Added Windows support via CMake build system, contributed by @compnerd (special thanks for your patience!)

  • I removed the Complex benchmarks from the test targets. Semantically, benchmarks are not the same as tests, and we don't need these to run in the same conditions. Further, the Xcode benchmark infrastructure isn't quite what we want for these, and they don't work on Windows anyway, because clang does not support C complex multiplication or division on Windows. These benchmarks will return in the next release in the form of an executable target.

  • Complex<T> now conforms to Differentiable if you are using a toolchain that supports Differentiable programming and T is Differentiable. Thanks to @rxwei and @dan-zheng for their work on this.

  • Some documentation fixes for Complex fell out from work that @markuswntr has been doing on Quaternions (check out the Quaternions branch to play with it, and help review his PRs)

  • I fixed a bug in pow(Float/Double, Int) for exceptionally large exponents. It's unlikely that this ever caused any real problems, because it only occurs when the exponent is so large that the result should always be 0, 1, or infinity, but now we get the right answer for these cases.

A reminder that Float16 support is available on the swift-5.3 branch, which will be merged to master when swift-5.3 is released. When that happens, I will also create a LTS swift-5.2 branch to support older toolchains that do not have Float16 support.

Any questions or problems can be discussed on the GitHub repo or on the Swift Forums: https://forums.swift.org/t/0-0-6-release-notes/38146

I saw the Sign, and it opened up my mind -

There are two changes in this release:

  • AlgebraicField now refines SignedNumeric instead of Numeric. This should have no visible change for most users, because all conforming types (Float, Double, Float80, and Complex) already conform to SignedNumeric. However, users who have code that is generic over the AlgebraicField protocol can now use unary negation (or remove existing explicit SignedNumeric constraints from that code).

  • The Real and Complex modules have been renamed RealModule and ComplexModule. If you import Numerics, then this change does not affect you. However, if you currently import either Real or Complex directly, you will need to update your import statements. (sorry!)

    This is not a change that I make lightly; I would very much prefer to avoid this sort of churn, even though Swift Numerics hasn't yet declared 1.0. However, there are real limitations of the current name lookup system, which prevents use of some nice patterns when a module name shadows a type.

    E.g. with this change, a user who mostly only wants to work with complex doubles can do the following:

    import ComplexModule
    
    typealias Complex = ComplexModule.Complex<Double>
    
    // Can now use the simpler name Complex for Complex<Double>:
    func foo(_ z: Complex) -> Complex {
      ...
    }
    
    // But can still get at the generic type when necessary:
    let a = ComplexModule.Complex<Float>
    

    Any Swift Numerics module that might have this ambiguity will be suffixed with Module in the future (just like the pattern of protocols being suffixed with Protocol when necessary to break ambiguity).

As always, if you have any questions or comments, there is a forums thread for discussing this release.

We will not be Fielding further questions at this time -

There are two changes in this release to call out specifically:

  • Complex's conformance to Codable now uses an unkeyed container, so e.g. .zero gets encoded as [0,0] in JSON instead of the old {"x":0, "y":0}.

  • There's a new protocol, AlgebraicField refining Numeric by adding the /, /= operators, and a .reciprocal: Self? property. The Real protocol refines it and Complex<T> conforms to it, allowing you to write code that is generic over them. There are some additional operations that it probably makes sense to add to this protocol (e.g. square root); I expect to add them gradually as needed.

I also want to draw special attention to a change that I expect to make in the next tag. In order to make certain use patterns simpler, I intend to give the Complex module a name that doesn't conflict with the Complex type. If you have opinions about this change, or suggestions for a naming pattern to adopt for modules in Swift Numerics, please weigh in on that thread.

If you have any questions, use this discussion thread on the Swift forums.

We have the power -

In earlier releases, Real.pow(Self, Int) simply called through to libm_pow. This is generally acceptable on platforms with a decent math library, but gets some cases wrong for Float and Double when the exponent is so large that it would be rounded in conversion to Self.

For example, consider Float.pow(-1, 0x1000001). Since the exponent is odd, the result should be -1, but when we simply called libm_pow(-1, Float(0x1000001)), the exponent was rounded to an even number, and the result was 1.

This behavior is fixed in this release; in particular the parity of integer exponents is always preserved, so that we will not have sign errors like above. There is still additional work to be done on the Real.pow implementations--especially to provide better support for platforms with suspect math libraries--but this is a significant improvement to these operations.

Complex API cleanup -

There are two significant API changes to the Complex module in this release:

  1. The unsafeLengthSquared property has been renamed lengthSquared, because--while it can overflow or underflow--the result is always well-defined, and therefore does not lead to memory unsafety. The old property is still present, marked unavailable, so you should get a useful renaming message from the compiler or your IDE. The old property will be removed after a period of time.

  2. The Complex(length:phase:) initializer has been made non-optional. Instead it is now a precondition that the length must be either zero or infinite if phase is not finite. Updating code to account for this change should be quite straightforward, because you are unlikely to have been using the paths that could return nil previously. If you have any questions, please ask for assistance on the forums.

Linux testing improvements -

Adds an empty Tests/LinuxMain.swift to fix building on Linux, and directs users to --enable-test-discovery for testing on Linux.

Initial tag -

I haven't yet formally defined the release policy for this repo, but it's useful for SPM workflows to have a tag exist, so I'm cutting 0.0.0 with the first few rounds of bug fixes and cleanups from open source contributors.

Thanks to everyone who has jumped to contribute!