# Custom Complex number type for specific Real type

**URL:** <https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374>\
**Category:** General Usage\
**Tags:** numbers, complex-numbers\
**Created:** [January 29, 2024, 3:19am UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374 "2024-01-29T03:19:22Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [January 29, 2024, 3:19am UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/1 "2024-01-29T03:19:22Z")

</div>

Hello! I have implemented a Julia interface to a C library for a special number type. The real and complex types are handled internally by the C library, so I have Julia wrapper structs that are basically `MyNumber` and `ComplexMyNumber` and have already laboriously implemented all of the operators including with promotion, etc (the C library has special operators when promoting a `MyNumber` to `ComplexMyNumber`).

I would like for `MyNumber <: Real` and `ComplexMyNumber <: Number`, however I see in `base/complex.jl`:

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

```

`ComplexMyNumber`, which is handled internally by the C library, cannot be defined as two separate `MyNumber`s for the real and imaginary parts. In my wrapper, `ComplexMyNumber` and `MyNumber` are just structs containing `Ptr`s to the C objects. So, is there some way that I can basically force that the type `Base.Complex{MyNumber} = ComplexMyNumber` so that whenever a `Complex{MyNumber}` is defined, it uses my special struct `ComplexMyNumber` instead?  
.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [January 29, 2024, 4:11am UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/2 "2024-01-29T04:11:52Z")

</div>

Technically you can make a constructor not return the specified type.

```julia
Base.Complex{MyNumber}(x::MyNumber) = ComplexMyNumber(x)
Base.Complex{MyNumber}(x::MyNumber, y) = ComplexMyNumber(x, convert(MyNumber, y))

```

**But you shouldn’t have to. And its generally pretty weird and unexpected when people do so**

You can overload

```julia
Base.complex(x::MyNumber) = ComplexMyNumber(x)
Base.complex(::Type([MyNumber}) = ComplexMyNumber

```

Which is the function that is supposed to be called to determine what complex type is used for any given real type. (If someone is instead hardcoding it to `Compelx{MyNumber}` then it is arguably a bug in their code)  
From the docs:

> complex(T::Type)
> 
> Return an appropriate type which can represent a value of type T as a  
> complex number.

and you have already implemented all  
“all of the operators” which is generally what I would suggest doing.  
Then anything that accepts `Number`s in general should work for your type

**What code right now exists that is causing you problems that you want to solve in this way?**

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 29, 2024, 6:22am UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/3 "2024-01-29T06:22:29Z")

</div>

I suppose it was just an instinct to subtype, alias, or otherwise relate `ComplexMyNumber` to the existing `Complex` like you would have `MyNumber` subtype `Real`, but there’s really no good reason for it. `Complex` is a parametric `UnionAll` type that contains _concrete_ `DataType` types with specified parameters. Since `ComplexMyNumber` cannot have the structure of `Complex{MyNumber}`, they cannot be the same concrete type, they cannot share methods, and `Complex` cannot contain `ComplexMyNumber`. In fact, I would suggest making the `Complex{MyNumber}` constructor throw a descriptive error if you should always use `ComplexMyNumber` instead. There’s no `AbstractComplex` type for complex numbers like the abstract `DataType` type `Real` for real numbers, which is why `Complex` subtypes `Number`; that does also mean the methods annotated by `Complex` in complex.jl is specific to that struct, and you’ll need to make analogous methods for `ComplexMyNumber`.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 29, 2024, 6:35am UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/4 "2024-01-29T06:35:09Z")

</div>

Let’s say we have this definition of `ComplexMyNumber`.

```julia-repl
julia> struct ComplexMyNumber <: Number
           ptr::Ptr{Float64}
       end

```

We can override `Base.getproperty` for this type.

```julia
julia> function Base.getproperty(c::ComplexMyNumber, s::Symbol) 
           if s == :re
               unsafe_load(getfield(c, :ptr), 1)
           elseif s == :im
               unsafe_load(getfield(c, :ptr), 2)
           else
               getfield(c, s)
           end
       end

```

Then we could do the following to create virtual properties that do not correspond to actual fields.

```julia-repl
julia> v = Float64[3,4]
2-element Vector{Float64}:
 3.0
 4.0

julia> cmn = ComplexMyNumber(pointer(v))
ComplexMyNumber(Ptr{Float64} @0x00007f42a82663e0)

julia> cmn.re
3.0

julia> cmn.im
4.0

```

We can then define conversion from `ComplexMyNumber` to `Complex{Float64}`.

```julia-repl
julia> Base.convert(::Type{Complex{Float64}}, c::ComplexMyNumber) =
           Complex{Float64}(c.re, c.im)

julia> convert(Complex{Float64}, cmn)
3.0 + 4.0im

```

We can then take advantage of implicit conversion such as when pushing or assigning into an array.

```julia-repl
julia> complex_vector = Complex{Float64}[]
ComplexF64[]

julia> push!(complex_vector, cmn)
1-element Vector{ComplexF64}:
 3.0 + 4.0im

julia> v2 = Float64[9,10]
2-element Vector{Float64}:
  9.0
 10.0

julia> cmn2 = ComplexMyNumber(pointer(v2))
ComplexMyNumber(Ptr{Float64} @0x00007f42a7fde930)

julia> cmn[1]
ComplexMyNumber(Ptr{Float64} @0x00007f42a82663e0)

julia> complex_vector
1-element Vector{ComplexF64}:
 3.0 + 4.0im

julia> complex_vector[1]
3.0 + 4.0im

julia> complex_vector[1] = cmn2
ComplexMyNumber(Ptr{Float64} @0x00007f42a7fde930)

julia> complex_vector[1]
9.0 + 10.0im

```

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [January 29, 2024, 2:30pm UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/5 "2024-01-29T14:30:09Z")

</div>

> [@oxinabox](#):
>
> What code right now exists that is causing you problems that you want to solve in this way?

There isn’t actually any “problem” in my code right now, it’s just that `MyNumber` and `ComplexMyNumber` do not really conform with the `Complex{T<:Real}` defined in Base. And so I’m not sure about saying `MyNumber <: Real`, when `MyNumber` cannot satisfy everything that a `Real` type can in Base. **However in theory `MyNumber` and `ComplexMyNumber` are “`Real`” and “`Number`” types respectively, and for usability of the package they should be allowed where these abstract types are allowed** , but I guess that does mean I need to play some tricks on the back end to prevent undefined behavior.

> [@Benny](#):
>
> Since `ComplexMyNumber` cannot have the structure of `Complex{MyNumber}`, they cannot be the same concrete type, they cannot share methods, and `Complex` cannot contain `ComplexMyNumber`. In fact, I would suggest making the `Complex{MyNumber}` constructor throw a descriptive error if you should always use `ComplexMyNumber` instead. There’s no `AbstractComplex` type for complex numbers like the abstract `DataType` type `Real` for real numbers, which is why `Complex` subtypes `Number`; that does also mean the methods annotated by `Complex` in complex.jl is specific to that struct, and you’ll need to make analogous methods for `ComplexMyNumber`.

This feels like the safest solution, since `ComplexMyNumber` is indeed not a `Complex{MyNumber}` as you say, and it prevents users from attempting to define a `ComplexMyNumber` in this way. But does this cover all bases when defining `MyNumber <: Real` and `ComplexMyNumber <: Number`?

> [@mkitti](#):
>
> We can override `Base.getproperty` for this type.
> 
> ```julia
> julia> function Base.getproperty(c::ComplexMyNumber, s::Symbol) 
> if s == :re
> unsafe_load(getfield(c, :ptr), 1)
> elseif s == :im
> unsafe_load(getfield(c, :ptr), 2)
> else
> getfield(c, s)
> end
> end
> 
> ```

Though the implementation of `MyNumber` is much more complicated than this on the C end, I think I could make something like this work for `ComplexMyNumber`. However because `Complex{MyNumber}` is not actually a `ComplexMyNumber`, I do have some concerns about undefined behavior from operations defined in Base with this method

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 29, 2024, 2:53pm UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/6 "2024-01-29T14:53:44Z")

</div>

> [@mattsignorelli](#):
>
> But does this cover all bases when defining `MyNumber <: Real` and `ComplexMyNumber <: Number`?

I’m not exactly sure what covers all bases means here, but I would say no because you can see many fundamental complex number methods like `imag`, `real`, (these 2 I would put in an nonexistent `AbstractComplex` interface) `abs`, `angle`, `conj`, etc. that all dispatch on `Complex`, which won’t apply to `ComplexMyNumber`. You would basically have to reimplement complex.jl at minimum, then any method that dispatches on `Complex` instead of `Number`. If it is even possible, converting between `Complex` and `ComplexMyNumber` would add some processing time but you can leverage the existing `Complex` methods. I’m not sold on implementing `getproperty` to emulate `Complex`’s `.re` and `.im` fields because the `real` and `imag` functions are used instead to be consistent with methods for other `Number`s; check complex.jl, the only time `.re` and `.im` shows up instead is a `show` method with `Complex{Bool}`, and I’m not convinced that wasn’t a typo.

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [January 29, 2024, 3:27pm UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/7 "2024-01-29T15:27:16Z")

</div>

> [@Benny](#):
>
> I’m not exactly sure what covers all bases means here

I mean, at what point am I allowed to say that something is a `Real` and something is a `Number`?

> [@Benny](#):
>
> but I would say no because you can see many fundamental complex number methods like `imag`, `real`, (these 2 I would put in an nonexistent `AbstractComplex` interface) `abs`, `angle`, `conj`, etc. that all dispatch on `Complex`, which won’t apply to `ComplexMyNumber`.

All of these methods you mention (`real`, `imag`, `angle`, `abs`, `conj`) I have implemented already ( the C library provides these methods). The library also comes with some methods even not in Base (`polar`, `rect`, `sinhc`). I just looked through complex.jl and most of these methods I have already implemented, and the few ones I haven’t yet I can do so.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [January 29, 2024, 3:37pm UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/8 "2024-01-29T15:37:36Z")

</div>

I would definitely favor finishing wrapping as much of the library as possible in base Julia functions and possibly new specific functions for consistent results, and include the `Complex` conversion as a pragmatic backup. I’m guessing the conversion can also help performance by storing results, specifiically I’m guessing `ComplexMyNumber` has its structure because computing the real and imaginary part is not cheap enough to do immediately upon instantiation.

> [@mattsignorelli](#):
>
> I mean, at what point am I allowed to say that something is a `Real` and something is a `Number`?

Roughly distinguished by whether it’s representing a real number, more precisely by their methods (for example, you want `real(::Real)` to be like `identity`). Look at the tree diagram in [Numbers · The Julia Language](https://docs.julialang.org/en/v1/base/numbers/#Standard-Numeric-Types), all base Julia numerical types are `Real` except for `Complex` which is a composite type parameterized by `<:Real`. Don’t worry about type hierarchies not reflecting mathematical sets perfectly, types are for instantiation and dispatch first and foremost.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [January 29, 2024, 4:08pm UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/9 "2024-01-29T16:08:11Z")

</div>

What you really want here is the proposed `AbstractComplex` in this issue: [abstract complex? · Issue #33246 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/33246). Unmerged implementation here: [https://github.com/JuliaLang/julia/pull/35587](https://github.com/JuliaLang/julia/pull/35587).

---

<div class="post-metadata">

**Author:** ![mattsignorelli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattsignorelli/32/221502_2.png) [@mattsignorelli](https://discourse.julialang.org/u/mattsignorelli)\
**Post date:** [January 30, 2024, 12:40am UTC](https://discourse.julialang.org/t/custom-complex-number-type-for-specific-real-type/109374/10 "2024-01-30T00:40:26Z")

</div>

> [@Benny](#):
>
> I would definitely favor finishing wrapping as much of the library as possible in base Julia functions and possibly new specific functions for consistent results, and include the `Complex` conversion as a pragmatic backup

Just to make sure I understand correctly, you recommend allowing `Complex{MyNumber}` as a backup? This in theory should work for all the Base methods I overloaded, but for the C library specific methods not in Base, would not be allowed. It certainly would be slower to use this backup.

> [@StefanKarpinski](#):
>
> What you really want here is the proposed `AbstractComplex` in this issue: [abstract complex? · Issue #33246 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/33246). Unmerged implementation here: [https://github.com/JuliaLang/julia/pull/35587](https://github.com/JuliaLang/julia/pull/35587).

This seems great! Only question I’d have is how to make sure that I can still declare `MyNumber <: Real` and ensure that the `AbstractComplex` representation is used by default instead of `Complex{MyNumber}` (preventing/making it very difficult for users to say `Complex{MyNumber}` over `ComplexMyNumber`)

For now at least I can call `ComplexMyNumber <: Number` and just wrap as much of the library as possible in Julia base functions, and decide how to handle or just prevent `Complex{MyNumber}`
