Relaxing constraint of Complex

Hello,
currently Complex is defined as follows

struct Complex{T<:Real} <: Number
    re::T
    im::T
end

I think that it would make sense to relax the T requirement from Real to Number (or something else). E.g. Maybe I want a biquaternion defined as Complex{Quaternion}. The current definition doesn’t allow for that.

1 Like

Since quaternions are non-associative it might be better to just use a new type, since Complex implementation may assume associativity and other property of reals.

They are associative. Are you thinking of commutativity? Also non commutativity is generally not a problem.

Sorry yes I meant commutative. It’s not a problem if the code assumes it, but I imagine there may be performance related code that assumes it.

Is there a difference between a Complex{Quaternion} and a Quaternion{Complex} in your case?

The second one almost never works.

Having looked into this in the past, for much the same reason, my current thought is that we need a distinct numeric splay and that merely relaxing the constraint on Complex{T} will open ill-considered constructs. There are three algebraic wraps of this general nature: Complex, Dual, Double. And, letting in the multilinear daylight is important … there should be a commonality of approach that well serves the Tensor fold, the Clifford facing, the Rotation oriented … and fans of Willmore flow visualization.

The shallow numeric type abstractions and their (eventually) concrete expression as umbrellas over leaf types were not designed to be as yet do better than, say, Haskell’s alternative numeric prelude. There is more of the how we calculate with distinct sorts of numeric types than there is of how we organize and interrelate these types of numerics.

I agree that more flexible is more helpful … imo preshaping the material [ways in which Julia is our purveyor of approachable avenues] more deeply would be a much bigger win.

Speaking of Dual, I was involved in the implementation of Dual{<:Complex}, spearheaded by @MikaelSlevinsky . There is a plan to make Dual <: Real which would mean that Complex{<:Dual} would work instead. I think this will lead to cleaner code.

So I think the point is there are often multiple ways of doing things, and jumping to relaxing type restrictions may not be necessary. Or in the case of complex quaternions is bit like calling a sock a shoe: sure it might work but maybe it’s better as a sock.

2 Likes

I still think there’s some yet-to-be-designed way to express that Complex{Dual{Float64}} and Dual{Complex{Float64}} are different names for the same “thing”: a number with four basis elements (1, im, eps, im*eps). I can’t think of any situation in which the two spellings should be treated differently, or why any spelling should be prohibited. They should be mutually reinterpret-able into each other.

I think this is related to:
1 https://en.wikipedia.org/wiki/AOS_and_SOA
2. https://en.wikipedia.org/wiki/Structural_type_system

There are several issues with trying to treat these the same. Probably, right now,
real(Complex{Dual{Float64}})::Dual{Float64} and real(Dual{Complex{Float64}})::Complex{Float64}, when actually in different situations you might want either of these, or also real(Dual{Complex{Float64}})::Float64.

I’m not confident this is the appropriate analogy, but

julia> AoS = [(1,2),(3,4)]
2-element Array{Tuple{Int64,Int64},1}:
 (1, 2)
 (3, 4)

julia> SoA = ([1,3],[2,4])
([1, 3], [2, 4])

julia> first(AoS)
(1, 2)

julia> first.(SoA)
(1, 2)

julia> first(SoA)
2-element Array{Int64,1}:
 1
 3

julia> first.(AoS)
2-element Array{Int64,1}:
 1
 3

One important difference between the two is that the memory layout is different. reinterpret is specifically for changing the type-interpretation of a block of memory.

That’s a good point. I was punning on reinterpret. There would need to be another generic operation to express what I meant.

And any code that relies on the memory layout of specific types would, of course, need to dispatch on the specific type that guarantees this memory layout.