Menu

Switch to the dark mode that's kinder on your eyes at night time.

Switch to the light mode that's kinder on your eyes at day time.

Switch to the dark mode that's kinder on your eyes at night time.

Switch to the light mode that's kinder on your eyes at day time.

in ,

Swift Numerics, Hacker News

Swift Numerics, Hacker News


                

I’m excited to announce a new open-source project for the Swift ecosystem,Swift Numerics! Swift Numerics will provide the building blocks of numerical computing in Swift, as a set of fine-grained modules bundled together into a single Swift package. My hope is that we can quickly fill some important gaps in the Standard Library’s existing APIs, and unlock new domains of programming to the Swift language.

I’ve seeded the repository with two much-requested modules that are immediately useful for computational mathematics:Real(providing the functionality ofSE – 0246) andComplex(providing complex numbers and arithmetic). Let’s take a look at what they do:

  

About the author: Steve Canon is a member of Apple’s Swift Standard Library team. Previously he spent a decade working on Apple’s math library and the Accelerate framework.

Real Numbers

SE – 0246proposed an API for “basic math functions” that would make operations like sine and logarithm available in generic contexts. It was accepted, but because of limitations in the compiler, the API cannot yet be added to the Standard Library in a source-stable manner. TheRealmoduleprovides that API as a separate module so that you can use it right away to get access to the improved API for these operations in your projects.

The module defines three protocols. The most general isElementaryFunctions, which makes the following functions available:

  • Exponential functions:exp,expMinusOne
  •   

  • Logarithmic functions:log,log (onePlus:)
  •   

  • Trigonometric functions:cos,sin,tan
  •   

  • Inverse trigonometric functions:acos,asin,atan
  •   

  • Hyperbolic functions:cosh,sinh,tanh
  •   

  • Inverse hyperbolic functions:acosh,asinh,atanh
  •   

  • Power and root functions:pow,sqrt, (root)

TheRealFunctionsprotocol refinesElementaryFunctions, and adds operations that are difficult to define or implement over fields more general than the real numbers:

  • atan2 (y: x:), which computesatan (y / x)with sign chosen by the quadrant of the point(x, y)in the Cartesian plane.
  •   

  • Hypot, which computessqrt (x * x y * y)without intermediate overflow or underflow.
  •   

  • erfanderfc, theerror functionand its complement.
  •   

  • Exponential functions:exp2andexp 10
  •   

  • Logarithmetic functions:log2andlog 10
  •   

  • Gamma functions:gamma,logGamma, andsignGamma, which evaluate thegamma function, its logarithm, and its sign.

The protocol that you will use most often isReal, 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. Suppose we were experimenting with some basic machine learning, and needed a genericsigmoid functionactivation function:

importNumericsFUNCSigmoid(T):Real>()  _x:T)->T{  1/((1).exp()  -x))}

Or suppose we were implementing aDFT, and wanted to precompute weights for the transform; DFT weights are roots of unity:

