Real and Complex uniform type conformance when dividing

I have generic operator functions to perform tasks like add, sub, mul, div, etc...

Right now add and sub require the element to conform to AdditiveArithmetic, and it works uniformly for Real and Complex.

Mul requires the element to conform to Numeric, which works for both Real and Complex. However div requires conformance to FloatingPoint, and Complex does not conform. This is causing a problem.

The Real and Complex types know how to generically do +, -, *, /
Is there any uniform conformance I can use when dividing to generically manipulate both Real and Complex types?

Thanks, Ed

This is Protocol for algorithms on both real and complex numbers · Issue #79 · apple/swift-numerics · GitHub

Hi Steve, thank you for the reference. Where do you stand on these issues? The SwiftRT framework is currently based on your Numerics package and we have several users very interested in Complex numbers.

1 Like

I'm going to throw up a branch with a sketch of this later today (edit: Introduce AlgebraicField protocol by stephentyrone · Pull Request #91 · apple/swift-numerics · GitHub), and would love to get some feedback on it.

1 Like

Thanks Steve! I can now generically add, sub, mul, and div the Real and Complex types generically in the SwiftRT framework. I'm really looking forward to the Complex implementation of the ElementaryFunctions protocol. It is likely to just work without any code changes on my part.
Please advise when you move the Field protocol to the master branch so I can update my Package file.

1 Like

I'm going to let people try it out for a couple days first; my plan to merge it to master and tag it on Friday.

Hi Steve,
I'm in Google research developing a framework for ml and hpc with a focus on performance and a cleaner user experience than TensorFlow. My work will compliment our larger MLIR effort to produce a next gen compiler for parallel computing. You're probably already aware of it. My project is open source called SwiftRT on github.

I have a thought about the naming of your generic Complex type. This is purely a matter of ergonomics and not about correctness. The Real portion of your project is a set of protocols conformed to by the standard Swift real types (Float, Double). Users simply use them in their code and pick up the functionality.
SwiftRT defines several generic tensor types such as Vector, Matrix, etc..
Application models typically select one precision to use throughout, and for the real type that is usually Float. Originally the user was required to explicitly declare types everywhere as Vector<Float>, Matrix<Float>. The element type never changes, is redundant, and makes the code look verbose. I then tried changing my generic tensor type names to VectorT<Element>, MatrixT<Element>, then declaring default type aliases likes typealias Vector = VectorT<Float>. It makes the user code easier to type and a lot less cluttered looking. The users can pick up these default definitions, or in their own module redefine them such as
typealias Vector = VectorT<Double>

So what I would like you to think about is possibly doing the same sort of thing with the Complex generic type. Perhaps calling it ComplexT and then doing a default like typealias Complex = ComplexT<Float>. This would make user code cleaner and less verbose, and of course the user can redefine these any time if it makes sense for their application. Then users can simply type Complex with the implied element type. As it stands, it isn't possible to do this kind of simplified typealias because there will be a naming collision with your generic type. What do you think? So far, internal users here have really liked the SwiftRT simplified tensor type syntax.

On a separate note, I'm curious, what company do you work for?

Thanks, Ed

ComplexT doesn't seem entirely "Swifty" (cryptic single-character suffixes aren't really Swift style); if we went that route, I'd probably use something like ComplexType or instead.

Leaving bike shedding aside: one thing to consider is that if we don't rename the type, we probably should rename the module so that it's possible to do:

typealias Complex = ComplexModule.Complex<Double>

for users who really just want to work with one type (and this would make it still possible to get at other specializations via ComplexModule.Complex<T>; unfortunately the name lookup rules stop looking after they find a match, so the generic type can't be found as just Complex<T> when a concrete typealias is present).

I'm curious, what company do you work for?

I'm at Apple; I spent a decade and change on the math libraries team within the OS org, then moved over to the languages-and-runtimes org a year and a half ago.

3 Likes

Yes of course, good call. I changed things like VectorT<Element> to VectorType<Element>. Any other comments you have to offer in the future are welcome.
Yes renaming the module to do as you suggest would be very helpful to make a less verbose api :slight_smile:

Hi Edward, this protocol (with the somewhat more verbose name AlgebraicField to avoid confusion for non-mathematicians) is now merged to master. I'll tag a release later today or tomorrow.

1 Like

I'm glad you made the name more verbose. I was worried about conflicts.
Any thoughts on renaming either the Complex type or the Complex module?

I'm going to open an issue to track it, and aim to make some decision in the next two weeks or so (edit: Rename Complex type or module · Issue #94 · apple/swift-numerics · GitHub).