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

improve promotion and equality checking #52

Merged
merged 24 commits into from
Feb 20, 2022
Merged

improve promotion and equality checking #52

merged 24 commits into from
Feb 20, 2022

Conversation

mikmoore
Copy link
Contributor

This PR is primarily motivated by an issue where Quaternion(1) == 1 returned false. There were two issues at play here.

First, convert(Quaternion,::Real) and convert(Quaternion,::Complex) were never setting the .norm field of the resulting Quaternion. This meant that Quaternion(1.0).norm == true while convert(Quaternion,1.0).norm == false.

Second, there was no special-cased ==(::Quaternion,::Quaternion) method. This meant that equality required the .norm fields to match.

This PR reroutes convert calls through the normal constructors to help .norm be set more consistently. It also adds a == method for Quaternions so that the .norm field is ignored in equality checks. Lastly, it adds some tests along these lines.

Copy link
Contributor

@dkarrasch dkarrasch left a comment

Choose a reason for hiding this comment

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

Just a few minor style comments.

src/Quaternion.jl Outdated Show resolved Hide resolved
src/Quaternion.jl Outdated Show resolved Hide resolved
@dkarrasch
Copy link
Contributor

This should close #40, IIUC.

@mikmoore
Copy link
Contributor Author

I noticed that & is probably better than && for short chains of simple comparisons (no branches), so I changed that slightly although it can certainly be reverted.
I also extended this PR to include the matching changes to Octonion and DualQuaternion, which I had neglected before.

@sethaxen sethaxen closed this Feb 19, 2022
@sethaxen sethaxen reopened this Feb 19, 2022
@codecov
Copy link

codecov bot commented Feb 19, 2022

Codecov Report

Merging #52 (c333db7) into master (be0fc14) will increase coverage by 9.84%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #52      +/-   ##
==========================================
+ Coverage   45.58%   55.43%   +9.84%     
==========================================
  Files           3        3              
  Lines         351      359       +8     
==========================================
+ Hits          160      199      +39     
+ Misses        191      160      -31     
Impacted Files Coverage Δ
src/DualQuaternion.jl 21.78% <100.00%> (+17.65%) ⬆️
src/Octonion.jl 28.04% <100.00%> (+18.04%) ⬆️
src/Quaternion.jl 87.50% <100.00%> (+2.44%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update be0fc14...c333db7. Read the comment docs.

Copy link
Collaborator

@sethaxen sethaxen left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Can you add tests for == for Octonion and DualQuaternion as well? Also, most of the conversions modified here are not yet covered by tests, and they should be tested as well.

src/DualQuaternion.jl Show resolved Hide resolved
src/Octonion.jl Outdated Show resolved Hide resolved
Co-authored-by: Seth Axen <[email protected]>
Copy link
Collaborator

@sethaxen sethaxen left a comment

Choose a reason for hiding this comment

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

Sorry, accidentally approved before the missing tests were added.

test/test_Quaternion.jl Outdated Show resolved Hide resolved
@mikmoore
Copy link
Contributor Author

I noted that the internal(?) shorthand functions quat, dualquat, and octo had the same issue of defaulting the norm field to false, in addition to being totally redundant. I redefined them to be constants equal to their corresponding type, which should fix that. In the grander scheme I think they really had ought to be removed, but that's beyond the scope of this PR.

I added explicit conversion tests. They revealed that DualQuaternion was broken when given DualNumbers.Dual because the field names were wrong. I believe I fixed that as well, but somebody should check because dual numbers aren't really my thing.

Copy link
Collaborator

@sethaxen sethaxen left a comment

Choose a reason for hiding this comment

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

Aliasing the shorthand functions would be a breaking change. While the public API of this package is not documented, they are exported functions and should be considered part of the API. As for their use quat is to Quaternion as complex is to Complex. The former is a method the user can overload to quaternion-ify anything, while the latter is a constructor that should return a Quaternion (and probably should not be overloaded). You could open an issue to discuss removing them, but that really should not be in this PR.

src/DualQuaternion.jl Outdated Show resolved Hide resolved
@@ -6,29 +6,29 @@ struct DualQuaternion{T<:Real} <: Number
norm::Bool
end

DualQuaternion(q0::Quaternion, qe::Quaternion, n::Bool = false) =
DualQuaternion(q0::Quaternion, qe::Quaternion, n::Bool = q0.norm) =
Copy link
Collaborator

Choose a reason for hiding this comment

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

This does not seem to be consistent with how norm is defined for DualQuaternion. norm returns a Dual number, and the DualQuaternion is normalized when the real part of the norm is 1 and the infinitesimal part is 0.

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 we only want to set norm=true when its value can be determined from either the types being provided or from the value of other norm fields, which I don't think we can do here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have zero knowledge of dual quaternions, hence my mistake. It appears this has veered wildly beyond the scope of both this PR and my understanding and I'll revert my change.

Off-topic discussion: wouldn't your statement imply that n::Bool = q0.norm & iszero(qe) would be the proper initialization? More importantly, doesn't any requirement on the infinitesimal part imply that normalize(::DualQuaternion) and friends are incorrect in setting norm=true without examining the infinitesimal? According to https://en.wikipedia.org/wiki/Dual_quaternion#Norm, however, it appears to be slightly less restrictive than what you have claimed. It says that a dual quaternion A+eB is unit when (AA*,AB*+BA*)=(1,0).

Copy link
Collaborator

Choose a reason for hiding this comment

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

TBH, I don't have much experience with dual quaternions either, so I can't really answer the questions without digging into the code and the theory a bit more. My point was just that the change would create inconsistencies in the code base.

@sethaxen
Copy link
Collaborator

Also, can you rebase on master and resolve merge conflicts?

mikmoore and others added 3 commits February 20, 2022 06:34
…wrong .norm field. but they should probably be removed altogether."

This reverts commit 3809886.
@mikmoore
Copy link
Contributor Author

As for their use quat is to Quaternion as complex is to Complex.

I see. In that case, I changed them to not default norm=false but instead to only forward a norm flag if provided one. This will let them inherit default norm behavior from the relevant constructors. Without this change, for example, Quaternion(1) and quat(1) resulted in different norm flags.

Copy link
Collaborator

@sethaxen sethaxen left a comment

Choose a reason for hiding this comment

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

If you browse through the code, CodeCov has annotated a few lines changed here that are not covered by the tests. Can you check that they are? Also, can you add tests that the shorthand cases you mentioned where norm was set differently than by the constructor are also tested?

test/test_Quaternion.jl Outdated Show resolved Hide resolved
Copy link
Collaborator

@sethaxen sethaxen left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants