From 53af148f0f0ecbbcff4234c3fb024061d29d7166 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 13 Jan 2020 16:19:19 -0500 Subject: [PATCH 1/5] Introduce Field protocol For now, this mostly allows one to write code generic over Real and Complex types. TBD whether it should also include finite fields, or be restricted to characteristic-zero. Also bikeshedding to be done on exactly what operations should fall into this protocol and what it should be named. This is just to get discussion started. --- Sources/Complex/Arithmetic.swift | 4 ++- Sources/Real/Field.swift | 51 ++++++++++++++++++++++++++++++++ Sources/Real/Real.swift | 12 +++++++- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 Sources/Real/Field.swift diff --git a/Sources/Complex/Arithmetic.swift b/Sources/Complex/Arithmetic.swift index 2c003072..7e61641d 100644 --- a/Sources/Complex/Arithmetic.swift +++ b/Sources/Complex/Arithmetic.swift @@ -9,6 +9,8 @@ // //===----------------------------------------------------------------------===// +import Real + // MARK: - Additive structure extension Complex: AdditiveArithmetic { @_transparent @@ -62,7 +64,7 @@ extension Complex { } // MARK: - Multiplicative structure -extension Complex: Numeric { +extension Complex: Field { @_transparent public static func *(z: Complex, w: Complex) -> Complex { return Complex(z.x*w.x - z.y*w.y, z.x*w.y + z.y*w.x) diff --git a/Sources/Real/Field.swift b/Sources/Real/Field.swift new file mode 100644 index 00000000..020a3826 --- /dev/null +++ b/Sources/Real/Field.swift @@ -0,0 +1,51 @@ +//===--- Field.swift ------------------------------------------*- swift -*-===// +// +// This source file is part of the Swift Numerics open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +// TODO: refining Numeric is slightly annoying because Numeric has the +// magnitude property and associated type, which doesn't quite make sense +// for all fields. However, simply picking magnitude = self in those cases +// is probably a sensible workaround, so this isn't catastrophic. Still, +// slightly annoying. + +/// A type modeling an algebraic field. +/// +/// This protocol refines `Numeric`, adding division and reciprocal operations. +public protocol Field: Numeric { + + static func /=(a: inout Self, b: Self) + + static func /(a: Self, b: Self) -> Self + + /// The (approximate) reciprocal (mulitplicative inverse) of this number, if one is representable. + /// + /// If reciprocal is non-nil, you can replace division by self with multiplication by reciprocal and + /// either get exact the same result (for finite fields) or approximately the same result up to a + /// typical rounding error (for floating-point formats). + /// + /// If self is zero, or if a reciprocal would overflow or underflow such that it cannot be accurately + /// represented, the result is nil. Implementations should be *conservative*; it is OK to return + /// nil even in some cases where a reciprocal can be represented. For this reason, a default + /// implementation that simply always returns nil is provided. + var reciprocal: Self? { get } +} + +extension Field { + @_transparent + public static func /(a: Self, b: Self) -> Self { + var a = a + a /= b + return a + } + + public var reciprocal: Self? { + return nil + } +} diff --git a/Sources/Real/Real.swift b/Sources/Real/Real.swift index 7ef43748..27c49883 100644 --- a/Sources/Real/Real.swift +++ b/Sources/Real/Real.swift @@ -26,7 +26,8 @@ /// - /// - `ElementaryFunctions` /// - `RealFunctions` -public protocol Real: FloatingPoint, RealFunctions { +/// - `Field` +public protocol Real: FloatingPoint, RealFunctions, Field { } // While `Real` does not provide any additional customization points, @@ -79,4 +80,13 @@ extension Real { public static func sqrt(_ x: Self) -> Self { return x.squareRoot() } + + @inlinable + public var reciprocal: Self? { + let recip = 1/self + if recip.isNormal || isZero || !isFinite { + return recip + } + return nil + } } From 1879299216460d6406b1a71860ea11e1357ceed3 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Mon, 13 Jan 2020 20:56:48 -0500 Subject: [PATCH 2/5] Avoid shadowing function argument by using a different name for local var. --- Sources/Real/Field.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Real/Field.swift b/Sources/Real/Field.swift index 020a3826..8484b7c0 100644 --- a/Sources/Real/Field.swift +++ b/Sources/Real/Field.swift @@ -40,9 +40,9 @@ public protocol Field: Numeric { extension Field { @_transparent public static func /(a: Self, b: Self) -> Self { - var a = a - a /= b - return a + var result = a + result /= b + return result } public var reciprocal: Self? { From b6839260cb34caaed39e43405860888ac1c1f65a Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Thu, 16 Jan 2020 11:11:56 -0500 Subject: [PATCH 3/5] Add some clarification to documentation of .reciprocal. --- Sources/Real/Field.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Real/Field.swift b/Sources/Real/Field.swift index 8484b7c0..c61745d9 100644 --- a/Sources/Real/Field.swift +++ b/Sources/Real/Field.swift @@ -24,7 +24,7 @@ public protocol Field: Numeric { static func /(a: Self, b: Self) -> Self - /// The (approximate) reciprocal (mulitplicative inverse) of this number, if one is representable. + /// The (approximate) reciprocal (multiplicative inverse) of this number, if it is representable. /// /// If reciprocal is non-nil, you can replace division by self with multiplication by reciprocal and /// either get exact the same result (for finite fields) or approximately the same result up to a @@ -34,6 +34,10 @@ public protocol Field: Numeric { /// represented, the result is nil. Implementations should be *conservative*; it is OK to return /// nil even in some cases where a reciprocal can be represented. For this reason, a default /// implementation that simply always returns nil is provided. + /// + /// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil for `Real` or + /// `Complex` types, because these types have an `.infinity` value that acts as the + /// reciprocal of `.zero`. var reciprocal: Self? { get } } From 98f3d25fe8b1f395fea982c5bed35e9ab0ba536a Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Sun, 19 Jan 2020 15:53:35 -0500 Subject: [PATCH 4/5] Rename Field -> AlgebraicField We might walk this back in the future, but for the short-term let's avoid stomping on the generic term "Field". --- Sources/Complex/Arithmetic.swift | 2 +- Sources/Real/AlgebraicField.swift | 85 +++++++++++++++++++++++++++++++ Sources/Real/Field.swift | 55 -------------------- Sources/Real/Real.swift | 4 +- 4 files changed, 88 insertions(+), 58 deletions(-) create mode 100644 Sources/Real/AlgebraicField.swift delete mode 100644 Sources/Real/Field.swift diff --git a/Sources/Complex/Arithmetic.swift b/Sources/Complex/Arithmetic.swift index 7e61641d..1ecafbb5 100644 --- a/Sources/Complex/Arithmetic.swift +++ b/Sources/Complex/Arithmetic.swift @@ -64,7 +64,7 @@ extension Complex { } // MARK: - Multiplicative structure -extension Complex: Field { +extension Complex: AlgebraicField { @_transparent public static func *(z: Complex, w: Complex) -> Complex { return Complex(z.x*w.x - z.y*w.y, z.x*w.y + z.y*w.x) diff --git a/Sources/Real/AlgebraicField.swift b/Sources/Real/AlgebraicField.swift new file mode 100644 index 00000000..809f8bfa --- /dev/null +++ b/Sources/Real/AlgebraicField.swift @@ -0,0 +1,85 @@ +//===--- AlgebraicField.swift ---------------------------------*- swift -*-===// +// +// This source file is part of the Swift Numerics open source project +// +// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +/// A type modeling an algebraic [field]. Refines the `Numeric` protocol, +/// adding division. +/// +/// A field is a set on which addition, subtraction, multiplication, and +/// division are defined, and behave basically like those operations on +/// the real numbers. More precisely, a field is a commutative group under +/// its addition, the non-zero elements of the field form a commutative +/// group under its multiplication, and the distributitve law holds. +/// +/// Some common examples of fields include: +/// +/// - the rational numbers +/// - the real numbers +/// - the complex numbers +/// - the integers modulo a prime +/// +/// The most familiar example of a thing that is *not* a field is the integers. +/// This may be surprising, since integers seem to have addition, subtraction, +/// multiplication and division. Why don't they form a field? +/// +/// Because integer multiplication does not form a group; it's commutative and +/// associative, but integers do not have multiplicative inverses. +/// I.e. if a is any integer other than 1 or -1, there is no integer b such +/// that a*b = 1. The existence of inverses is requried to form a field. +/// +/// If a type `T` conforms to the `Real` protocol, then `T` and `Complex` +/// both conform to `AlgebraicField`. +/// +/// See Also: +/// - +/// - Real +/// - Numeric +/// - AdditiveArithmetic +/// +/// [field]: https://en.wikipedia.org/wiki/Field_(mathematics) +public protocol AlgebraicField: Numeric { + + static func /=(a: inout Self, b: Self) + + static func /(a: Self, b: Self) -> Self + + /// The (approximate) reciprocal (multiplicative inverse) of this number, + /// if it is representable. + /// + /// If reciprocal is non-nil, you can replace division by self with + /// multiplication by reciprocal and either get exact the same result + /// (for finite fields) or approximately the same result up to a typical + /// rounding error (for floating-point formats). + /// + /// If self is zero, or if a reciprocal would overflow or underflow such + /// that it cannot be accurately represented, the result is nil. Note that + /// `.zero.reciprocal`, somewhat surprisingly, is *not* nil for `Real` or + /// `Complex` types, because these types have an `.infinity` value that + /// acts as the reciprocal of `.zero`. + var reciprocal: Self? { get } +} + +extension AlgebraicField { + @_transparent + public static func /(a: Self, b: Self) -> Self { + var result = a + result /= b + return result + } + + /// Implementations should be *conservative* with the reciprocal property; + /// it is OK to return `nil` even in cases where a reciprocal could be + /// represented. For this reason, a default implementation that simply + /// always returns `nil` is correct, but conforming types should provide + /// a better implementation if possible. + public var reciprocal: Self? { + return nil + } +} diff --git a/Sources/Real/Field.swift b/Sources/Real/Field.swift deleted file mode 100644 index c61745d9..00000000 --- a/Sources/Real/Field.swift +++ /dev/null @@ -1,55 +0,0 @@ -//===--- Field.swift ------------------------------------------*- swift -*-===// -// -// This source file is part of the Swift Numerics open source project -// -// Copyright (c) 2019 Apple Inc. and the Swift Numerics project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// -//===----------------------------------------------------------------------===// - -// TODO: refining Numeric is slightly annoying because Numeric has the -// magnitude property and associated type, which doesn't quite make sense -// for all fields. However, simply picking magnitude = self in those cases -// is probably a sensible workaround, so this isn't catastrophic. Still, -// slightly annoying. - -/// A type modeling an algebraic field. -/// -/// This protocol refines `Numeric`, adding division and reciprocal operations. -public protocol Field: Numeric { - - static func /=(a: inout Self, b: Self) - - static func /(a: Self, b: Self) -> Self - - /// The (approximate) reciprocal (multiplicative inverse) of this number, if it is representable. - /// - /// If reciprocal is non-nil, you can replace division by self with multiplication by reciprocal and - /// either get exact the same result (for finite fields) or approximately the same result up to a - /// typical rounding error (for floating-point formats). - /// - /// If self is zero, or if a reciprocal would overflow or underflow such that it cannot be accurately - /// represented, the result is nil. Implementations should be *conservative*; it is OK to return - /// nil even in some cases where a reciprocal can be represented. For this reason, a default - /// implementation that simply always returns nil is provided. - /// - /// Note that `.zero.reciprocal`, somewhat surprisingly, is *not* nil for `Real` or - /// `Complex` types, because these types have an `.infinity` value that acts as the - /// reciprocal of `.zero`. - var reciprocal: Self? { get } -} - -extension Field { - @_transparent - public static func /(a: Self, b: Self) -> Self { - var result = a - result /= b - return result - } - - public var reciprocal: Self? { - return nil - } -} diff --git a/Sources/Real/Real.swift b/Sources/Real/Real.swift index 27c49883..859d025a 100644 --- a/Sources/Real/Real.swift +++ b/Sources/Real/Real.swift @@ -26,8 +26,8 @@ /// - /// - `ElementaryFunctions` /// - `RealFunctions` -/// - `Field` -public protocol Real: FloatingPoint, RealFunctions, Field { +/// - `AlgebraicField` +public protocol Real: FloatingPoint, RealFunctions, AlgebraicField { } // While `Real` does not provide any additional customization points, From 323542cba1cc93c3e2f81e02c1aed9ff45f58ea8 Mon Sep 17 00:00:00 2001 From: Stephen Canon Date: Sun, 19 Jan 2020 16:06:31 -0500 Subject: [PATCH 5/5] Add short notes to README docs on AlgebraicField. --- Sources/Complex/README.md | 2 +- Sources/Real/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/Complex/README.md b/Sources/Complex/README.md index 65195b5f..0666509e 100644 --- a/Sources/Complex/README.md +++ b/Sources/Complex/README.md @@ -7,7 +7,7 @@ This module provides a `Complex` number type generic over an underlying `RealTyp ``` This module provides approximate feature parity and memory layout compatibility with C, Fortran, and C++ complex types (although the importer cannot map the types for you, buffers may be reinterpreted to shim API defined in other languages). -The usual arithmetic operators are provided for Complex numbers, as well as conversion to and from polar coordinates and many useful properties, plus conformances to the obvious usual protocols: `Equatable`, `Hashable`, `Codable` (if the underlying `RealType` is), and `Numeric` (hence also `AdditiveArithmetic`). +The usual arithmetic operators are provided for Complex numbers, as well as conversion to and from polar coordinates and many useful properties, plus conformances to the obvious usual protocols: `Equatable`, `Hashable`, `Codable` (if the underlying `RealType` is), and `AlgebraicField` (hence also `AdditiveArithmetic` and `Numeric`). ### Dependencies: - The `Real` module. diff --git a/Sources/Real/README.md b/Sources/Real/README.md index 763eb092..562511b0 100644 --- a/Sources/Real/README.md +++ b/Sources/Real/README.md @@ -6,7 +6,7 @@ The `Real` module provides that API as a separate module so that you can use it ## Protocols and Methods -The module defines three protocols. The most general is `ElementaryFunctions`, which makes the following functions available: +The module defines four protocols. The most general is `ElementaryFunctions`, which makes the following functions available: - Exponential functions: `exp`, `expMinusOne` - Logarithmic functions: `log`, `log(onePlus:)` - Trigonometric functions: `cos`, `sin`, `tan` @@ -28,6 +28,9 @@ The `RealFunctions` protocol refines `ElementaryFunctions`, and adds operations The protocol that you will use most often is `Real`, which describes a floating-point type equipped with the full set of basic math functions. This is a great protocol to use in writing generic code, because it has all the basics that you need to implement most numeric functions. +The fourth protocol is `AlgebraicField`, which `Real` also refines. This protocol is a very small refinement of `Numeric`, adding the `/` and `/=` operators and a `reciprocal` property. +The primary use of this protocol is for writing code that is generic over real and complex types. + ## Using Real First, either import `Real` directly or import the `Numerics` umbrella module. @@ -70,3 +73,4 @@ Not having this protocol is a significant missing feature for numerical computin [ErrorFunction]: https://en.wikipedia.org/wiki/Error_function [GammaFunction]: https://en.wikipedia.org/wiki/Gamma_function [SE-0246]: https://github.com/apple/swift-evolution/blob/master/proposals/0246-mathable.md +[Sigmoid]: https://en.wikipedia.org/wiki/Sigmoid_function