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

Fix float issues for CairoMakie by using Float64 #2573

Closed
wants to merge 57 commits into from
Closed

Conversation

ffreyer
Copy link
Collaborator

@ffreyer ffreyer commented Jan 8, 2023

Description

This is basically a light version of #2550, aiming to fix float precision errors for CairoMakie but not gl backends. Basically I'm just changing a bunch of variables to Float64 so we don't lose that precision. I will probably go through this again later to remove Float32 -> Float64 conversions.

Some related issues are collected in #2848.

Working

  • scatter(1:10, 1e9 .+ (1:10)) in empty Scene with manual camera.projectionview
  • Axis
    • autolimits of axis
    • scatter
    • lines
    • linesegments
    • mesh
    • heatmap
    • image
    • meshscatter
    • text
    • poly
  • Axis3
    • mesh
    • surface
    • volume
    • meshscatter
  • revive GLMakie
  • revive WGLMakie
  • revive RPRMakie?

TODO:

  • fix issues with varying types (either pin the converted type, allow automatic converting (like Rect2f -> Rect2{Float64}) or make converted type adjustable) - switched to Float64 for now
  • switch to lazy converts (i.e. don't convert to Float64 by default. Keep the given (concrete?) type as long as possible)

test

Type of change

  • Bug fix? I don't think there is a breaking change in this.

Checklist

  • Added an entry in NEWS.md (for new features and breaking changes)
  • Added or changed relevant sections in the documentation
  • Added unit tests for new algorithms, conversion methods, etc.
    • Added unit test for "range step cannot be zero"
  • Added reference image tests for new plotting functions, recipes, visual options, etc.

@MakieBot
Copy link
Collaborator

MakieBot commented Jan 8, 2023

Compile Times benchmark

Note, that these numbers may fluctuate on the CI servers, so take them with a grain of salt. All benchmark results are based on the mean time and negative percent mean faster than the base branch. Note, that GLMakie + WGLMakie run on an emulated GPU, so the runtime benchmark is much slower. Results are from running:

using_time = @ctime using Backend
# Compile time
create_time = @ctime fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @ctime Makie.colorbuffer(display(fig))
# Runtime
create_time = @benchmark fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @benchmark Makie.colorbuffer(display(fig))
using create display create display
GLMakie 10.35s (10.26, 10.41) 0.05+- 1.13s (1.12, 1.15) 0.01+- 803.41ms (792.03, 838.10) 16.06+- 10.09ms (10.04, 10.18) 0.04+- 88.66ms (87.80, 89.14) 0.45+-
master 10.20s (10.17, 10.23) 0.02+- 1.06s (1.06, 1.07) 0.00+- 765.53ms (760.72, 769.86) 3.97+- 9.91ms (9.81, 10.02) 0.08+- 88.60ms (87.29, 94.16) 2.47+-
evaluation +1.47%, 0.15s slower X (4.09d, 0.00p, 0.03std) +6.01%, 0.07s slower❌ (9.82d, 0.00p, 0.01std) +4.71%, 37.88ms slower X (3.24d, 0.00p, 10.01std) +1.84%, 0.19ms slower X (2.95d, 0.00p, 0.06std) +0.06%, 0.06ms invariant (0.03d, 0.95p, 1.46std)
CairoMakie 9.10s (8.93, 9.23) 0.09+- 1.13s (1.09, 1.17) 0.02+- 267.48ms (262.65, 271.73) 3.26+- 10.02ms (9.85, 10.24) 0.14+- 5.18ms (5.04, 5.35) 0.11+-
master 9.00s (8.90, 9.06) 0.05+- 1.16s (1.14, 1.16) 0.01+- 240.09ms (237.78, 244.11) 2.46+- 9.93ms (9.86, 10.10) 0.10+- 5.66ms (5.55, 5.85) 0.10+-
evaluation +1.08%, 0.1s slower X (1.29d, 0.04p, 0.07std) -2.62%, -0.03s faster ✓ (-1.64d, 0.02p, 0.02std) +10.24%, 27.39ms slower❌ (9.49d, 0.00p, 2.86std) +0.83%, 0.08ms invariant (0.69d, 0.22p, 0.12std) -9.24%, -0.48ms faster✅ (-4.64d, 0.00p, 0.10std)
WGLMakie 11.79s (11.63, 12.11) 0.19+- 1.25s (1.20, 1.30) 0.04+- 11.95s (11.85, 12.16) 0.11+- 14.01ms (13.43, 15.95) 0.89+- 879.33ms (859.11, 905.37) 16.32+-
master 11.66s (11.38, 11.91) 0.20+- 1.31s (1.25, 1.37) 0.04+- 11.94s (11.53, 12.17) 0.21+- 13.73ms (12.66, 14.80) 0.76+- 872.60ms (833.08, 988.65) 52.48+-
evaluation +1.13%, 0.13s invariant (0.69d, 0.22p, 0.19std) -4.60%, -0.06s faster ✓ (-1.54d, 0.01p, 0.04std) +0.07%, 0.01s invariant (0.05d, 0.92p, 0.16std) +2.05%, 0.29ms invariant (0.35d, 0.53p, 0.82std) +0.77%, 6.73ms invariant (0.17d, 0.76p, 34.40std)

@jkrumbiegel
Copy link
Member

Very cool, this will enable easy use of DateTime as well, as that can be converted to Float64 losslessly if I'm not wrong

@ffreyer
Copy link
Collaborator Author

ffreyer commented Jan 9, 2023

Would it be better to have plot.converted to be more specific, i.e. just Point{2, Float64} or Point{3, Float64} for scatter, or allow eltypes from input arguments to carry through, i.e. also Point{3, Float32} to exist? The latter would reduce the number of conversions we do and reduce memory overhead but I guess it would hurt compilation/load times. I've also seen some type annotations here and there that would be invalid with option 2.

@jkrumbiegel
Copy link
Member

Hmm good question. In general, I think we've had issues because of converting things too eagerly. Everything is Float32 because GLMakie needs that eventually, but that has always robbed CairoMakie of using higher precision input data. Although the compilation latency is an argument. I think conversion should generally be the shortest path to a backend-consumable type.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Jan 10, 2023

This should fix all the Float32 precision errors in plotting for CairoMakie only. Related issues include: #931, #1579 (error fixed, plotting only in CairoMakie), #396 (is this related?), #1419 (not sure), #780, #1373, #1196

In general this should fix range step cannot be zero errors in Axis and problems with transforms caused by lack of precision. Fixes: #1670, #2298, #1040

# Else, we need to convert it!
return (GeometryBasics.mesh(mesh, pointtype=Point{N, Float32}, facetype=GLTriangleFace),)
end
return (GeometryBasics.mesh(mesh, pointtype=Point{N, Float64}, facetype=GLTriangleFace),)
Copy link
Member

Choose a reason for hiding this comment

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

Why did you remove this? I just recently added this to avoid unnecessary conversions, which has been slow, and also materialized any Buffer inside the mesh, breaking this feature: https://docs.makie.org/stable/examples/plotting_functions/mesh/index.html#using_geometrybasicsmesh_and_buffersampler_type

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I just wanted to merge master for now without creating more failures.

There are a ton of probably unnecessary conversion in this, compared to lazy conversions. We'd need a bunch more automatic conversions in GeometryBasics to switch to lazy ones here. I don't really know how to do this correctly, but something like

Base.convert(::Type{<: Rect{N, T}}, r::Rect{N}) where {N, T} = Rect{N, T}(r.origin, r.widths)

does the trick of Rect. Maybe that's also something you already worked on somewhere?

Comment on lines +371 to +427
macro trace(expr)
if expr.head in (Symbol("="), :function)
name = _to_typed_name(expr.args[1])
file = " in " * string(__source__.file) * ':'
line = string(__source__.line) * '\n'
code = quote
begin
printstyled($name, bold = true)
printstyled($file, color = :light_black)
printstyled($line, bold = true)
$(expr.args[2])
end
end
out = Expr(
expr.head, # function or =
esc(expr.args[1]), # function name w/ args
esc(code)
)
return out
end
return expr
end

function _to_typed_name(e::Expr)
if e.head == :where
_to_typed_name(e.args[1])
elseif e.head == :call
if length(e.args) > 1 && e.args[2] isa Expr && e.args[2].head == :parameters
args = join(_to_type.(e.args[3:end]), ", ")
kwargs = join(_to_type.(e.args[2].args), ", ")
return string(e.args[1]) * "(" * args * "; " * kwargs * ")"
else
args = join(_to_type.(e.args[2:end]), ", ")
return string(e.args[1]) * "(" * args * ")"
end
else
dump(e)
return "ERROR"
end
end

_to_type(s::Symbol) = "::Any"
function _to_type(e::Expr)
if e.head == Symbol("::")
return "::" * string(e.args[end])
elseif e.head == Symbol("...")
return _to_type(e.args[1]) * "..."
elseif e.head == :parameters # keyword args
return _to_type(e.args[1])
elseif e.head == :kw # default value
return _to_type(e.args[1])
else
dump(e)
error("Failed to parse expression.")
return ""
end
end
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I put this together to have an easier time figuring out function call chains. You can put @trace before a function (full or inline) and it'll inject some printing which will print the function header with types, the file and the line number. Should we keep this as a debug tool?

@ffreyer
Copy link
Collaborator Author

ffreyer commented May 1, 2023

I changed things to allow Float32 and Float64 to pass through. Also did some cleanup (write out return, changed some Number -> Real types, removed an effectively duplicate method).

JuliaGeometry/GeometryBasics.jl#195 was necessary for the pr at some point, not sure if it still is. It would help avoid conversion errors either way though.

@j-fu
Copy link
Contributor

j-fu commented Nov 7, 2023

Will this also allow to plot data with ranges exceeding the range of Float32 ?

@gvdeynde
Copy link

@ffreyer do you have any idea whether this is still "under active development"? This is really a breaking thing for me (I work a lot in loglog scales with data ranges that fall outside Float32 range...). Not pushing you or the Makie community but I would like to make a decision whether to wait for the coolest visualisation tool to get this feature or to go on with Plots.jl... I wish I could help, but I don't think my Julia nor Makie skills are up to par for this...

@ffreyer
Copy link
Collaborator Author

ffreyer commented Jan 29, 2024

This pr isn't active anymore but we are planning to work on the problem. Most likely in new prs. #3550 and #3565 are aimed towards this, #3226 might become the new main pr

@ffreyer ffreyer deleted the ff/Float64 branch March 28, 2024 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants