Skip to content
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

Add Floating class #457

Closed
wants to merge 1 commit into from
Closed

Add Floating class #457

wants to merge 1 commit into from

Conversation

aijony
Copy link
Collaborator

@aijony aijony commented Mar 29, 2022

While I was waiting on #451, I actually wasn't too unhappy with having Floating being split into multiple segments. This would allow more flexible instances (e.g. square matrices via power series). We could also move (a)tan(h) to Floating if we wanted to remove Dividable from Trig. I put ** and logBase into Floating as well because giving them a multi parameter type class messes with stuff like logBase 2 2.0 - even though a power series of e^x or log x imply power series a^x or log_a x resp. One can also come up with a power series for sqrt, so I really haven't broken it up to allow all the mathematical edge-cases.

I'll be the first to admit this seems a bit advanced for a standard library, so I'm happy to make floating into a mega class when #451 gets sorted out. Either way, I'd love some input.

Fixes: #366

@stylewarning stylewarning self-assigned this Mar 29, 2022

(%define-floating-functions Single-Float cl:single-float)
(%define-floating-functions Double-Float cl:double-float)
(%define-floating-functions (Complex Single-Float) (cl:complex cl:single-float))
Copy link
Collaborator

@eliaslfox eliaslfox Mar 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be (cl:or cl:single-float (cl:complex cl:single-float)).

See (typep #C(1 0) '(complex integer))

Copy link
Collaborator Author

@aijony aijony Mar 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are only used to coerce pi at the moment, and actually it seems that coercing pi as either single-float or (complex single-float) work equally well. However, one cannot (coerce pi '(or single-float (complex single-foat)). However, if we don't coerce pi, it will sometimes decide it wants be a double-float.

Given the option of (coerce pi 'single-float) => 3.14... and (coerce pi '(complex single-float)) ;=> #C(3.14... 0) do think there is a best choice?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think either is fine. I misunderstood the use case.

@braised-babbage
Copy link
Contributor

I left a few questions about the granularity & specific details of the type constraints above.

I think it's worth distinguishing between two uses of typeclasses (perhaps there are others):

  • as syntactic sugar (e.g. letting us write (exp matrix) rather than (expm matrix))
  • as a substrate for generic programming (e.g. so we can write code that is agnostic to floating point precision, or which is also capable of working under interval arithmetic, automatic differentiation, etc).

This PR appears to be sensitive to both of these items, which is good. I would say that I would be surprised to see serious numeric code which is generic across substantially different domains (e.g. which works effectively for both complex double floats & real matrices), so I don't think it's worth sweating that part too much.

@eliaslfox
Copy link
Collaborator

@kilimanjaro If you had other comments, I don't think they saved.

(coalton-toplevel
(define-class (Exponent :a)
"Exponential functions where exp is exponentiation of Euler's number and
(exp (+ a b)) = (* (exp a) (exp b)) and (exp 0) = 1."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I take this to mean that :a is a member of some other typeclass (providing arithmetic and 0, 1)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I figured Num would imply commutative, but we could easily add it.

(exp (+ a b)) = (* (exp a) (exp b)) and (exp 0) = 1."
(exp (:a -> :a)))

(define-class (Exponent :a => Logarithm :a)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, do you have something in mind that would be an instance of Exponent but not Logarithm?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No specific examples, but if we end up splitting the class like this I think exp would be the most useful function. I guess for the cases were either ln(X) doesn't converge but e^X does - it could just throw an error (or in the case were the implementer is too lazy).

(atanh (:a -> :a)))

(define-class ((InvTrig :a) (Num :a) => Floating :a)
"Common floating point operations."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be a bit pedantic here, I don't quite see what makes this a floating type. For example, one could imagine implementing all of these with fixed point arithmetic. Also, unless I'm missing something, it could even be satisfied by symbolic expressions (this is perhaps a silly thing to mention).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy if anyone has any better names. I just was trying to keep it somewhat friendly to Haskell-familiar users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. No real objections then.

"Logarithmic functions such that log is the inverse of exp."
(log (:a -> :a)))

(define-class ((Exponent :a) (Dividable :a :a) => Trig :a)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear why we need (Dividable :a :a). There's no comparable constraint placed on Exponent or Logarithm.

Copy link
Collaborator Author

@aijony aijony Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of a way to define tan without division, but I could be wrong. I could also move tan to Floating and remove Dividable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a power series doesn't require division

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it how my brain decided the only way to compute tan was sin/cos and just pretended tan didn't have a series representation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when @kilimanjaro computes tan he uses a compass and ruler; i've seen it with my own two eyes

(cosh (:a -> :a))
(tanh (:a -> :a)))

(define-class ((Logarithm :a) (Trig :a) => InvTrig :a)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here; it's not clear why we need Logarithm. To me it sort of implies that these inverse trig functions can be defined in terms of log, which they can, but you need complex arithmetic for that, and also it's probably not how anybody would want to implement them.

Copy link
Collaborator Author

@aijony aijony Mar 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was trying to instill some semblance of hierarchy, but I agree this one perhaps is just restrictive with no benefit.

(define (acosh x) (lisp ,coalton-type (x) (cl:acosh x)))
(define (atanh x) (lisp ,coalton-type (x) (cl:atanh x))))

(define-instance (Floating ,coalton-type)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps abs is worth adding here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have abs for instances of Ord, but perhaps modulus or absolute? Or we could change abs for Ord to something like positive. However, I have code were I need Complex numbers as a vector space (for dot products), likewise I have been using norm on Complex numbers. But implementing linear algebra code is probably out of scope for this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It strikes me as a bit odd that abs requires Ord, for the reason that you mentioned: one will end up defining a modulus or absolute or something for complex numbers. So if we do have the ability to move stuff around, I suggest dropping the Ord constraint.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, abs in ord seems weird. I think haskell does it but I would be fine nixing it.

@aijony aijony mentioned this pull request Apr 2, 2022
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

sqrt and some transcendental functions not implemented
4 participants