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

1.0-dev: improve printing and fix alignment issue #555

Merged
merged 13 commits into from
Feb 15, 2023

Conversation

OlivierHnt
Copy link
Member

@OlivierHnt OlivierHnt commented Jan 23, 2023

Over the week-end I experimented with the printing mechanism of the 1.0-dev branch. This is the resulting PR 🙂.

Fix #316, #434.

It seems that the alignment issue when printing arrays is linked to the fact that Interval <: Real and sometimes the bounds of the intervals are printed with no decimal points. The current behaviour (before this PR) relies on the format "%.g" which gives the shortest form for a given number (e.g. 1.0 would be returned as 1).

So this would explain also why IntervalBox had no printing issues: it is not a subtype of Real.

In this PR I tried to stay as close as possible to the current behaviour, so the only visual change is that exact integers are displayed with a trailing decimal point and 0, for instance 1 is printed as 1.0.
I have modified the tests to comply with this change.

Before this PR:

julia> M = [1.2..1.6 1.2..1.6; -1..1 -1..1]  # format === :standard
2×2 Matrix{Interval{Float64}}:
      [1.19999, 1.60001]       [1.19999, 1.60001]
 [-1, 1]                  [-1, 1]

julia> M = [1.2..1.6 1.2..1.6; -1..1 -1..1]  # format === :midpoint
2×2 Matrix{Interval{Float64}}:
     1.4 ± 0.200001      1.4 ± 0.200001
 0 ± 1               0 ± 1

After this PR:

julia> M = [1.2..1.6 1.2..1.6; -1..1 -1..1]  # format === :standard
2×2 Matrix{Interval{Float64}}:
  [1.19999, 1.60001]   [1.19999, 1.60001] # expected shift to the right to account for potential minus signs `-`
 [-1.0, 1.0]          [-1.0, 1.0]

julia> M = [1.2..1.6 1.2..1.6; -1..1 -1..1]  # format === :midpoint
2×2 Matrix{Interval{Float64}}:
 1.4 ± 0.200001  1.4 ± 0.200001
 0.0 ± 1.0       0.0 ± 1.0

Other changes

  • There was only one test which did not pass but I think it is wrong.
    Before this PR:
string(2.1..2.2) == [2.1, 2.20001]   # seems wrong, no?

After this PR:

string(2.1..2.2) == [2.09999, 2.20001]
  • I uncommented some tests for Complex{<:Interval}, they pass successfully. In the file changelog.md, it is mentioned that support for Complex{<:Interval} is dropped 🙁. However, there are still existing functionalities for Complex{<:Interval} on this branch and the display tests pass without extra work.

  • Rename the keyword argument sigfigs of setformat to sigdigits to match the naming convention of Julia (e.g. see the keywords of round).

  • Fix all the inconsistencies where == was used instead of === (or != instead of !==). In particular, use === to compare symbols.

  • Improve performance of printing. For instance, faster supscriptify and subscriptify and representation is now type-stable.

For instance, before this PR:

julia> @btime IntervalArithmetic.subscriptify($8);
  135.643 ns (6 allocations: 352 bytes)

julia> @btime IntervalArithmetic.subscriptify($111);
  202.847 ns (7 allocations: 464 bytes)

julia> @btime IntervalArithmetic.supscriptify($8);
  148.988 ns (7 allocations: 448 bytes)

julia> @btime IntervalArithmetic.supscriptify($111);
  178.257 ns (7 allocations: 496 bytes)

After this PR:

julia> @btime IntervalArithmetic.supscriptify($8);  # Same timing for subscriptify
  3.666 ns (0 allocations: 0 bytes)

julia> @btime IntervalArithmetic.supscriptify($111);  # Same timing for subscriptify
  107.967 ns (4 allocations: 240 bytes)

@OlivierHnt OlivierHnt changed the title Improve printing and fix alignment issue 1.0-dev: improve printing and fix alignment issue Jan 23, 2023
@codecov-commenter
Copy link

codecov-commenter commented Jan 29, 2023

Codecov Report

Base: 85.22% // Head: 85.13% // Decreases project coverage by -0.09% ⚠️

Coverage data is based on head (3944712) compared to base (1d2e6e4).
Patch coverage: 79.25% of modified lines in pull request are covered.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

Additional details and impacted files
@@             Coverage Diff             @@
##           1.0-dev     #555      +/-   ##
===========================================
- Coverage    85.22%   85.13%   -0.09%     
===========================================
  Files           34       34              
  Lines         1793     1837      +44     
