Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ispositive, etc. #53677

Open
wants to merge 65 commits into
base: master
Choose a base branch
from
Open

Add ispositive, etc. #53677

wants to merge 65 commits into from

Conversation

putianyi889
Copy link
Contributor

@putianyi889 putianyi889 commented Mar 9, 2024

It's faster for some complicated Real types.

julia> f(x) = x<=zero(typeof(x))
f (generic function with 1 method)

julia> g(x) = signbit(x)||iszero(x)
g (generic function with 1 method)

julia> @btime f(x) setup=(x=rand(BigFloat))
  99.055 ns (4 allocations: 144 bytes)
true

julia> @btime g(x) setup=(x=rand(BigFloat))
  10.700 ns (0 allocations: 0 bytes)
false

If you think this is a good idea then I'll add docstrings and tests.

Edit: FAQs according to discussions below

TODO:

@goerz
Copy link
Contributor

goerz commented Mar 9, 2024

For additional context: JuliaRegistries/General#102561

@DilumAluthge DilumAluthge requested a review from simonbyrne March 9, 2024 16:34
base/number.jl Outdated Show resolved Hide resolved
@mikmoore
Copy link
Contributor

mikmoore commented Mar 11, 2024

I'll just remark on ispositive, but the same goes for isnegative.

Needs NaN handling. For example,

julia> ispositive(NaN)  ispositive(-NaN)
true

A possible fix is in the flavor of ispositive(x) = !isnan(x) && !signbit(x) && !iszero(x). The default for non-float Number types is isnan(x) = false so this should be compiled away when irrelevant.

Also needs proper Missing behavior, ispositive(::Missing) = missing.

