Skip to content

Complex

Oleksandr Bretsko edited this page Dec 27, 2020 · 1 revision

Complex

A complex number represented by real and imaginary parts.

@frozen public struct Complex<RealType> where RealType: Real

TODO: introductory text on complex numbers

Implementation notes:

This type does not provide heterogeneous real/complex arithmetic, not even the natural vector-space operations like real * complex. There are two reasons for this choice: first, Swift broadly avoids mixed-type arithmetic when the operation can be adequately expressed by a conversion and homogeneous arithmetic. Second, with the current typechecker rules, it would lead to undesirable ambiguity in common expressions (see README.md for more details).

Unlike C's _Complex and C++'s std::complex<> types, we do not attempt to make meaningful semantic distinctions between different representations of infinity or NaN. Any Complex value with at least one non-finite component is simply "non-finite". In as much as possible, we use the semantics of the point at infinity on the Riemann sphere for such values. This approach simplifies the number of edge cases that need to be considered for multiplication, division, and the elementary functions considerably.

.magnitude does not return the Euclidean norm; it uses the "infinity norm" (max(|real|,|imaginary|)) instead. There are two reasons for this choice: first, it's simply faster to compute on most hardware. Second, there exist values for which the Euclidean norm cannot be represented (consider a number with .real and .imaginary both equal to RealType.greatestFiniteMagnitude; the Euclidean norm would be .sqrt(2) * .greatestFiniteMagnitude, which overflows). Using the infinity norm avoids this problem entirely without significant downsides. You can access the Euclidean norm using the length property.

Inheritance

AdditiveArithmetic, AlgebraicField, CustomDebugStringConvertible, CustomStringConvertible, Decodable, Differentiable, Encodable, Hashable

Nested Type Aliases

IntegerLiteralType

public typealias IntegerLiteralType = Int

Initializers

init(_:_:)

A complex number constructed by specifying the real and imaginary parts.

@_transparent public init(_ real: RealType, _ imaginary: RealType)

init(_:)

The complex number with specified real part and zero imaginary part.

@inlinable public init(_ real: RealType)

Equivalent to Complex(real, 0).

init(imaginary:)

The complex number with specified imaginary part and zero real part.

@inlinable public init(imaginary: RealType)

Equivalent to Complex(0, imaginary).

init(_:)

The complex number with specified real part and zero imaginary part.

@inlinable public init<Other: BinaryInteger>(_ real: Other)

Equivalent to Complex(RealType(real), 0).

init?(exactly:)

The complex number with specified real part and zero imaginary part, if it can be constructed without rounding.

@inlinable public init?<Other: BinaryInteger>(exactly real: Other)

init(integerLiteral:)

@inlinable public init(integerLiteral value: Int)

init(length:phase:)

Creates a complex value specified with polar coordinates.

@inlinable public init(length: RealType, phase: RealType)

Edge cases:

  • Negative lengths are interpreted as reflecting the point through the origin, i.e.:

    Complex(length: -r, phase: θ) == -Complex(length: r, phase: θ)
    
  • For any θ, even .infinity or .nan:

    Complex(length: .zero, phase: θ) == .zero
    
  • For any θ, even .infinity or .nan, if r is infinite then:

    Complex(length: r, phase: θ) == .infinity
    
  • Otherwise, θ must be finite, or a precondition failure occurs.

See also:

  • .length

  • .phase

  • .polar

Properties

normalized

A normalized complex number with the same phase as this value.

var normalized: Complex?

If such a value cannot be produced (because the phase of zero and infinity is undefined), nil is returned.

reciprocal

The reciprocal of this value, if it can be computed without undue overflow or underflow.

var reciprocal: Complex?

If z.reciprocal is non-nil, you can safely replace division by z with multiplication by this value. It is not advantageous to do this for an isolated division, but if you are dividing many values by a single denominator, this will often be a significant performance win.

Typical use looks like this:

func divide<T: Real>(data: [Complex<T>], by divisor: Complex<T>) -> [Complex<T>] {
  // If divisor is well-scaled, use multiply by reciprocal.
  if let recip = divisor.reciprocal {
    return data.map { $0 * recip }
  }
  // Fallback on using division.
  return data.map { $0 / divisor }
}

real

The real part of this complex value.

var real: RealType

If z is not finite, z.real is .nan.

imaginary

The imaginary part of this complex value.

var imaginary: RealType

If z is not finite, z.imaginary is .nan.

zero

The additive identity, with real and imaginary parts both zero.

var zero: Complex

See also:

  • .one

  • .i

  • .infinity

one

The multiplicative identity, with real part one and imaginary part zero.

var one: Complex

See also:

  • .zero

  • .i

  • .infinity

i

The imaginary unit.

var i: Complex

See also:

  • .zero

  • .one

  • .infinity