===========================================
+ Hits          1528     1564      +36     
- Misses         265      273       +8     
Impacted Files Coverage Δ
src/IntervalArithmetic.jl 60.00% <ø> (+10.00%) ⬆️
src/display.jl 80.00% <79.25%> (-2.92%) ⬇️
src/intervals/rounding.jl 60.00% <0.00%> (-7.15%) ⬇️
src/intervals/real_interface.jl 84.61% <0.00%> (-2.89%) ⬇️
src/intervals/construction.jl 96.42% <0.00%> (-0.13%) ⬇️
src/intervals/arithmetic/basic.jl 91.80% <0.00%> (+0.27%) ⬆️
src/intervals/rounding_macros.jl 92.30% <0.00%> (+92.30%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Collaborator

@Kolaru Kolaru left a comment

Choose a reason for hiding this comment

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

Very nice job overall, I'm very thankful you decided to delve into this!

Also thanks very much for the informative comments and docstrings.

src/display.jl Outdated Show resolved Hide resolved
src/display.jl Show resolved Hide resolved
src/display.jl Show resolved Hide resolved
src/display.jl Outdated Show resolved Hide resolved
test/display_tests/display.jl Show resolved Hide resolved
lucaferranti
lucaferranti previously approved these changes Jan 29, 2023
src/display.jl Outdated Show resolved Hide resolved
@lucaferranti lucaferranti dismissed their stale review January 29, 2023 19:28

I had approved by accident, sorry

@OlivierHnt
Copy link
Member Author

@Kolaru @lucaferranti
Thank you for the feedback on this PR! 🙂

I have made the requested changes.

The commit cbbd9ed (which introduces tests for large exponents) revealed that the printing of large exponent changes depending on the OS, e.g. Windows. Since I work on macOS, I did not see this issue.
Does anyone know what could be the cause? An issue with how BigFloat is handled?

Moreover, the two last commits are worth discussing. The commit f66abd1 defines show on ::MIME"text/plain" as suggested by @Kolaru. The default show with no MIME checks for :compact in which case it will behave as ::MIME"text/plain" (in particular, this ensures that we have nice printing for matrices).
Doing this makes show(::Interval) behave exactly like showfull. So the commit d2418e3 removes showfull.
N.B.: now string(::Interval) returns a string which looks like the output of show(::Interval), which feels like the right thing.

@Kolaru
Copy link
Collaborator

Kolaru commented Feb 6, 2023

The commit cbbd9ed (which introduces tests for large exponents) revealed that the printing of large exponent changes depending on the OS, e.g. Windows. Since I work on macOS, I did not see this issue.
Does anyone know what could be the cause? An issue with how BigFloat is handled?

Oh no. This is concerning, I will try to reproduce locally (I've access to linux and windows) and see if can understand where it comes from.

N.B.: now string(::Interval) returns a string which looks like the output of show(::Interval), which feels like the right thing.

I think it feels right as well, but we need to check if the IEEE standard as an opinion about representing intervals as string. Worst case we need an additional specific method to have the output prescribed by the standard.

@OlivierHnt
Copy link
Member Author

Oh no. This is concerning, I will try to reproduce locally (I've access to linux and windows) and see if can understand where it comes from.

Thanks for looking into this!

I think it feels right as well, but we need to check if the IEEE standard as an opinion about representing intervals as string. Worst case we need an additional specific method to have the output prescribed by the standard.

I just had a look at the standard and, as far as I can tell, they only specify "interval literals" for parsing purposing. So the meaning of the function string from Base does not seem to be conflicting (in fact, it is kind of better now because the output of string is now valid code to construct the exact interval).
Perhaps @lucaferranti could double-check this since he seems well versed with the standard? 🙂

@lucaferranti
Copy link
Member

sorry for the very slow follow-up. I am in a very busy couple of weeks, with several paper deadlines approaching.

I'll try to double check the discussion today

@lbenet lbenet added the 1.0 Planned for the major 1.0 release label Feb 12, 2023
@Kolaru
Copy link
Collaborator

Kolaru commented Feb 14, 2023

I checked and the test are passing on my CentOS machine. So I'm very confused.

I will run the tests with the PR on my windows machine tonight.

@Kolaru
Copy link
Collaborator

Kolaru commented Feb 14, 2023

This is a platform dependent behavior (bug ?) of BigFloat. I reported it there JuliaLang/julia#48678.

On Windows:

julia> big"-5.87565e+1388255822130839282"
-Inf

On Ubuntu (WSL):

julia> big"-5.87565e+1388255822130839282"
-5.875649999999999999999999999999999999999999999999999999999999999999999999999983e+1388255822130839282

For now I think it would be good enough to test with a slightly less crazy bound like 1e123456789.

@OlivierHnt
Copy link
Member Author

Thanks @Kolaru for looking into this! The CI now runs successfully with the smaller exponent that you suggested 🎉

src/display.jl Outdated Show resolved Hide resolved

setformat(:full)
@test string(a) == "DecoratedInterval(Interval(2.0, 3.0), com)"
@test sprint(show, MIME("text/plain"), a) == "DecoratedInterval(Interval(2.0, 3.0), com)"
Copy link
Member

Choose a reason for hiding this comment

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

This is a comment/suggestion, which I guess is small enough to be incorporated for intervals/decorated intervals with BigFloats: I like that the :standard format shows the precision of the BigFloat end-points; this is not included in the :full format, as shown by this example. I think displaying the precision is informative; while we usually fix it (and forget it), MPFR allows to work with different precisions, which is a case where it is worth a reminder.
So, what about including the precision for Interval{BigFloat} and DecoratedInterval{BigFloat} always?

Copy link
Member Author

@OlivierHnt OlivierHnt Feb 15, 2023

Choose a reason for hiding this comment

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

It is not a bad idea. Note that we need a signature for show which returns parseable Julia code (cf. docstring for show). In this PR this behaviour is tied with the formatting mechanism via setformat(:full).
I think the best option would be to add an other symbol, say :parseable, such that setformat(:parseable) replaces the current setformat(:full). Then, setformat(:full) can have the behaviour you suggest.

Also, do you have a display in mind? Something like @biginterval(1) is displayed as "Interval(1.0, 1.0)₂₅₆"?

Copy link
Member Author

@OlivierHnt OlivierHnt Feb 15, 2023

Choose a reason for hiding this comment

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

For consistency, what about doing this for :midpoint as well?

So it would look like

julia> setformat(:midpoint)
Display options:
  - format: midpoint
  - decorations: true
  - significant digits: 6

julia> big(1)..2
(1.5 ± 0.5)₂₅₆

julia> setformat(:full);  # full display of the bounds and show precision if BigFloat

julia> big(1)..2
Interval(1.0, 2.0)₂₅₆

julia> setformat(:parseable);  # parseable Julia code

julia> big(1)..2
Interval(1.0, 2.0)

EDIT: Mhmm looking at this a bit closer, I am not sure this is really the right call. It seems more logical to display Interval{BigFloat} for :full. Then the user would know that they are dealing with BigFloat (unlike for :standard) and checking the precision is as easy as precision(BigFloat).
This would keep the setformat mechanism simple (no need to add another symbol :parseable). Also, this is more consistent with the behaviour in Base.

Copy link
Member

Choose a reason for hiding this comment

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

Great that the comment was useful! 😄

I like all proposals:

  • the :raw and :full formats, where the raw format is for copy-paste (or saving results to be reused), and the full format is to have all details displayed;
  • I like the display you propose Interval(1.0, 1.0)₂₅₆;
  • include the precision in the :midpoint format.

Copy link
Member Author

Choose a reason for hiding this comment

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

But now I am not unsure about the :raw/:parseable options 🤔. Mainly because the way Julia gives information of the precision for BigFloat is through the function precision and not the display.
So showing Interval{BigFloat} is sufficient and consistent with Base. A user would know that they have an interval with bounds of type BigFloat which has precision precision(BigFloat).

But it is definitely useful to display the precision for :midpoint. So I will add a commit for this.

@OlivierHnt
Copy link
Member Author

OlivierHnt commented Feb 15, 2023

With the last two commits, the decorations are shown by default and the precision for :midpoint is shown if the bounds have type BigFloat.
Also, I have changed the display of decorated intervals with the format :midpoint. Before DecoratedInterval(1..2) was displayed 1.5 ± 0.5_com, now it is shown as (1.5 ± 0.5)_com.

Copy link
Collaborator

@Kolaru Kolaru left a comment

Choose a reason for hiding this comment

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

Everything looks good to me, thanks a lot!

@lbenet
Copy link
Member

lbenet commented Feb 15, 2023

LGTM too, so let's go ahead and merge this!

@lbenet lbenet merged commit 466606b into JuliaIntervals:1.0-dev Feb 15, 2023
@lbenet
Copy link
Member

lbenet commented Feb 15, 2023

Thanks a lot @OlivierHnt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.0 Planned for the major 1.0 release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants