Skip to content

Conversation

@ravibitsgoa
Copy link
Contributor

No description provided.

ravibitsgoa and others added 26 commits April 6, 2020 23:50
Co-Authored-By: Stefan Karpinski <stefan@karpinski.org>
Co-Authored-By: Stefan Karpinski <stefan@karpinski.org>
Co-Authored-By: Stefan Karpinski <stefan@karpinski.org>
When the input type is a dispatch tuple, we know that a subtype lookup
is sufficient, so we can do a slightly more direct lookup and avoid
considering `Union{}`-declared methods as possible results.

This also fixes an issue with the special `jl_matching_methods` query
being done by `jl_insert_backedges`: we would set the `done` flag when a
method "fully covered" the result, even if that method was deleted.
Rather than continue to abuse the meaning of `world == 0` (which also
has performance implications), we add a specific flag for this case.
…mate (JuliaLang#34859)

equality since BLAS and Julia might use square roots that round differently.
This has occasionally caused CI failures.
This allows julia code to more easily consume the output of jl-parse-all
(see JuliaLang#34715) and is consistent with the way line number nodes have file
name information in all non-toplevel constructs.
This changes unnamed type variables to print as valid syntax, via the
`var""` printing macro.

For example, before this commit, `Vector{<:Bool}` printed as a
non-syntactic string:
```julia
julia> Vector{<:Bool}
Array{#s16,1} where #s16<:Bool
```

And now it prints as a valid, syntactic expression:
```julia
julia> Vector{<:Bool}
Array{var"#s46",1} where var"#s46"<:Bool
```
We have deleteat!(vec::Vector, itr), but it returns the modified vector.
If you instead want to obtain the delted elements, there is
splice!(::Vector, ::UnitRange) as a special case of the three-argument
version that inserts additional elements where the old ones were
deleted. However, these is no two argument splice! for arbitrary
iterables, so probably the best way to write that currently is:

```
inds = collect(itr)
vals = A[inds]
deleteat!(A, inds)
vals
```

which is both less efficient and more verbose than ideal. I'm wondering
if perhaps deleteat! should have been returning the deleted elements,
but that's not a change we can make in 1.x. Instead, I'm proposing
here to extend the 2 argument (but not the 3 argument for obvious reasons)
variant of splice! to arbitrary iterables.
* More tests for Triangular, div and mult

* Long form function defs

* avoid useless unwrapping-wrapping in triangular multiplication

Co-authored-by: Daniel Karrasch <Daniel.Karrasch@gmx.de>
When we added the check to prevent inlining through ambiguous methods,
we failed exclude this check for calls to `invoke` (which skip ambiguous
methods, since the method is explicitly selected). Fixes JuliaLang#34900.
Here we implement a syntax for ccall with Julia-like type annotations on the arguments.

Compared to ccall:
* The new syntax is more familiar to Julia programmers
* The new syntax gives a notation for calling C varargs functions
* Support for specifying calling convention is not yet implemented as that will require another syntax discussion
* The semantics for interpolating "function like things" is much simplified (only function pointers are allowed)
* Tranpose for non-numeric eltype bidiag tests

* More tests for bidiag mul
This fixes the case in JuliaLang#34482 and adds some more robustness for similar
cases, though I wouldn't be surprised if there were more dragons hiding
here. Intrinsics are a bit special and from the start, we sort of expected
them to only ever be called correctly under pain of segfaults or other
undefined behavior. We've been gradually making these more robust,
but fundamentally, they were never intended to be used by users directly,
only through the type-validating wrappers in Base. What has changed
to cause the recent influx of issues in this area is that people now
like to do compiler transforms that happily recurse through these wrappers
and perform transforms that are not always legal on Intrinsics. This should
help catch a number of them, but this interface is still not very thoroughly
validated, and I would be surprised to see crashes or other errors stemming
from incorrect usage here.
Co-Authored-By: Stefan Karpinski <stefan@karpinski.org>
@StefanKarpinski
Copy link
Member

Would be good to get someone with complex number expertise to review.

Co-Authored-By: Stefan Karpinski <stefan@karpinski.org>
Copy link
Member

@simeonschaub simeonschaub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good, but see my comments about checking for overflow. I think the div and rem should also have their own docstring explaining how they work.

base/complex.jl Outdated
a, b = Complex{R}(a), Complex{R}(b)
= conj(b)
# TODO: Handle overflow when calculating a*b̅
t = a*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since div can't overflow for real numbers, I think, we should try to guarantee that div doesn't overflow for complex numbers either. I think this safety is worth the extra performance cost in most uses of this function. Implementing Base.checked_mul(::Complex{<:Integer}, ::Complex{<:Integer}) should actually be fairly straightforward to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, but an issue with the current division algorithm (a/b = a*b̅/abs2(b)), is that due to abs2 overflow, we cannot ensure that a/b never overflows. We can indeed try to catch and handle all the possible overflows, but that will be it.

We can always find some inputs which have b::Complex{T} and abs2(b)>typemax(T), which will make div(a, b) overflow.

E.g. a = Complex{Int32}(1, 1), b= Complex{Int32}(10^5, 10^5). Here, a/b will overflow due to the current algorithm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, what you mean. I mean, we check in abs2 and in *, that nothing overflows and then you're just calling div on real integers, which can't overflow, so we should catch all cases of overflow. I'm wondering though, if there aren't any better algorithms for division of complex integers, because checking in each step will prevent overflow in the result, but we will also throw errors for cases where either a*b' or abs2(b) overflows, but a/b should actually still be representable as the complex integer type of a and b.

Copy link
Contributor Author

@ravibitsgoa ravibitsgoa Apr 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we will also throw errors for cases where either a*b' or abs2(b) overflows

This is what I meant. It is possible that abs2(b) overflows, but a/b is still representable as a complex integer. E.g. div(Complex{Int32}(1, 1), Complex{Int32}(10^5, 10^5)).

Also, should checked_mul(::Complex, ::Complex) be in complex.jl or in checked.jl ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As soon as one of abs(b) or a*b' overflows, isn't the end result also wrong with your current algorithm, even if the result might still be representable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ravibitsgoa Could you explain your usecase for this? That would probably help to understand some of the constraints

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#35125
This PR is based on this issue. After quite a long discussion there, we decided we should avoid using floating points or rationals or wider type internally. Although those three are quite decent solutions IMO, but they will create a problem when we want to find exact integer answers of div, rem and gcd.

Copy link
Contributor

@KlausC KlausC Apr 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really cannot imagine those overflow-checked stuff (if you get it right) will ever be more efficient than just calculating with widened precision.
At least I would like to see a performance comparison.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can someone help me find good test cases for performance testing for these methods?
Since I am new to Julia and do not have much experience with performance testing. I am unable to construct tests for which gcd would take more iterations.
I tried running a few tests for the gcd method, which take like 0.000001 seconds to complete.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Euclidean GCD algorithm, the worst case occurs when arguments are consecutive Fibonacci numbers.

Using this, I tested gcd(f1 + 0im, f2+ 0im), for f1 = 1836311903, f2= 2971215073, since these are the largest Int64 arguments for which gcd does not overflow.

Benchmarking with @time gives 0.000009 seconds (3 allocations: 96 bytes) for the current overflow-checked implementation.

I compared it against another implementation, which calculates gcd by widening arguments into Complex{BigInt} first. For such a complex gcd method, @time gives around 0.000150 seconds (2.23 k allocations: 46.305 KiB).

Hence, we can safely conclude that for arguments below up to f1, f2, calculating gcd without widening into Complex{BigInt} might be a better decision.

If someone finds better benchmarks, I am happy to try them as well.

@KlausC
Copy link
Contributor

KlausC commented May 7, 2020

Please fix this ambiguity error:

ambiguous: Test Failed at D:\buildbot\worker-tabularasa\tester_win64\build\share\julia\test\ambiguous.jl:169
  Expression: detect_ambiguities(Core, Base; imported = true, recursive = true, ambiguous_bottom = false) == []
  Evaluated: Tuple{Method,Method}[(rem(a::Complex{T}, b::Complex{T}, rr::RoundingMode) where T<:Integer in Base at complex.jl:1125, rem(x, y, ::RoundingMode{:ToZero}) in Base at div.jl:67), (rem(a::Complex{T}, b::Complex{T}, rr::RoundingMode) where T<:Integer in Base at complex.jl:1125, rem(x, y, ::RoundingMode{:Up}) in Base at div.jl:69), (rem(a::Complex{T}, b::Complex{V}, rr::RoundingMode) where {T<:Integer, V<:Integer} in Base at complex.jl:1133, rem(x, y, ::RoundingMode{:ToZero}) in Base at div.jl:67), (rem(a::Complex{T}, b::Complex{V}, rr::RoundingMode) where {T<:Integer, V<:Integer} in Base at complex.jl:1133, rem(x, y, ::RoundingMode{:Up}) in Base at div.jl:69), (rem(a::Complex{T}, b::Complex{V}, rr::RoundingMode) where {T<:Integer, V<:Integer} in Base at complex.jl:1133, rem(x, y, r::RoundingMode{:Nearest}) in Base at div.jl:70), (rem(a::Complex{T}, b::Complex{T}, rr::RoundingMode) where T<:Integer in Base at complex.jl:1125, rem(x, y, ::RoundingMode{:Down}) in Base at div.jl:68), (rem(a::Complex{T}, b::Complex{V}, rr::RoundingMode) where {T<:Integer, V<:Integer} in Base at complex.jl:1133, rem(x, y, ::RoundingMode{:Down}) in Base at div.jl:68)] == Any[]

@ravibitsgoa

This comment has been minimized.

@ravibitsgoa
Copy link
Contributor Author

Hi everyone,
Can someone review this PR again?

@StefanKarpinski
Copy link
Member

@simonbyrne, @stevengj, any chance either of you could review this and merge if it looks good?

@ravibitsgoa
Copy link
Contributor Author

Hi everyone,
Can someone review and merge this PR?

resulting in the return of a negative value of imaginary part. This overflow
occurs only when `conj` is applied to the minimum representable value of a
signed integer. That is, when `x == typemin(typeof(x))`,
`imag(conj(x * im)) == x < 0`, not `-x` as might be expected.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warning seems unnecessary. We don't have similar warnings for - or other basic arithmetic operators.

1 - 3im
julia> conj(1 + typemin(Int64)im)
1 - 9223372036854775808im
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same … this doesn't seem the place to talk about integer overflow

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added it because the expected behaviour would be 1 - typemin(Int64) im.

@ViralBShah ViralBShah added the maths Mathematical functions label Feb 25, 2022
@adienes adienes added the complex Complex numbers label Jun 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

complex Complex numbers maths Mathematical functions

Projects

None yet

Development

Successfully merging this pull request may close these issues.