I'm wondering if it is good to have AbstractComplex
, I think this is pretty useful since there can be different representation format for complex number as well, and they should share some similar interface and be treated both as complex number. e.g
exp(theta*im)
, it only need one floating point or number.on the other hand, currently use some traits might help, but this will make complex numbers different from other numbers...
PS. integers have Integer
, real numbers have Real
, but complex number have none... it is not fair :-)
Seems good. Would allow e.g. PolarComplex <: AbstractComplex
to inherit most generic code.
I'm not necessarily opposed to an abstract supertype per se, but polar representations are a poor motivation.
The possibility of internally representing complex numbers in polar or cartesian form is one of those shibboleths that OOP textbooks like to give as examples of abstraction, but which are never used in practice. The basic issue is that practically the only thing you would ever want to do with the polar representation is multiplication/division, but for these restricted operations a Number
abstraction is not useful — you can and should just work with (r,φ)
directly. You don't need or want an "internal" polar representation that is hidden by abstraction: you have to work with the polar form explicitly to ensure that the only operations you do on this format are multiplication and similar.
or in my case I would like to define a symbolic type for complex number, which will directly inherit most of the complex number interfaces and make a lot function "just work"
If you have a symbolic type for a real number that is a <: Real
, then you can use it with the existing Complex
type already. Or you can make your SymbolicComplex
a subtype of Number
(or Any
), since virtually nothing will "just work" on a complex type that doesn't share the structure of Complex
and doesn't represent a specific numeric value anyway.
I'd like to see a useful example of an alternative AbstractComplex
subtype where nontrivial existing Complex
code would actually work if it were changed to use the new type…
Thanks for the comment @stevengj , the main motivation for me is the symbolic calculations. I just think other cases can also benefit from this.
If you have a symbolic type for a real number that is a <: Real, then you can use it with the existing Complex type already.
I actually tried this solution at first, but it seems not working so well, this is because expressions can be complex as well, and it might not be
able to be divided into 2 real symbolic expression easily (and not necessarily), it will be much easier to define the imaginary unit and merge similar terms when possible then tag the expression's domain. Handling the imaginary numbers with correct printing also seems to be painful with this solution.
Or you can make your SymbolicComplex a subtype of Number (or Any), since virtually nothing will "just work" on a complex type that doesn't share the structure of Complex and doesn't represent a specific numeric value anyway.
This is basically what I'm doing now, I made SymComplex
and SymReal
to be a symbolic tag of expressions, they will tag the expression's domain via type inference. so they will be treated correctly in practice, however since I cannot define SymComplex <: AbstractComplex
, this means in practice other packages (like our own) which contains boundary constrains on Complex
won't work but it should work in principal, a simplified version:
struct ArrayReg{B, T<:Complex, MT <: AbstractMatrix{T}} <: AbstractRegister{B}
# blabla
end
(we don't actually do type checks here, but in constructors) and if I feed my symbolic complex number, it will give me warnings saying this is not a complex number currently, which is not very elegant.
or another case here: https://github.com/QuantumBFS/YaoBlocks.jl/blob/cc3344559ae7ac8bde7d293ddadb054ac8a4e0f3/src/primitive/reflect_gate.jl
the point is existing Complex
do not actually allow users to define a generic upper bound of the desired complex type if we consider there are things like symbolic complex numbers. It limits the possibility of extending this object.
If a function specifies T <: Complex
, and cannot handle T <: Number
, then it is hard for me to imagine that any nontrivial code would actually support a totally different symbolic complex-number type. If the code does support your symbolic complex type, then it would probably support Number
as well and should be widened accordingly.
e.g. in the quantum code you linked, if it is generic enough to handle your symbolic type, then arguably it is also generic enough to handle e.g. quaternion quantum states and should be widened to Number
.
So do you suggest we could use Number
as some sort of AbstractComplex
here? if
If a function specifies T <: Complex, and cannot handle T <: Number, then it is hard for me to imagine that any nontrivial code would actually support a totally different symbolic complex-number type.
I think we add that <:Complex
bound mainly because we wanted a complex domain upper bound just for readability it work for Number
indeed.
Triage is ok with this. Adding the type seems harmless enough. Adding a pure imaginary type is one possible application.
The tricky part of this is going through and figuring out which methods in Base could apply to AbstractComplex
. It's nontrivial, since presumably every AbstractComplex type can implement reim
but that doesn't necessarily mean the method is useful (a loose analogy perhaps is calling dense matrix fallback implementations on sparse arrays).
I don't think Number
is a good substitute for AbstractComplex
because one can imagine wanting to write a f(::AbstractComplex)
that doesn't work on say Quaternion
s or CliffordNumbers
.
This is tentatively approved but there is work to do to move it forward: someone needs to go through the various method definitions for Complex
and decide which ones are specific to the concrete Complex
type and which ones are applicable to AbstractComplex
.
I would like to implement a type ComplexParticles <: AbstractComplex
that internally represents the number as a collection of samples from a distribution. This already works very well for real numbers in MonteCarloMeasurements.jl, but all the code generation there assumes that
Particles{T} <: Real
and T<: Real
such that every function that accepts a particle type also accepts one of its internal samples. If I represent this as Complex{Particles{T<: Real}}
I need to reimplement a lot of functions, and the reverse Particles{Complex{T}}
does not work as it violates the assumption that Particles and its samples are the "same" type.
That's an interesting use case. The path here is open:
someone needs to go through the various method definitions for
Complex
and decide which ones are specific to the concreteComplex
type and which ones are applicable toAbstractComplex
.
To be clear, I'm not aware of anyone who is currently planning on doing this, so if someone wants an AbstractComplex
type they should perhaps work on this.
I'm interested in working on this. I'll try to open a PR sometime in the next week.
Why can't the package accept Number
?
Why can't the package accept
Number
?
I don't know about @baggepinnen's usecase, but I'm working with symbolic number-types. In particular, I'm quite interested in non-commutative number systems, so I want to reserve subtypes of Number
for types where I can't make any assumptions about commutativity. Complex numbers do commute under multiplication so I don't want something like SymbolicComplex <: Number
.
Why can't the package accept
Number
?
It can, it's just a matter of how to implement the functionality. If I go for Complex{Particles}
, which is currently the case, the number dispatches as a Complex
and I would have to implement a lot of methods for Complex{<:AbstractParticles}
instead of getting that behavior for free by dispatching as AbstractParticles
.
However, is type Particles
supposed to be Real
or I mean mathematically it is a complex number or something else? @baggepinnen I think if mathematically if a type follows the same structure as Complex
, that's where AbstractComplex
is useful.
Yeah, I opened this issue originally for symbolic systems. It'd be great if @MasonProtter could work on this, or I could work on this in summer when I have more free time. Also let me know if I could help when you open the PR!
I would like to implement a type
ComplexParticles <: AbstractComplex
I have Particles <: Real
and would like to have the new ComplexParticles <: AbstractComplex
would like to have the new ComplexParticles <: AbstractComplex
Why? What methods do you want to use that are not defined for Number
?
Just to add one more motivation for AbstractComplex
: In https://github.com/kalmarek/Cyclotomics.jl I deal with (semi-symbolic?) sums of roots of unity, which are definitely complex with non-trivial arithmetic:
julia> E(45)^9 # 45-th root of unity raised to power 9
ζ₅
julia> E(45)^5 #45-th root of unity raised to power 5
-ζ₉⁴ -ζ₉⁷ # since vector space of 9-th root of unity does not contain ζ₉
julia> E(45)^5 + E(45)^9
ζ₄₅² +ζ₄₅⁸ +ζ₄₅¹¹ +ζ₄₅¹⁷ -ζ₄₅²⁴ +ζ₄₅²⁶ +ζ₄₅²⁹ +ζ₄₅³⁸ -ζ₄₅³⁹ +ζ₄₅⁴⁴ # no reduction to smaller cyclotomic field is possible
julia> x = -E(5)^2 - E(5)^3
-ζ₅² -ζ₅³
julia> isreal(x)
true
julia> float(x) == (1+√5)/2
true
It'd be much easier for me to make it into AbstractComplex
Most helpful comment
I'm not necessarily opposed to an abstract supertype per se, but polar representations are a poor motivation.
The possibility of internally representing complex numbers in polar or cartesian form is one of those shibboleths that OOP textbooks like to give as examples of abstraction, but which are never used in practice. The basic issue is that practically the only thing you would ever want to do with the polar representation is multiplication/division, but for these restricted operations a
Number
abstraction is not useful — you can and should just work with(r,φ)
directly. You don't need or want an "internal" polar representation that is hidden by abstraction: you have to work with the polar form explicitly to ensure that the only operations you do on this format are multiplication and similar.If you have a symbolic type for a real number that is a
<: Real
, then you can use it with the existingComplex
type already. Or you can make yourSymbolicComplex
a subtype ofNumber
(orAny
), since virtually nothing will "just work" on a complex type that doesn't share the structure ofComplex
and doesn't represent a specific numeric value anyway.I'd like to see a useful example of an alternative
AbstractComplex
subtype where nontrivial existingComplex
code would actually work if it were changed to use the new type…