# Complexifying pi

**URL:** <https://discourse.julialang.org/t/complexifying-pi/23885>\
**Category:** General Usage\
**Created:** [May 5, 2019, 9:37pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885 "2019-05-05T21:37:21Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tobydriscoll](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobydriscoll/32/1843_2.png) [@tobydriscoll](https://discourse.julialang.org/u/tobydriscoll)\
**Post date:** [May 5, 2019, 9:37pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/1 "2019-05-05T21:37:21Z")

</div>

I want to make a type that internally stores values in a `Complex` form. I assumed that I should simply convert any `Real` input. However, some math constants give an error doing this:

```julia
julia> Complex(2.0f0)
2.0f0 + 0.0f0im

julia> Complex(pi/2)
1.5707963267948966 + 0.0im

julia> Complex(pi)
ERROR: MethodError: no method matching Irrational{:π}(::Int64)
 ...
Stacktrace:
 [1] convert(::Type{Irrational{:π}}, ::Int64) at ./number.jl:7
 [2] oftype(::Irrational{:π}, ::Int64) at ./essentials.jl:334
 [3] zero(::Irrational{:π}) at ./number.jl:262
 [4] Complex(::Irrational{:π}) at ./complex.jl:16
 [5] top-level scope at none:0

```

The same happens for `ℯ`. I caught it only because of a very arbitrary unit test. I’m not saying this behavior is irrational (har har!), but it is arguably not intuitive, so I would love to know if it’s intentional.

For the `Real` types I’ve thought to try, it does always work to add `0im`. Not a big deal, but it feels a bit non-Julian to use addition for what is fundamentally a type conversion. I’m open to equally simple yet general alternatives.

---

<div class="post-metadata">

**Author:** ![longemen3000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/longemen3000/32/7298_2.png) [@longemen3000](https://discourse.julialang.org/u/longemen3000)\
**Post date:** [May 5, 2019, 10:00pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/2 "2019-05-05T22:00:07Z")

</div>

a easy solution would be `Complex(1*pi)`, but i’m checking about the irrational type:

update, i found this:

> **[Some fun with Π in Julia](https://julialang.org/blog/2017/03/piday/)**
>
> Some fun with π in Julia | !pi (/assets/blog/2017-03-14-piday/pi.png) ^credit ...

basically, the Irrational type gives arbitrary precision for math constants, allowing BigFloat(pi) to be pi with more digits, instead of a Float64 aproximation, the blog post explains that with more detail

---

<div class="post-metadata">

**Author:** ![tkluck](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkluck/32/15769_2.png) [@tkluck](https://discourse.julialang.org/u/tkluck)\
**Post date:** [May 5, 2019, 10:15pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/3 "2019-05-05T22:15:01Z")

</div>

To add to @longemen3000’s explanation: the thing that does work is

```julia
julia> Complex{Float64}(pi)
3.141592653589793 + 0.0im

```

because now we can infer to what precision you want to have it.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [May 5, 2019, 10:15pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/4 "2019-05-05T22:15:26Z")

</div>

The question is, should those types compose? Should there be a `Complex{Irrational{...}}` type?

---

<div class="post-metadata">

**Author:** ![longemen3000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/longemen3000/32/7298_2.png) [@longemen3000](https://discourse.julialang.org/u/longemen3000)\
**Post date:** [May 5, 2019, 10:29pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/5 "2019-05-05T22:29:35Z")

</div>

in this case, i think is preferable to allow the composition, and transform the irrational to a concrete numeric type when an operation is performed, but the problem is that the Complex type asigns the type of the stored numbers to be the same:

```julia
julia> x = Complex(1,BigFloat(1))
1.0 + 1.0im

julia> typeof(x)
Complex{BigFloat}

julia> typeof(x.re)
BigFloat

```

as there is no implementation of zero(\<:Irrational) (and it doesn’t make sense, zero and one aren’t irrationals), the generation of the Complex{Irrational{…}}` fails. this can be avoided at expense of hiding the transformation from the user, using a sane default (Float64?)

```julia
#something like this?
Complex(x::Irrational) = Complex{Float64}(x)

```

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 5, 2019, 11:10pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/6 "2019-05-05T23:10:01Z")

</div>

> [@DNF](#):
>
> Should there be a `Complex{Irrational{...}}` type?

The type exists already:

```julia
julia> complex(pi, pi) |> typeof
Complex{Irrational{:π}}

```

but the point is that the parameter of `Complex{T}` is the type of both the real _and_ the imaginary parts, so the only way to build a `Complex{Irrational{...}}` is to do `complex(x, x)` with `x isa Irrational`.

If one wants to have `complex(pi)` with the highest precision possible, `complex(big(pi))` is the only solution currently.

For more information see:

> <https://github.com/JuliaLang/julia/issues/21204>
>
> A \`Complex{Irrational}\` is allowed (e.g., \`Complex(pi, pi)\`), but there are a fe…w functions that currently don't work with this type (e.g., \`exp\`, \`exp2\`, \`exp10\`; \`exp\` can be easily fixed if #21203 is merged, the other two functions can be fixed as part of the fix to #21200).
> 
> Should this type be kept (and non-working functions fixed) or automatically converted to, say, \`Complex{Float64}\`? I don't think this type is widely used (its odd representation in the REPL seems to confirm this) and any mathematical operation with it promotes it to another type, but I think that a decision should be taken about its status.
> 
> TODO list:
> \- \[x\] make \`big(complex(pi, pi))\` work (PR #21218)
> \- \[x\] fix \`exp\` (PR #21596)
> \- \[x\] make \`sinpi(complex(pi, pi))\` work (actually, not even \`sinpi(pi)\` works right now). Same for \`cospi\`. Requires #21219 (PR #21781)
> \- \[x\] fix \`log1p\`: can be fixed with \`one(float(T))\` in place of \`float(one(T))\` (which is also more efficient for Big numbers, only one number is allocated) if #21219 if merged (PR #21784)
> \- \[x\] fix \`exp2\` and \`exp10\`: see \[this comment\](https://github.com/JuliaLang/julia/issues/21204#issuecomment-289900420) below, requires PR #21219 (PR #21874)
> \- \<s\>make \`complex(pi)\` work (PR #22928)\</s\> The decision was to not make \`complex(irrational)\` work at all.

---

<div class="post-metadata">

**Author:** ![tkluck](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkluck/32/15769_2.png) [@tkluck](https://discourse.julialang.org/u/tkluck)\
**Post date:** [May 6, 2019, 7:13am UTC](https://discourse.julialang.org/t/complexifying-pi/23885/7 "2019-05-06T07:13:02Z")

</div>

> [@longemen3000](#):
>
> there is no implementation of zero(\<:Irrational) (and it doesn’t make sense, zero and one aren’t irrationals),

OTOH, they are numbers that can be computed to arbitrarily high precision (trivially), so there’s also no particular reason _not_ to implement such methods.

Not sure that’s helpful in the grander scheme of what you’re saying, just thought it might be a useful way to think about it.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [May 6, 2019, 7:13am UTC](https://discourse.julialang.org/t/complexifying-pi/23885/8 "2019-05-06T07:13:52Z")

</div>

Irrationals are represented by singletons. It would be easy to have `Irrational{:zero}` (after all, `Irrational` represents a non-terminating turing machine that writes better and better approximations of a real number onto an infinite tape, i.e. is a glorified function pointer), but `Complex` does not permit real and imaginary parts to have different types. This means that e.g.

```julia
julia> Complex(pi, ℯ) |>typeof
Complex{Float64}

```

Even if we had irrational zero, this would not be enough to represent `Complex(pi, true_zero)`.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 6, 2019, 9:08am UTC](https://discourse.julialang.org/t/complexifying-pi/23885/9 "2019-05-06T09:08:48Z")

</div>

These things were already discussed in the issue I linked above, including the irrational zero.

Actually, there are a couple of ways to define a complex irrational number which retains its irrational features:

```julia
julia> Complex{Real}(pi, 0)
π = 3.1415926535897... + 0im

[...]

julia> Base.complex(x::T) where {T<:Irrational} = Complex{Union{T, Int}}(x, 0)

julia> complex(pi)
π = 3.1415926535897... + 0im

```

but they aren’t super efficient (at least, they weren’t two years ago) because of type instability, see  
[https://github.com/JuliaLang/julia/pull/22928](https://github.com/JuliaLang/julia/pull/22928)

---

<div class="post-metadata">

**Author:** ![tobydriscoll](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobydriscoll/32/1843_2.png) [@tobydriscoll](https://discourse.julialang.org/u/tobydriscoll)\
**Post date:** [May 6, 2019, 1:53pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/10 "2019-05-06T13:53:19Z")

</div>

Thanks to @longemen3000 and @giordano for the pointers to the discussions.

To be clear, I’m not personally after some abstract notion of complex irrationals, which I’ve never given any thought to. I was looking for a way to convert to a complex representation built from whatever real type the caller sends. And yes, it’s easy for me to actually do.

My point is the same one made by @giordano on 28 July 2017 in the thread he gave: “I’d say that it’s better to automatically convert complex(pi) to Complex{Float64} (first version of this PR) rather than throwing an error for a simple conversion.”

Justifications of current behavior based on preserving the abstraction of `pi` to irrationality or arbitrary precision then have to explain why `Complex(pi,0)` and `pi/2` not only work but produce results based on `Float64` (which is what the majority of scientific computation expects).

The treatment of `pi` as a special kind of value is only a mm deep. Indeed, `methodswith(typeof(pi))` returns no results in base. Irrational pi should be the use case that you go out of your way to find, as opposed to the current situation of the tail wagging the dog. I’d even go so far as to say it ought to be an explicit import.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 6, 2019, 2:03pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/11 "2019-05-06T14:03:29Z")

</div>

> [@tobydriscoll](#):
>
> Justifications of current behavior based on preserving the abstraction of `pi` to irrationality or arbitrary precision then have to explain why `Complex(pi,0)` and `pi/2` not only work but produce results based on `Float64` (which is what the majority of scientific computation expects).

`complex(pi, 0)` promotes the arguments to a common type, that is `Float64`. `pi/2` is a `Float64`.

> [@tobydriscoll](#):
>
> Indeed, `methodswith(typeof(pi))` returns no results in base.

This is more useful:

```julia
julia> methodswith(AbstractIrrational)
[1] *(x::Bool, y::AbstractIrrational) in Base at irrationals.jl:135
[2] *(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:133
[3] +(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:133
[4] -(x::AbstractIrrational) in Base at irrationals.jl:131
[5] -(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:133
[6] /(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:133
[7] <(x::Float32, y::AbstractIrrational) in Base at irrationals.jl:76
[8] <(x::Float64, y::AbstractIrrational) in Base at irrationals.jl:74
[9] <(x::Float16, y::AbstractIrrational) in Base at irrationals.jl:78
[10] <(x::BigFloat, y::AbstractIrrational) in Base at irrationals.jl:82
[11] <(x::Rational{BigInt}, y::AbstractIrrational) in Base at irrationals.jl:117
[12] <(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:61
[13] <(x::AbstractIrrational, y::Float64) in Base at irrationals.jl:73
[14] <(x::AbstractIrrational, y::Float32) in Base at irrationals.jl:75
[15] <(x::AbstractIrrational, y::Float16) in Base at irrationals.jl:77
[16] <(x::AbstractIrrational, y::BigFloat) in Base at irrationals.jl:79
[17] <(x::AbstractIrrational, y::Rational{BigInt}) in Base at irrationals.jl:116
[18] <(x::AbstractIrrational, y::Rational{T}) where T in Base at irrationals.jl:99
[19] <(x::Rational{T}, y::AbstractIrrational) where T in Base at irrationals.jl:108
[20] <=(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:66
[21] <=(x::AbstractIrrational, y::AbstractFloat) in Base at irrationals.jl:86
[22] <=(x::AbstractFloat, y::AbstractIrrational) in Base at irrationals.jl:87
[23] <=(x::AbstractIrrational, y::Rational) in Base at irrationals.jl:119
[24] <=(x::Rational, y::AbstractIrrational) in Base at irrationals.jl:120
[25] ==(::AbstractIrrational, ::AbstractIrrational) in Base at irrationals.jl:57
[26] ==(x::AbstractIrrational, y::Real) in Base at irrationals.jl:69
[27] ==(x::Real, y::AbstractIrrational) in Base at irrationals.jl:70
[28] ^(::Irrational{:ℯ}, x::AbstractIrrational) in Base.MathConstants at mathconstants.jl:91
[29] ^(x::AbstractIrrational, y::AbstractIrrational) in Base at irrationals.jl:133
[30] big(x::AbstractIrrational) in Base at irrationals.jl:174
[31] isfinite(::AbstractIrrational) in Base at irrationals.jl:122
[32] isinteger(::AbstractIrrational) in Base at irrationals.jl:123
[33] isone(::AbstractIrrational) in Base at irrationals.jl:125
[34] iszero(::AbstractIrrational) in Base at irrationals.jl:124
[35] rationalize(::Type{T}, x::AbstractIrrational) where T in Base at irrationals.jl:91

```

---

<div class="post-metadata">

**Author:** ![tobydriscoll](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobydriscoll/32/1843_2.png) [@tobydriscoll](https://discourse.julialang.org/u/tobydriscoll)\
**Post date:** [May 6, 2019, 2:24pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/12 "2019-05-06T14:24:49Z")

</div>

I see I missed the modest number methods. I find `iszero` and `isone` to be quite bizarre in the context of errors for `zero(pi)` and `one(pi)`!

More broadly, I fail to see how the “common type” of two arguments is not the type of either one of them. Logical consistency would imply that `pi/2` is irrational, no?

I reiterate: if I have to import special functions, or linear algebra for that matter, because they aren’t of interest to many who want to use Julia, then I don’t see why this stuff is part of the base.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 6, 2019, 2:44pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/13 "2019-05-06T14:44:19Z")

</div>

> [@tobydriscoll](#):
>
> More broadly, I fail to see how the “common type” of two arguments is not the type of either one of them.

Remember that irrationals in Julia are singletons. Rather than “common type” I should have said “the simplest type to which both numbers can be promoted”.

> [@tobydriscoll](#):
>
> Logical consistency would imply that `pi/2` is irrational, no?

There have been quite some discussions on GitHub about this. It was decided in the end that any time you perform any operation on an Irrational it “collapses” onto a concrete `AbstractFloat`, either `Float64` or `BigFloat` or whatever. Even though it could be interesting, It’d be very tricky to have an arithmetic for irrationals so that any elementary operations with irrationals gives another irrational, and so.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 6, 2019, 2:47pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/14 "2019-05-06T14:47:27Z")

</div>

> [@tobydriscoll](#):
>
> I fail to see how the “common type” of two arguments is not the type of either one of them

There is no general reason that should hold. It depends on the operation, eg `/(::Int, ::Int)` gives a `Float64`. Even for straightforward conversion (`promote_type`), you can run into cases where the promoted type is neither argument.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 6, 2019, 2:53pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/15 "2019-05-06T14:53:34Z")

</div>

> [@Tamas\_Papp](#):
>
> It depends on the operation, eg `/(::Int, ::Int)` gives a `Float64`.

This is slightly different because in `1/1` the arguments are converted to an `AbstractFloat`, no promotion rule involved here. In the case of `complex(pi, 0)` the only operations performed is `promote`, but the promotion rule for `AbstractIrrational` and `Int` is to promote to `Float64`

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 6, 2019, 3:13pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/16 "2019-05-06T15:13:11Z")

</div>

My general point was that for `f(::T1, ::T2, ...)::S`, there is no rule that `S` has to be one of `T1`, `T2`, …, or an applicable parametric type with `S`, regardless of whether `f` is a constructor or a function.

```julia
julia> promote_type(AbstractIrrational, Int)
Float64

```

is actually a nice example of how this can fail.

Regarding the original question: one solution I did not see above is

```julia
julia> Complex(float(pi))
3.141592653589793 + 0.0im

```

which could actually be a fallback for the single-argument `Complex` constructor.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 6, 2019, 3:21pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/17 "2019-05-06T15:21:58Z")

</div>

> [@Tamas\_Papp](#):
>
> which could actually be a fallback for the single-argument `Complex` constructor.

which was rejected in [Define method for complex(x::Irrational) by giordano · Pull Request #22928 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/22928)

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [May 6, 2019, 3:30pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/18 "2019-05-06T15:30:28Z")

</div>

I am not sure we are talking about the same thing — the PR is about something else, using `float` was [mentioned in a comment](https://github.com/JuliaLang/julia/pull/22928#issuecomment-318442715) and was generally approved.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [May 6, 2019, 3:43pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/19 "2019-05-06T15:43:49Z")

</div>

The PR had initially the method you suggested.

Quoting verbatim from the comment you linked:

> [@](#):
>
> Given that it’s not clear what is intended, maybe the current `MethodError` is best. Users can write `complex(float(x))` if that is what they need.

He suggested to manually do `complex(float(pi))` and keep `complex(pi)` erroring

---

<div class="post-metadata">

**Author:** ![tobydriscoll](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tobydriscoll/32/1843_2.png) [@tobydriscoll](https://discourse.julialang.org/u/tobydriscoll)\
**Post date:** [May 6, 2019, 4:15pm UTC](https://discourse.julialang.org/t/complexifying-pi/23885/20 "2019-05-06T16:15:04Z")

</div>

I appreciate the great, thoughtful replies. The `/` example isn’t perfect; everyone understands rational/real numbers to generalize integers, while the relationship between `Irrational` and `Float64` is less intuitive. But the larger point is taken.

(Also, why `complex(1)`, `Complex(1)`, `float(1)`, but not `Float(1)`…never mind, I probably don’t want to know.)

I do still find a language that offers `pi` and `2pi` by default, yet regards them as fundamentally different types of numerical objects, counter to my expectations from decades of both mathematics and computing. I still don’t see the payoff that it provides, aside from modest convenience in showing off VPA. Unlike most of the choices in base Julia, this one feels oddly skewed toward a niche form of computing.

There is probably no use of an `Irrational` type that doesn’t end in either full symbolic math or bailing out to a float type. Now, I have no problem with reverting to float. I was caught off-guard when that _didn’t_ happen. And there are a bunch of ways to avoid/force it, I know.

I also get that people have thought about these things before, and decisions are made to optimize different goals. Consider this commentary more than criticism. I was relating the experience of one aspiring package author who was trying to follow the use of parameterized types, terminology like `Real`, and formal-sounding functions like `one` and `zero`, but still ended up getting burned in the case of the single input `pi`. The community can decide for itself whether it finds that concerning, or just the result of an old dog slow to learn new tricks.

[Next page](https://discourse.julialang.org/t/complexifying-pi/23885.md?page=2)
