Closed axkr closed 3 years ago
Unfortunately, no. The reason is that Complex implements FieldElement
but not 'RealFieldElement`. There are several methods that cannot be implemented (floor, ceil, copysign, remainder, rint, round). There are also other missing methods that could be implemented but we never did (for example inverse hyperbolic sine, using some choice for branch cut since the function is multivalued).
I have been thinking about introducing an intermediate interface between FieldElement
and 'RealFieldElementso we could have
Complex` implement it and share more algorithms with reals like differentiation. I have not yet found a way to do it.
For example Mathematica has the convention that real and imaginary parts are applied separately for complex numbers, at least for Floor
, Ceiling
. Mod
is also working for complex arguments.
Maybe this "conventions" can be implemented in hipparchus in the same way?
I was not aware of such conventions and would not have proposed this as it seems a little weird to me. However, if it is already accepted elsewhere, then yes, it makes sense to implement it in Hipparchus too.
Unfortunately, no. The reason is that Complex implements
FieldElement
but not 'RealFieldElement`. There are several methods that cannot be implemented (floor, ceil, copysign, remainder, rint, round). There are also other missing methods that could be implemented but we never did (for example inverse hyperbolic sine, using some choice for branch cut since the function is multivalued).I have been thinking about introducing an intermediate interface between
FieldElement
and 'RealFieldElementso we could have
Complex` implement it and share more algorithms with reals like differentiation. I have not yet found a way to do it.
An intermediary interface would be preferable. I have a basic question though. Are you sure Rall's setup lifts directly to complex-valued functions? How exactly are the differentials evaluated? How will these things be used? Sorry, I am probably missing something basic here.
OK for an intermediate interface.
I think extending the Ralls numbers to complex will work. They work by applying directly the chain rule at each operation, so they require only having a proper add, subtract, multiply... In fact, we already did extend differentiation when we created FieldDerivativeStructure
in december 2016, which was the more heavyweight work to do. Now that this is up and working properly since a few years, the rest should be easier. There are however, quite a number of methods to implement as our Complex
class is currently rather basic.
I'm working on this issue. It is harder than I first thought.
Up to now, I have created an intermediate CalculusFieldElement
interface between FieldElement
and RealFieldElement
and have Complex
implement it. This means Complex
now have all calculus related functions implemented (including all inverse hyperbolic and all linear combinations).
None of this is tested yet! Testing will be the next step.
I am not sure however that this will be sufficient for derivatives. Perhaps instead of allowing Complex
to be used inside FDSFactory
we will need a reversed implementation (i.e. something the FieldComplex
or DerivativeComplex
). I really did not think thoroughly about it as I am focusing on a complete and tested Complex
class for now.
I have pushed a complex-field branch. It is a first step (not complete yet) to solve this issue. The branch contains a new intermediate interface and implementation of all calculus-related functions for complex. There is a fair number of tests, including tests for branch cuts on some functions. I still need to add some tests for atan2 and all the linear combinations functions, but thought it can wait a little. No work has been done yet on the differentiation aspect, so I still don't know if this extension will be sufficient or if a sister class devoted to complex-with-derivatives will be needed. If someone could review the current implementation, I would be happy to get some feedback.
I have completed a first pass on this and merged the complex-branch back to master.
Now Complex
implements CalculusFieldElement<Complex>
which provides a lot of calculus-related functions. Validating this allowed to find a number of bugs in existing classes (#76 and #82).
Some existing functions have been rewritten, either to improve efficiency or to improve accuracy (for example multiplication now uses linear combinations with high accuracy).
Next step is to look at the derivatives.
We have added quite a large number of functions so Complex
now implements the new CalculusFieldElement
interface. There are four remaining functions that belong to RealFieldElement
but not to CalculusFieldElement
: the remainder
function with two signatures, the round
function and the abs
function.
The two first functions (remainder with two signatures) do not really make sense for Complex. We could try to use a different integer factor for real and imaginary parts, but that seems really awkward to me and I cannot see a use case.
The round
function could be implemented ignoring the imaginary part, but it it really useful or should users simply call FastMath.round(z.getReal())
in that case?
The abs
function should definitely be moved upward in the hierarchy, at CalculusFieldElement
level, but this is not possible without breaking compatibility as Complex
already as an abs
method but with a different return type (double instead of Complex
). I think the existing Complex.abs
should be renamed so we can add a new abs
with the Complex
return type compatible with the interface. This however cannot be done for version 1.7, it will have to wait for version 2.0 for compatibility sake.
I have prepared work for derivatives with new tests that can be easily adapted for any field. I think that we also need to rethink the DerivativeStructure
and FieldDerivativeStructure
so they refer to CalculusFieldElement
and not RealFieldElement
. This would imply we cannot use remainder
anymore in derivatives, and perhaps not round
(but this is moot for the round
as in any case derivatives are zero for rounded integers). So here again, we have to wait for 2.0.
So I think the work that can be done for 1.7 is already there and the improvements made to the Complex
class are already important, but this issue can be completely solved only with 2.0.
For the remainder
function see function QuotientRemainder
in MMA. It is also defined for complex numbers:
IMO round
should be applied separately to real and imaginary parts of complex numbers.
For the
remainder
function see functionQuotientRemainder
in MMA
I'm sorry, I don't get it. Could you tell me what would be expected as the result of new Complex(14, -5).remainder(new Complex(3, 4))
?
IMO
round
should be applied separately to real and imaginary parts of complex numbers.
OK, so I moved the round
method back to RealFieldElement
. There is already a method in CalculusFieldElement
and therefore already implemented in Complex
that you could use: rint()
.
@maisonobe: I created a GIST which should show the quotient and remainder idea:
Thanks a lot! I finally got it. I was stupid and stubbornly focused on classical integer, I forgot about Gaussian integers.
I have implemented the remainder
method. I used a different implementation than the one in your GIST, both to comply to existing API (returning only remainder) and to ensure consistent behaviour by using rint() rather than round().
This allowed to move the remainder implementation up one level in the interfaces hierarchy. This change has been pushed to the master branch.
The only problem left is abs()
, and here we cannot do anything before 2.0. I did however implement something beforehand. It is in a new calculus-field branch. I removed the abs()
method from Complex
, added a norm()
method to CalculusFieldElement
(which is implemented by Complex
) and replaced almost everywhere the uses of RealFieldElement
bu CalculusFieldElement
. This now allows to use FieldDerivativeStructure<Complex>
! As I wrote before, this cannot be integrated in the 1.x series, so the calculus-field ranch will not be merged immediately. You can however check it out and play with it already, by compiling it by yourself. I don't consider the complex derivatives are sufficiently validated, so more work is needed on it, but most of the heavy lifting is done. I would be happy if you could give it a try and give me some feedback.
Do you have a time schedule for 2.0?
Not yet. I confess I have been slow on this (still have to look at the bug you identified in eigen decomposition). I would like to take the 2.0 opportunity to introduce another major change, related to contrained optimisation, but this takes a lot of time and delays 2.0 a lot.
OK thanks for your response.
Well, we are making some progress and heading for a 2.0 release. The calculus-field branch has been merged into master yesterday. Beware that as started above (comment from 2020-06-02) it introduces incompatibilities (this is the reason why it had to wait for a major release before being merged). I hope we could publish 2.0 in about a month.
Looking at RealFieldElement
do we still need it since it only has two methods left? The abs()
method just delegates to nom()
which Complex
implements. The round()
method was already extended to the complex numbers with rint()
. So both those methods seem to be defined for complex numbers.
From the inheritance perspective I think the case can be made either way. A complex number "is a" real number augmented with an imaginary number. A real number "is a" complex number with zero imaginary part. One can make these statements again about quaternions, e.g. a quaternion is a complex number augmented with two more imaginary numbers. I think your idea of the calculus field interface is a good one to avoid that issue, but it will make it harder for users to update to the new version.
OK to mode the two methods upward and remove the RealFieldElement
interface,
which would finally correspond to having renamed it.
yes or just remove the two methods left in RFE since abs()
just delegates to norm()
and round()
just delegates to FastMath.round()
. Although it is nice to have an abs() method even if it is the same as norm().
I'll move the methods upward.
I forgot a few renames that should be added IMHO: RealField{Univariate|Bivariate|Vector|Matrix}Function
.
However, I don't think renaming also RealVector
or RealMatrix
would be wise. Any thought?
I have no problems with "renaming", but I don't know how other dependent projects think about it?
If you do a "general renaming" then IMO you should think about all possible renaming suggestions:
Otherwise it will probably be better to keep the names as "compatible" as possible to older releases.
I forgot a few renames that should be added IMHO:
RealField{Univariate|Bivariate|Vector|Matrix}Function.
+1 to rename since the interface to which they refer is going away
However, I don't think renaming also RealVector or RealMatrix would be wise. Any thought?
-1 for renaming these since they never used the RFE interface in the first place.
I recall the change about renaming the add method. The fact is that add is a method name rather than an interface name, so it is used in many places. In my workspace, a quick count showed 581 different calls to the "add" method defined by this interface, among a total number of 15726 calls to other "add" methods. So in my own IDE (eclipse), I could do the rename on Hipparchus and have it propagated to my dependent projects automatically. External users on the other hand that would just download the updated would need to sort out the calls just by fixing compiler errors, most probably one by one. Given the ubiquity of "add", this is much more difficult than renaming something very specific as "RealFieldElement".
I will close this issue and open a new one, closely related.
Do you have a unit test or example how to use FDSFactory
You can look at FunctionsWithFastMathVersionTest.testCos
, which calls doTestFn
which it self call 'checkFnEqualities`.
Is there something similar like a FDSFactory for type Complex available?