Now for your benchmarks (which appear to implement a "isnonpositive" function that does not appear in this PR, but I'll roll with it for now):

julia> using BenchmarkTools

julia> f(x) = x<=zero(typeof(x));

julia> g(x) = signbit(x)||iszero(x);

julia> g_fixed(x) = !isnan(x)&&(signbit(x)||iszero(x));

julia> h(x) = x <= false;

julia> h_int(x) = x <= 0;

julia> vi32 = rand(Int32,1000); vf64 = randn(Float64,1000); vbf = randn(BigFloat,1000);

julia> @btime count(f,$vi32); @btime count(g,$vi32); @btime count(g_fixed,$vi32); @btime count(h,$vi32); @btime count(h_int,$vi32)
  84.528 ns (0 allocations: 0 bytes)
  84.112 ns (0 allocations: 0 bytes)
  84.495 ns (0 allocations: 0 bytes)
  84.079 ns (0 allocations: 0 bytes)
  82.622 ns (0 allocations: 0 bytes)
517

julia> @btime count(f,$vf64); @btime count(g,$vf64); @btime count(g_fixed,$vf64); @btime count(h,$vf64); @btime count(h_int,$vf64)
  61.914 ns (0 allocations: 0 bytes)
  90.900 ns (0 allocations: 0 bytes)
  134.514 ns (0 allocations: 0 bytes)
  59.470 ns (0 allocations: 0 bytes)
  58.163 ns (0 allocations: 0 bytes)
489

julia> @btime count(f,$vbf);  @btime count(g,$vbf);  @btime count(g_fixed,$vbf);  @btime count(h,$vbf);  @btime count(h_int,$vbf)
  25.100 μs (2000 allocations: 101.56 KiB)
  12.900 μs (0 allocations: 0 bytes)
  14.000 μs (0 allocations: 0 bytes)
  54.900 μs (2000 allocations: 39.06 KiB)
  14.200 μs (0 allocations: 0 bytes)
498

Don't get hung up on differences of a few ns, which are probably benchmarking artifacts. But notice that this function is notably slower for native floats (and much slower when correctly handling NaN). And it doesn't out-do simple comparisons for BigFloat (that avoid creating a new BigFloat).

My preferred implementation would be h, but that implementation is so simple that I wouldn't introduce a function for it. But my benchmarks do show that we're missing good inequality handling for _comparison_(BigFloat, Bool), in that there's no reason h_int should be better than h in that case.

@giordano giordano added needs tests Unit tests are required for this change needs docs Documentation for this change is required labels Mar 11, 2024
@putianyi889
Copy link
Contributor Author

putianyi889 commented Mar 11, 2024

I'll just remark on ispositive, but the same goes for isnegative.

Thanks for the investigation! Looks like the problem is more complex than what I've imagined. Are you on the dev version of Julia? My benchmark results are a little different. count(h_int,$vbf) also has 2000 allocations. I'm on v1.10.2.

Anyway, the methods need different implementations for different types. I'm not sure which the fallback should be.

@putianyi889 putianyi889 requested a review from giordano March 17, 2024 19:12
test/numbers.jl Outdated Show resolved Hide resolved
test/numbers.jl Show resolved Hide resolved
test/numbers.jl Outdated Show resolved Hide resolved
test/numbers.jl Outdated Show resolved Hide resolved
test/numbers.jl Outdated Show resolved Hide resolved
@simonbyrne
Copy link
Contributor

One thought: rather than defining these, would it be better to define a function which returns a Real or Number, say value?

@putianyi889
Copy link
Contributor Author

One thought: rather than defining these, would it be better to define a function which returns a Real or Number, say value?

How does that help?

@barucden
Copy link
Contributor

Don't forget to add a compatibility note for the added functions (they are likely to be a part of Julia 1.12.0).

test/numbers.jl Outdated Show resolved Hide resolved
@mikmoore
Copy link
Contributor

Would it make more sense for x > 0 (etc) to be the default implementation and the !isnan & !iszero & !signbit to be the specialization for BigFloat and maybe ::BigInt (basically, to swap the IEEEFloat specialization and the ::Any fallback?) As far as I can tell, BigFloat (and maybe ::BigInt) is the only Base type that will be handled better by this function.

I still think that it might be worth exploring faster inequality checks for BigFloat mixed with other types (at least Bool should be easy). It's a little more complicated to implement, but it could make these new functions unnecessary. I don't see these new functions being used especially often. They add a faster alternative to a specific BigFloat operation, but otherwise are equivalent to x -> x>0 etc. BigFloat is already very slow so I don't see this making a big performance difference in real code.

base/number.jl Outdated Show resolved Hide resolved
Co-authored-by: Mosè Giordano <giordano@users.noreply.github.com>
base/floatfuncs.jl Outdated Show resolved Hide resolved
@putianyi889
Copy link
Contributor Author

As far as I can tell, BigFloat (and maybe ::BigInt) is the only Base type that will be handled better by this function.

I can think of AbstractIrrational (although signbit has not yet been specialized) and Unsigned as well, so basically IEEEFloat is the only group where isnegative(x)=x<0 is strictly better (and SignedBitInteger is on par) and it's because of hardware support.

@giordano giordano added maths Mathematical functions needs docs Documentation for this change is required and removed needs tests Unit tests are required for this change needs docs Documentation for this change is required labels Nov 27, 2024
Copy link
Contributor

@giordano giordano left a comment

Choose a reason for hiding this comment

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

I think the docstrings need to be added to the manual, no?

base/int.jl Outdated Show resolved Hide resolved
test/numbers.jl Show resolved Hide resolved
test/numbers.jl Outdated Show resolved Hide resolved
test/numbers.jl Show resolved Hide resolved
@putianyi889
Copy link
Contributor Author

I think the docstrings need to be added to the manual, no?

Should they be with isless (base.md), isfinite (numbers.md) or signbit (math.md)?

@giordano
Copy link
Contributor

Should they be with isless (base.md), isfinite (numbers.md) or signbit (math.md)?

Having to choose, I'd probably pick isless, seems to be the somewhat closer in spirit.

@putianyi889 putianyi889 requested a review from giordano November 28, 2024 08:09
@giordano giordano removed the needs docs Documentation for this change is required label Nov 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
maths Mathematical functions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants