-
Notifications
You must be signed in to change notification settings - Fork 30
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 support for Dual{Complex} #29
Conversation
|
||
function squareroot(x) | ||
it = x | ||
while abs(it*it - x) > 1e-13 | ||
it = it - (it*it-x)/(2it) | ||
it = (it+x/it)/2 |
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.
Any reason for this change?
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.
It's just two fewer operations https://en.wikipedia.org/wiki/Square_root#Computation
This looks potentially very useful, thanks! We've been waiting for an application where this was needed, do you have an example or small write up that explains why dual numbers make sense with complex element types? What about Please deprecate the methods which are being removed, also use |
CC @goretkin |
I guess this breaks with 0.3 as well. Some toy examples are here, and with this PR, they carry over naturally to automatic differentiation of functions of a complex variable. I haven't thought about |
I'm okay with breaking 0.3, just be sure to update REQUIRE and the travis settings. |
I haven't followed along too carefully, but I imagine that ApproxFun needs to take some derivatives through, for example, a DFT. You could define a DFT that doesn't use any complex arithmetic, but the way the DFT is usually expressed relies on complex arithmetic. This is from a while back, but here's a sort-of clear example https://github.com/goretkin/ComplexDualNumbers.jl/blob/master/examples/dft_autodiff.jl I defined two functions |
For me, the biggest question is still what to do about Complex{Dual} and Dual{Complex}. Current definitions aside, it does make sense to interpret a "Complex Dual" number both as a Complex number with Dual parts, and as a Dual number with Complex parts. The same goes for Dual Quaternions. I don't think you necessarily have to answer that question in order to get this working. It wouldn't be so terrible to have |
|
||
immutable Dual{T<:ReComp} <: Number | ||
value::T | ||
dual::T |
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.
First of all, +1 to this, thanks for writing it up. I'm really interested in seeing this in action.
Can we use epsilon
instead of dual
for the ɛ
component? I feel like it would be clearer, and also less confusing for users transitioning to the new version of DualNumbers.jl (since before, dual
was the name of the vectorized constructor).
Thanks for taking a look! @mlubin, I deprecated the binding for @jrevels, I tossed the typealias constructors, reverted Now, the type @goretkin, @dlfivefifty showed me that the DFT/DCT of |
language: julia | ||
os: | ||
- linux | ||
- osx | ||
julia: | ||
- 0.3 | ||
- 0.4 | ||
- release |
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 should be 0.4
@mlubin, fixed! |
|
||
typealias Dual64 Dual{Float32} | ||
typealias Dual32 Dual{Float16} | ||
typealias DualComplex256 Dual{Complex128} |
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.
Do you find these useful or is this just for consistency?
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.
I don't know if I'll use them more often than the generic constructor, but I guess their purpose is to mirror Float64
and Complex128
etc...
The name |
The names I can think of are: num
values # assumes there are many
var
base
read # this is a stretch |
How about “diag”, since dual(a,b) is equivalent to [a b; 0 a]
|
What about defining:
In addition to avoiding needless deprecations in existing code, |
Another option is to define getindex(x::Dual, idx) = (idx == 1) ? x.value : x.epsilon # plus some error checking and use |
How about just using “x.value” and “x.epsilon”?
|
I don't see a reason to remove the current accessors for the real case, but if the dot syntax helps for the complex case then I'm not opposed. |
+1 for unexported I'm also in favor of the |
real(x::Dual{T<:Complex}) should return dual(real(x.value),real(x.epsilon))
|
Isn't there an expectation that |
I think “real” here is short for “realpart”, like “imag” is short for “imagpart”. So has nothing to do with the type Real. I’d expect Real(::Number) to return a Real.
|
👍 to unexported If we choose to be mathematically rigorous, shouldn't The same holds for One option is to add |
|
The dual part returns the epsilon component of z multiplied by f'(z.value), so: julia> z = Dual(1.0,1.0+im)
1.0 + 0.0im + 1.0 + 1.0imɛ
julia> exp(z)
2.718281828459045 + 0.0im + 2.718281828459045 + 2.718281828459045imɛ Doesn't this mean that the function must be complex differentiable? |
No I think it's just lim (f(z.value + h*z.epsilon) - f(z.value))/h When f is complex differentiable, this reduces to f'(z.value)z.epsilon, but that's just a special case. Sent from my iPhone
|
Actually, it is not true that real always returns a real in Base: julia> real([1+im 2; 3 4])
2x2 Array{Int64,2}:
1 2
3 4 |
I should've been more clear, I meant that It seems that these definitions: real{T<:Real}(x::Dual{T}) = x
real{T<:Complex}(x::Dual{T}) = Dual(real(value(x)), real(epsilon(x))) ...now have some pretty substantial arguments backing them, and I'm close to being won over. I especially feel like it would be very difficult to claim our I also do still share the same concerns as @mlubin - the standard nomenclature when discussing dual numbers is to refer to the non-perturbative component as the "real" component, and the function I'm not sure we can resolve this without introducing inconsistency somewhere, but in the end, if inconsistency is introduced, I'd rather have that inconsistency be nominative than behavioral (i.e. I prefer distorting Base's assumption of |
@jrevels, the dual part of the julia> using DualNumbers
julia> z = Dual(1.0+im,1.0)
1.0 + 1.0im + (1.0 + 0.0im)ɛ
julia> real(z)
1.0 + 1.0ɛ
julia> z = Dual(1.0+im,2.0+im)
1.0 + 1.0im + (2.0 + 1.0im)ɛ
julia> real(z)
1.0 + 2.0ɛ
julia> z = Dual(1.0+im,im)
1.0 + 1.0im + (0.0 + 1.0im)ɛ
julia> real(z)
1.0 + 0.0ɛ
As far as I can tell, this is correct. Thoughts on either of these three ways forward?
immutable Dual{T<:ReComp}
primal::T
dual::T
end and rename the accessors
|
Who would've thought this would be so hard? :) How about: realpart{T<:Real}(x::Dual{T}) = x.value
value(x::Dual) = x.value
epsilon(x::Dual) = x.epsilon
export realpart, epsilon
# After a cycle of deprecations, define:
real{T<:Real}(x::Dual{T}) = x
real{T<:Complex}(x::Dual{T}) = Dual(real(value(x)), real(epsilon(x))) I really don't want to give up the analogy of the first component being the real component in the case of |
Sounds good to me! I can make these changes if you're ok with me using your code. |
Sure, go ahead |
yet dual numbers can be generalized over any commutative ring R (real or complex numbers for instance) as the quotient ring R[X]/X^2 (so long as ɛ^2 = 0). https://en.wikipedia.org/wiki/Dual_number#Generalization This page makes me want to have |
@MikaelSlevinsky I didn't understand whether that was in support of removing the derivative definition or not. |
Sorry, I misread completely. That part is in the section on overriding Base functions, whereas the paragraphs above describe dual numbers more generally. Usually, evaluating a function at a dual number returns a dual number, so what's wrong with the definition (in the context of that part of the readme)? Suggest any changes? |
I don't want to hold up this PR with too much bikeshedding, but if we're leaning towards defining |
I'd much prefer |
Fair enough, you have more experience demoing this stuff then I do. Either way, +1 to both component accessors having names of the form |
|
||
value(z::Dual) = z.value | ||
epsilon(z::Dual) = z.epsilon | ||
value(z::Number) = z |
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.
When would this definition be used?
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.
I can't tell exactly but if you mean line 32: value(z::Number) = z
: if you know z
's a Number
but not necessarily a Dual
it's good form to have a no-op.
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.
I'd be in favor of removing the no-op until someone requests it for a good reason, but it's just a minor 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.
I agree with @mlubin. I'm not aware of other definitions of value
, but I am thinking of it as an accessor of the non-dual part of a dual number, and not as a "mathematical function" so to speak, so I don't think we need to (yet) worry about finding a meaning for other types of numbers.
Ok I removed I also added the constant julia> using DualNumbers
julia> z = 1.0+2.0im-3.0ɛ+4.0imɛ
1.0 + 2.0im - 3.0ɛ + 4.0imɛ
julia> ɛ
ɛ
julia> im*ɛ
imɛ
|
Looks basically ready to merge. Could you squash the commits? |
- remove dual() constructor (float() is deprecated in 0.4 anyways) - replace real & epsilon with value and dual - change du to ɛ addressing #24 - add Base.sinpi and Base.cospi as a patch addressing #23 - add complex function tests add 2-norm(Vector{Dual}) add cis (cos+im*sin) update readme to reflect more general support fixes dual => epsilon re-add vectorized constructor dual deprecate_binding du release => 0.4 and deprecate real(::Dual) dual => epsilon in readme un-export value incorporate some changes of #28: __precompile()__ function => constructor in readme import value in test add definitions for non-complex differentiable functions and tests all generic norms work with isinf() and float() fix print so that you can copy paste and it will make a new Dual add limit definition in readme fix typo: float is a no-op on Union{Dual{T},Dual{Complex{T}}}, not Union{Dual{T},Complex{T}} add no-op real{T<:Real} add angle, realpart, and dualpart deprecate real{T<:Real}(z::Dual{T}). also, because sign := z/|z| in base, it works automatically and so #22 is no longer necessary. remove value(z::Number) and epsilon(z::Number) - add the constant imɛ - add special dual_show methods for Dual{Bool}’s and Dual{Complex{Bool}}’s - replace real with DualNumbers.value - change du to ɛ addressing #24 - add imɛ - add Base.sinpi and Base.cospi as a patch addressing #23 - add cis (cos+im*sin) - deprecate_binding du - remove 0.3 from Travis build (and therefore support) - deprecate real(::Dual) - (real,epsilon) => (realpart,dualpart) in readme - incorporate some changes of #28: -- __precompile()__ - function => constructor in readme - add isinf() and float() so that all generic norms work - add show_dual{T<:Complex} so that you can copy paste and it will make a new Dual - add show_dual{T<:Bool} and show_dual{T<:Complex{Bool}} - add limit definition in readme - add angle, realpart, and dualpart - deprecate real{T<:Real}(z::Dual{T}) - sign := z/|z| in base, it works automatically and so #22 is no longer necessary - add tests
Done! :) |
Thanks! |
🎉 |
Thanks again! |
Is it time for a Pkg.tag? I'd like to push some code to SingularIntegralEquations but I want it to "require" the right version number of DualNumbers so users are unencumbered. |
I'll tag tomorrow. I've been running on master for a while with no apparent issues. |
Dear dualists (duellers?)
We would like to use DualNumbers in ApproxFun and dependent packages such as SingularIntegralEquations as a requirement. In ApproxFun, we create expansions of functions in numerous spectral bases (Chebyshev, Fourier, Legendre, Jacobi, etc...) and solve ordinary and partial differential equations in these bases as well.
One particular use of dual numbers will be in automatic differentiation to compute normal derivatives of incident waves to solve elliptic PDEs like the Helmholtz equation with Neumann boundary conditions in SingularIntegralEquations.
To make the transition possible, DualNumbers must be extended to work on Complex data types as well. This required a minor rewrite, and for the most part an improvement.
I would be happy to see this merged, though I can be patient given dependencies on DualNumbers that would break.
If merged, this would close #13, #23 (as a patch), and #24.