-
Notifications
You must be signed in to change notification settings - Fork 162
Introduce AlgebraicField protocol #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
|
CC @airspeedswift Thoughts on naming?
|
|
Brainstorming the bikeshed:
• • • Regarding commutativity, on one hand it would seem odd to arbitrarily exclude quaternions from such a basic protocol. On the other hand, it is extremely likely that people will write algorithms against this protocol that implicitly assume multiplication is commutative, because most people won’t even think about the possibility of it being otherwise. It might be beneficial to require commutativity in the protocol, so that people working with quaternions don’t get caught off guard by such algorithms. Of course, then people might provide their own retroactive conformance |
|
It occurs to me that The new protocol does not create this problem. The problem already exists. |
|
Right, Numeric deliberately didn't require commutative multiplication because we thought it was more useful to allow square matrices and quaternions to conform. This isn't really a "problem" per se; it's a deliberate design choice. It's actually fairly hard for non-experts to get into too much trouble with Numeric allowing non-commutative multiplication though, because for simple use, it tends to only cause a problem when people start multiplying by inverses to cancel expressions (and multiply on the wrong side), and there's no way to get an inverse out of the Numeric protocol. It can also cause a problem when people re-arrange large expressions, but when you don't have inverses there's rarely a good reason to do that in a generic setting. It's worked out OK so far. My question is really just "should this thing include quaternions or not", because quaternions are the only finite-dimensional thing that would be added in relaxing the conformance to division rings--all finite division rings are fields, and the only division rings that are finite-dimensional algebras over the reals are the reals, complex, and quaternions. On the other hand, this would exacerbate the non-commutativity of multiplication somewhat, because in this setting, you do have inverses and people are likely to apply them on the wrong side. |
…what, you mean you don’t expect people to model noncommutative finite-dimensional algebras over ℚ in the Swift type system? :-p But yeah, point taken. There are certainly some algorithms that make sense for the noncommutative case, for instance taking the average of a sequence: • • • Coming at this from another angle, are there any important algorithms which require commutativity? And are there any compiler optimizations which can be enabled through commutativity? |
There aren't a lot of things that require commutativity, but there are a lot of things that admit a faster implementation on a computer if you can assume commutativity. e.g. computing the product of an array of numbers; if I can assume commutativity (and I am prepared to accept small rounding perturbations, and assume that intermediate overflow isn't an issue), I can have two accumulators and run all the evens into one and all the odds into the other, then multiply the two together. This doesn't reduce the asymptotic complexity, but it reduces the length of the dependency chain, and can be a significant latency win. It's not a huge deal, but such things do exist.
We don't (yet?) have any means to communicate to the compiler that an operation is commutative (it knows a priori about the commutativity of the operations in the Builtin module, but we don't have a way to add that information to an operator defined in Swift code). |
|
I think that I've talked myself around to requiring commutativity for this protocol, because as we start adding linear algebra operations, I would like to initially avoid the complexity of left- and right-modules. So let's focus on names for Field-like. |
Sources/Real/Field.swift
Outdated
| /// 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 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the rationale for making the reciprocal property Optional?
For comparison, the unary-negation operator in the standard library is non-optional and traps on overflow (eg. -Int.min).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditions under which unary-negation would trap are trivially detectable a priori, so a trap is an appropriate error mechanism. The conditions under which multiplying by a reciprocal would deliver a result that is either incorrect or has significantly reduced accuracy are significantly harder to characterize, and users are likely to either fail to detect them, or get them wrong. Returning an optional lets us handle the check for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This glosses over the fact that detecting when unary-negation would trap in a protocol context is non-trivial for all the named protocols in the hierarchy under SignedNumeric, so we probably should add an optional negated property at some point.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s actually really clever. People who want the trapping behavior can write 1/x, and people who want to check for failure can use reciprocal.
+1 to this design, and +1 to bringing it to the standard library for negated as well.
Works for me, I vote |
We might walk this back in the future, but for the short-term let's avoid stomping on the generic term "Field".
|
After discussion with the rest of the standard library team, I've changed the name to |
| /// adding division. | ||
| /// | ||
| /// A field is a set on which addition, subtraction, multiplication, and | ||
| /// division are defined, and behave basically like those operations on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// division are defined, and behave basically like those operations on | |
| /// division are defined, and behaves basically like those operations on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Behave" is correct here; the subject is "addition, subtraction, ... and division".
I’m not convinced the longer name is actually clearer. To the vast majority of people, “algebra” means high school Algebra 1 & 2, and they have no idea that abstract algebra even exists. If they have encountered anything “algebraic” before, it is only the algebraic numbers. To these people, And to people who are familiar with abstract algebra, the term “algebraic field” probably makes them think of the field of algebraic numbers. To these people, |
|
I think Naked |
This is basically the feedback I got from people I talked to about the issue. "Field" is very natural to us as mathematicians, but for someone who is not from a math background, "Field" doesn't even suggest that it's a mathematical concept that's being described. "AlgebraicField" is not perfect, but it gives a clue to the uninitiated that it's describing a "math thing" (and Google will lead them to the truth). One option to resolve this in the longer term would be to introduce an "abstract algebra" module and put all of the formal structures, as well as the conformances, into that module, where they wouldn't bother people who just want to use complex numbers or generic math functions. This is useful in the meantime. |
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.