infinity

The point at infinity.

var infinity: Complex

See also:

  • .zero

  • .one

  • .i

conjugate

The complex conjugate of this value.

var conjugate: Complex

isFinite

True if this value is finite.

var isFinite: Bool

A complex value is finite if neither component is an infinity or nan.

See also:

  • .isNormal

  • .isSubnormal

  • .isZero

isNormal

True if this value is normal.

var isNormal: Bool

A complex number is normal if it is finite and either the real or imaginary component is normal. A floating-point number representing one of the components is normal if its exponent allows a full- precision representation.

See also:

  • .isFinite

  • .isSubnormal

  • .isZero

isSubnormal

True if this value is subnormal.

var isSubnormal: Bool

A complex number is subnormal if it is finite, not normal, and not zero. When the result of a computation is subnormal, underflow has occurred and the result generally does not have full precision. See also:

  • .isFinite

  • .isNormal

  • .isZero

isZero

True if this value is zero.

var isZero: Bool

A complex number is zero if both the real and imaginary components are zero.

See also:

  • .isFinite

  • .isNormal

  • .isSubnormal

magnitude

The ∞-norm of the value (max(abs(real), abs(imaginary))).

var magnitude: RealType

If you need the Euclidean norm (a.k.a. 2-norm) use the length or lengthSquared properties instead.

Edge cases:

  • If z is not finite, z.magnitude is .infinity.

  • If z is zero, z.magnitude is 0.

  • Otherwise, z.magnitude is finite and non-zero.

See also:

  • .length

  • .lengthSquared

canonicalized

A "canonical" representation of the value.

var canonicalized: Self

For normal complex numbers with a RealType conforming to BinaryFloatingPoint (the common case), the result is simply this value unmodified. For zeros, the result has the representation (+0, +0). For infinite values, the result has the representation (+inf, +0).

If the RealType admits non-canonical representations, the x and y components are canonicalized in the result.

This is mainly useful for interoperation with other languages, where you may want to reduce each equivalence class to a single representative before passing across language boundaries, but it may also be useful for some serialization tasks. It's also a useful implementation detail for some primitive operations.

description

var description: String

debugDescription

var debugDescription: String

length

The Euclidean norm (a.k.a. 2-norm, sqrt(real*real + imaginary*imaginary)).

var length: RealType

This property takes care to avoid spurious over- or underflow in this computation. For example:

let x: Float = 3.0e+20
let x: Float = 4.0e+20
let naive = sqrt(x*x + y*y) // +Inf
let careful = Complex(x, y).length // 5.0e+20

Note that it is still possible for this property to overflow, because the length can be as much as sqrt(2) times larger than either component, and thus may not be representable in the real type.

For most use cases, you can use the cheaper .magnitude property (which computes the ∞-norm) instead, which always produces a representable result.

Edge cases:

If a complex value is not finite, its .length is infinity.

See also:

  • .magnitude

  • .lengthSquared

  • .phase

  • .polar

  • init(r:θ:)

lengthSquared

The squared length (real*real + imaginary*imaginary).

var lengthSquared: RealType

This property is more efficient to compute than length, but is highly prone to overflow or underflow; for finite values that are not well-scaled, lengthSquared is often either zero or infinity, even when length is a finite number. Use this property only when you are certain that this value is well-scaled.

For many cases, .magnitude can be used instead, which is similarly cheap to compute and always returns a representable value.

See also:

  • .length

  • .magnitude

unsafeLengthSquared

var unsafeLengthSquared: RealType

phase

The phase (angle, or "argument").

var phase: RealType

Returns the angle (measured above the real axis) in radians. If the complex value is zero or infinity, the phase is not defined, and nan is returned.

Edge cases:

If the complex value is zero or non-finite, phase is nan.

See also:

  • .length

  • .polar

  • init(r:θ:)

polar

The length and phase (or polar coordinates) of this value.

var polar: (length: RealType, phase: RealType)

Edge cases:

If the complex value is zero or non-finite, phase is .nan. If the complex value is non-finite, length is .infinity.

See also:

  • .length

  • .phase

  • init(r:θ:)

Methods

+(z:w:)

@_transparent public static func +(z: Complex, w: Complex) -> Complex

-(z:w:)

@_transparent public static func -(z: Complex, w: Complex) -> Complex

+=(z:w:)

@_transparent public static func +=(z: inout Complex, w: Complex)

-=(z:w:)

@_transparent public static func -=(z: inout Complex, w: Complex)

*(z:w:)

@_transparent public static func *(z: Complex, w: Complex) -> Complex

/(z:w:)

@_transparent public static func /(z: Complex, w: Complex) -> Complex

*=(z:w:)

@_transparent public static func *=(z: inout Complex, w: Complex)

/=(z:w:)

@_transparent public static func /=(z: inout Complex, w: Complex)

==(a:b:)

@_transparent public static func ==(a: Complex, b: Complex) -> Bool

hash(into:)

@_transparent public func hash(into hasher: inout Hasher)
Types
Protocols
Global Typealiases
Global Variables
Global Functions
  • MACH_MSGH_BITS(_:_:)
  • MACH_MSGH_BITS_REMOTE(_:)
  • QCKMain(_:configurations:testCases:)
  • __allTests()
  • afterEach(_:)
  • afterSuite(_:)
  • allPass(_:)
  • allPass(_:_:)
  • assertClose(_:_:allowedError:file:line:)
  • assertClose(_:_:allowedError:worstError:file:line:)
  • be(_:)
  • beAKindOf(_:)
  • beAnInstanceOf(_:)
  • beCloseTo(_:within:)
  • beEmpty()
  • beFalse()
  • beFalsy()
  • beGreaterThan(_:)
  • beGreaterThanOrEqualTo(_:)
  • beIdenticalTo(_:)
  • beLessThan(_:)
  • beLessThanOrEqualTo(_:)
  • beNil()
  • beTrue()
  • beTruthy()
  • beVoid()
  • beforeEach(_:)
  • beforeSuite(_:)
  • beginWith(_:)
  • catchBadInstruction(block:)
  • catchBadInstruction(in:)
  • chain(_:_:)
  • contain(_:)
  • containElementSatisfying(_:_:)
  • context(_:flags:closure:)
  • crashIf(_:_:)
  • crashIfAnyFalse(_:_:)
  • crashIfAnyFalse(_:crashMessage:)
  • crashIfAnyFalse(_:message:)
  • crashIfAnyNotExpected(_:expected:_:)
  • crashIfAnyNotExpected(_:expected:crashMessage:)
  • crashIfAnyTrue(_:_:)
  • crashIfFalse(_:_:)
  • crashIfNegative(_:)
  • crashIfNil(_:)
  • crashIfNotEqual(_:)
  • crashIfNotEqual(_:_:)
  • crashIfNotExpected(_:expected:_:)
  • crashIfNotNil(_:)
  • describe(_:flags:closure:)
  • doubleForLoop(_:_:)
  • elementsEqual(_:)
  • elementsEqual(_:by:)
  • encode(encodable:)
  • endWith(_:)
  • equal(_:)
  • expect(_:file:line:)
  • expect(_:line:expression:)
  • fail(_:file:line:)
  • fail(_:line:)
  • fail(_:location:)
  • fcontext(_:flags:closure:)
  • fdescribe(_:flags:closure:)
  • fit(_:flags:file:line:closure:)
  • fitBehavesLike(_:flags:file:line:)
  • fitBehavesLike(_:flags:file:line:context:)
  • fitBehavesLike(_:flags:file:line:sharedExampleContext:)
  • gatherExpectations(silently:closure:)
  • gatherFailingExpectations(silently:closure:)
  • haveCount(_:)
  • isValidLength(_:)
  • it(_:flags:file:line:closure:)
  • itBehavesLike(_:flags:file:line:)
  • itBehavesLike(_:flags:file:line:context:)
  • itBehavesLike(_:flags:file:line:sharedExampleContext:)
  • match(_:)
  • matchError(_:)
  • pending(_:closure:)
  • postNotifications(_:fromNotificationCenter:)
  • prettyCollectionType(_:)
  • product(_:_:)
  • raiseException(named:reason:userInfo:closure:)
  • recordFailure(_:location:)
  • satisfyAllOf(_:)
  • satisfyAnyOf(_:)
  • sharedExamples(_:closure:)
  • stringify(_:)
  • succeed()
  • threadSingleton(_:)
  • throwAssertion()
  • throwError()
  • throwError(_:closure:)
  • throwError(closure:)
  • throwError(errorType:closure:)
  • uuid()
  • waitUntil(timeout:file:line:action:)
  • withAssertionHandler(_:file:line:closure:)
  • xcontext(_:flags:closure:)
  • xdescribe(_:flags:closure:)
  • xit(_:flags:file:line:closure:)
  • xitBehavesLike(_:flags:file:line:context:)
Operators
  • !=(lhs:rhs:)
  • !==(lhs:rhs:)
  • &&(left:right:)
  • **(lhs:rhs:)
  • <(lhs:rhs:)
  • <=(lhs:rhs:)
  • ==(lhs:rhs:)
  • ===(lhs:rhs:)
  • >(lhs:rhs:)
  • >=(lhs:rhs:)
  • ||(left:right:)
  • ~=(regex:input:)
  • ±(Float:)
  • ±(double:)
  • ±(lhs:rhs:)
  • √(double:)
  • ≈(lhs:rhs:)

Clone this wiki locally