-
Notifications
You must be signed in to change notification settings - Fork 3
Add realdot
#2
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 realdot
#2
Changes from 18 commits
537fbad
4b08ee2
15ae11e
262f9a9
8a750f2
b91d03d
360dcb9
d6ba3be
25bbc2a
638a6f5
96b44e9
073001f
ce9c71e
5de6e9b
e48caee
5618631
7194ee9
1ecb63d
c6d1099
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,22 @@ | ||
module RealDot | ||
|
||
# Write your package code here. | ||
using LinearAlgebra: LinearAlgebra | ||
|
||
export realdot | ||
|
||
""" | ||
realdot(x, y) | ||
|
||
Compute `real(dot(x, y))` while avoiding computing the imaginary part if possible. | ||
|
||
This function can be useful if you work with derivatives of functions on complex | ||
numbers. In particular, this computation shows up in pullbacks for non-holomorphic | ||
functions. | ||
""" | ||
@inline realdot(x, y) = real(LinearAlgebra.dot(x, y)) | ||
@inline realdot(x::Complex, y::Complex) = muladd(real(x), real(y), imag(x) * imag(y)) | ||
@inline realdot(x::Real, y::Number) = x * real(y) | ||
@inline realdot(x::Number, y::Real) = real(x) * y | ||
@inline realdot(x::Real, y::Real) = x * y | ||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,63 @@ | ||
using RealDot | ||
using LinearAlgebra | ||
using Test | ||
|
||
# struct need to be defined outside of tests for julia 1.0 compat | ||
# custom complex number (tests fallback definition) | ||
struct CustomComplex{T} <: Number | ||
re::T | ||
im::T | ||
end | ||
|
||
Base.real(x::CustomComplex) = x.re | ||
Base.imag(x::CustomComplex) = x.im | ||
|
||
Base.conj(x::CustomComplex) = CustomComplex(x.re, -x.im) | ||
|
||
function Base.:*(x::CustomComplex, y::Union{Real,Complex}) | ||
return CustomComplex(reim(Complex(reim(x)...) * y)...) | ||
end | ||
Base.:*(x::Union{Real,Complex}, y::CustomComplex) = y * x | ||
function Base.:*(x::CustomComplex, y::CustomComplex) | ||
return CustomComplex(reim(Complex(reim(x)...) * Complex(reim(y)...))...) | ||
end | ||
|
||
# custom quaternion to test definition for hypercomplex numbers | ||
# adapted from Quaternions.jl | ||
struct Quaternion{T<:Real} <: Number | ||
s::T | ||
v1::T | ||
v2::T | ||
v3::T | ||
end | ||
|
||
Base.real(q::Quaternion) = q.s | ||
Base.conj(q::Quaternion) = Quaternion(q.s, -q.v1, -q.v2, -q.v3) | ||
|
||
function Base.:*(q::Quaternion, w::Quaternion) | ||
return Quaternion( | ||
q.s * w.s - q.v1 * w.v1 - q.v2 * w.v2 - q.v3 * w.v3, | ||
q.s * w.v1 + q.v1 * w.s + q.v2 * w.v3 - q.v3 * w.v2, | ||
q.s * w.v2 - q.v1 * w.v3 + q.v2 * w.s + q.v3 * w.v1, | ||
q.s * w.v3 + q.v1 * w.v2 - q.v2 * w.v1 + q.v3 * w.s, | ||
) | ||
end | ||
|
||
function Base.:*(q::Quaternion, w::Union{Real,Complex,CustomComplex}) | ||
a, b = reim(w) | ||
return q * Quaternion(a, b, zero(a), zero(a)) | ||
end | ||
Base.:*(q::Union{Real,Complex,CustomComplex}, w::Quaternion) = w * q | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quaternions aren't multiplicatively commutative, so this does not work: julia> using Quaternions
julia> q = quatrand();
julia> p = Quaternion(randn(2)..., 0, 0);
julia> p*q - q*p # doesn't work
Quaternion{Float64}(0.0, 0.0, 2.3421250648992906, 1.0118653825634418, false)
julia> p*q - (q'*p')' # this is fine
Quaternion{Float64}(0.0, 0.0, 0.0, 0.0, false) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha yeah I guess it's clear that I've never worked with quaternions 😄 In fact for the tests it does not matter and the result could be anything since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be fixed now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, quaternions are super useful as a sanity check for chain rules of numers/numerical arrays, since they behave like matrices. I have probably thought way too much about them. |
||
|
||
@testset "RealDot.jl" begin | ||
# Write your tests here. | ||
scalars = ( | ||
randn(), randn(ComplexF64), CustomComplex(randn(2)...), Quaternion(randn(4)...) | ||
) | ||
arrays = (randn(10), randn(ComplexF64, 10)) | ||
|
||
for inputs in (scalars, arrays) | ||
for x in inputs, y in inputs | ||
@test realdot(x, y) == real(dot(x, y)) | ||
end | ||
end | ||
end |
Uh oh!
There was an error while loading. Please reload this page.