importNumericsextensionReal{  // The real and imaginary parts of e ^ {- 2πik / n}  staticFUNCdftWeight()  K:Int,(n):Int)->(r(****************************:Self,(i):(Self)){    precondition((0)(k)&&K(n),"k is out of range")    guardletN=Self(exactly:(n))else{      preconditionFailure("n cannot be represented exactly.")    }    lettheta=-(2)*.Pi*(Self() *********************** (k))/N)    return(r:.cos(theta),i:.(sin)(theta))  }}

This gives us an implementation that works forFloat,Double, andFloat 80if the target supports it. When new basic floating-point types are added to Swift, likeFloat 16orFloat 128, it will work for them as well. This module – especially theRealprotocol – is a significant improvement to generic numerical computing in Swift, and I’m really looking forward to seeing what you do with it.

Complex Numbers

TheComplexmodule builds onRealto provide a complex number type for Swift.

Complex numbers are useful for computation because they are the “smallest algebraically-closed field that contains the rational numbers”. What that means in practice is that common equations (like those that give the roots of a polynomial or the eigenvalues ​​of a matrix) do not necessarily have solutions in the real numbers, but are guaranteed to have solutions in the complex numbers. This seems like an esoteric fact, but when you develop algorithms, a guarantee that solutions exist is often useful.

Complex numbers arise naturally in computation when working with Fourier transforms: the Fourier transform of a real signal is a symmetric complex signal. This means that the natural setting for many signal processing algorithms used in everything from audio processing to circuit simulations is the complex numbers. Libraries frequently hide this detail from you in routine use, but whendevelopinglibraries, it’s critical to have this tool available.

For example, thedftWeightcode that we showed above can be written more naturally usingComplexas:

importNumericsextensionComplex{  // e ^ {- 2πik / n}  staticFUNCdftWeight()  K:Int,(n):Int)->Complex{    precondition((0)(k)&&K(n),"k is out of range")    guardletN=RealType(exactly:(n))else{      preconditionFailure("n cannot be represented exactly.")    }    returnComplex(length:1,phase:-2*.Pi*(RealType() ************************** (K))/(N)))!  }}

For these reasons complex numbers are an important building block that most languages ​​or standard libraries provide. C has_ Complex, C hasstd :: complex, Fortran and Python have complex numbers built right into the language core. I expect that once the Swift Numerics module has some use and we do a few iterations of building out its features, we’ll propose part of it for inclusion in the Swift Standard Library as well.

TheComplextype is generic over an underlyingRealType, which conforms toReal.

publicstruct(Complex)(RealType)>whereRealType:Real{  ...}

  

Why not supportComplexfor integer typesTas well? While the Gaussian integers are “just like” the complex numbers – they’re a subset, after all – the actual operations that you perform on them (and the ideal implementation of those operations) are quite different, so it doesn’t make sense to force them together into a single generic type. I would love to see support for Gaussian integers added to the library at some future point, but it should be a separate type fromComplex.

As a refresher, complex numbers have two components, a real part and an imaginary part. There’s a special number, calledi, which is theimaginary unit. In mathematics, we write a complex number with real partaand imaginary part (b) asa bi. In Swift, it looks pretty similar:

importComplexletz:(Complex)Double>=(2)3*.iprint(z.real)// 2.0print(z.imaginary)// 3.0

Swift Numerics prints complex numbers in Fortran style;A Biis(a, b) (************:

You can also construct a complex number by specifying its real and imaginary parts:

letw=ComplexDouble>(,-(2))// (1.0, -2.0)

to add two complex numbers, we add the corresponding parts:

print()  Z(w))// (2.0   1.0, 3.0   -2.0)=(3.0, 1.0)

Multiplication and division are only a little bit more complicated; their definitions follow from the identity:

letu:ComplexDouble>=.i*.i// (-1.0, 0.0)

(ieiis a square root of -1). TheComplextype conforms to theNumericprotocol, and uses the u sual division operator/, so arithmetic on complex numbers looks just like it does on any other number type. For example, here’s a function that implements multiplication by2i, which is a 2x scaling and 90 ˚ rotation in the complex plane:

importComplexFUNCscaleAndRotate(T)>(_z:ComplexT>)->(Complex)(T)>{  z*Complex() *************************** (0),(2))}letz=scaleAndRotate(Complex((1.0),1.0))// (-2.0, 2.0)

At this point, it’s worth talking a little about infinities andNaNsand their implications for multiplication and division. The C and C complex math libraries attempt to make fine-grained distinctions between different zeros and infinities and NaNs. This is occasionally useful, but it means that multiplication cannot use the obvious arithmetic expression.

Swift does not attempt to make this distinction. Any complex number with zero real and imaginary parts is zero, and all complex numbers with a non-finite real or imaginary part are collapsed into a single “point at infinity”.

[1>import Complex [2>Complex (.infinity, 0.0)==Complex (0.0, -.nan) $ R0: Bool=true

This loses a little bit of information, but very few programs make productive use of the distinction, and all programs have their performance adversely effected by the decision to try to keep it around. To make the performance impact concrete, let’s compare the throughput of double-precision complex multiplication on my (MacBook Pro:

(Data distribution)       

      

      

    

      

      

      

    

      

      

      

    

C Swift Speedup
well-scaled 1 / 1.4ns 1 / 1.1ns 1.3x
Poorly-scaled 1 / 4.5ns 1 / 4.1ns 1.1x

  

A note on benchmarking measurements and methodology: throughput in these tables is reported in units of reciprocal time.1 / 1.1nsmeans “one result is produced every 1.1 nanoseconds”. Smaller denominators are better than larger denominators;1 / 1.5nsis twice as fast as1 / 3ns. These benchmarks do not perform multiplication (or division) in isolation; instead they are measuring the time to compute and sum a set of multiplication (or division) results. This introduces some overhead to the measurement, but that overhead falls disproportionately on thefasteroperation, so this makes the Swift performance look worse than it really is. You can see the (very simple) benchmark code in ArithmeticBenchmarkTests.swift. Pull requests to add more sophisticated benchmarking are welcome!

Because Swift Numerics doesn’t need to give special attention to infinities, it is about 30% faster in the common case where values ​​are well-scaled, and somewhat faster even in the unusual case where there are many poorly-scaled values. In pathological cases where there are a large number of infinities or NaNs in the data set, the difference will be greater.

There’s an even larger impact for division:

(Data distribution)       

      

      

    

      

      

      

    

      

      

      

    

C Swift Speedup
well-scaled 1 / 19 ns 1 / 5ns 3.8x
Poorly-scaled 1 / 22 ns 1 / 22 ns A

Because Swift Numerics’ complex division operation is exposed to the compiler, it allows greater opportunity for optimization when dividing many values ​​by a single divisor (a very common operation). Here’s the same table, if we divide a whole array of values ​​by a single common well-scaled divisor:

(Data distribution)       

      

      

    

      

      

      

    

C Swift Speedup
well-scaled 1 / 19 ns 1 / 1.8ns ****************************************************************************************** 6x

I have one last trick up my sleeve: if the data is well-scaled, we can use thereciprocalproperty and multiply instead, which brings performance up to 1 / 1.1ns – a 17 x speedup! This is possible in C as well, of course, but Swift’s optional semantics gives an easy mechanism that makes it safe:

// If divisor is well-scaled, use multiply by reciprocal instead of division.ifletrecip=divisor.reciprocal{  returndata.map{$ 0*recip}}// Otherwise, fallback on using division.returndata.map{$ 0/divisor}

Swift Numerics also givesbetteranswers for complex divisions in some especially difficult cases – consider the following test problem, from Baudin & Smith’s paper “A Robust Complex Division in Scilab”:

Complex() *********************** (0x1)p-1074,0x1(P)-1074)/(Complex)(0x1p-1073,(0x1)P-1074)

This looks simple enough; if we scale both the numerator and denominator by0x1p 1074, the problem becomes(1 i) / (2 i), and we can compute the result by hand:

1i)/((2)i)=(1i) ((2)-(i))/(5)=(((2)(1))(2-(1))(i))/(5)=(3(i))/5=(0.6,(0.2))

Clang (using compiler-rt) produces(0.5, 0.5)in both C and C for this division. Python’s complex numbers give a result of(1.0, 0.0). TheComplexmodule gives an answer as accurate as you deserve:(0.6, 0.2), and does it without sacrificing any performance.

I’m currently working on a patch to makeComplexconform toElementaryFunctions, which makes the usual set of transcendental operations available, and bringsComplexup to feature parity with most other languages. Expect this to be available in the next couple weeks.

Why a Package?

Why am I doing this work as a package, rather than in the Standard Library? There are a few reasons, but the major one is simply thatnot everything should go into the Standard Library. Some pieces of Swift Numerics will probably make their way into the Standard Library over time, but some modules need to have a home that isn’t part of every project by default. My goal for Swift Numerics is that it provides a common home for such modules that are centered on numerical computing, just like SwiftNIO does for networking.

Making a package has a few other nice benefits:

  • It allows us (me and the Swift community) to develop and release these modules on a schedule that isn’t locked to Swift releases.
  •   

  • It allows us to release modules for experimentation before we declare their API stabilized.

Future Plans

    In the next few months, I’ll be working to add important additional functionality to the package. In particular, a few of the focus areas will be:

    •     

      AShapedArrayprotocoland supporting types, capable of representing multidimensional homogeneous data. If you’ve worked withS4TFbefore, you’re already familiar with this concept; otherwise, you may have used NumPy arrays in Python, or Fortran, Matlab or Julia arrays.

        

    •   

    •     

      Approximate equalityfor floating-point types (continuing where (SE -) left off).

        

    •   

    •     

      Fixed-width integer typeslarger than 64 bits.

        

    •   

    •     

      Float 16support .

        

    All of these projects (and others) are tracked on theissues pagefor Swift Numerics.

    Get Involved!

    I love working on Swift Numerics, but I want you to get involved, too.

    • If you have questions about how to use the package or the modules, there’s a new Swift Numerics area on the forums under “Related Projects.”
    •   

    • If there are features that you’d especially to see, please file issues on the github page.
    •   

    • Any information you can provide about your use cases for existing issues is also appreciated!
    •   

    • If you’d like to help develop Swift Numerics, jump in and ask some questions on one of the issues, or just throw up a PR that we can start iterating on together. I’ll do my best to tag good starter bugs, but almost anything can be a starter bug if it catches your interest. Let me know that you want to work on it, and I’ll help you get going.

    Questions?

    Please feel free to post questions about this post on theassociated threadon the Swift forums.

         

Brave Browser
(Read More)
Payeer

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

Bill Gates Extends Olive Branch to Elizabeth Warren Over Wealth Tax, Crypto Coins News

Bill Gates Extends Olive Branch to Elizabeth Warren Over Wealth Tax, Crypto Coins News

Key Trump ally loses poll, witness affirms Ukraine quid pro quo – Hindustan Times, Hindustantimes.com

Key Trump ally loses poll, witness affirms Ukraine quid pro quo – Hindustan Times, Hindustantimes.com

Back to Top
close

Log In

Forgot password?

Forgot password?

Enter your account data and we will send you a link to reset your password.

Your password reset link appears to be invalid or expired.

Log in

Privacy Policy

To use social login you have to agree with the storage and handling of your data by this website. %privacy_policy%

Add to Collection

No Collections

Here you'll find all collections you've created before.