mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-09 05:38:32 +00:00

Distribute everything from `libm/` to better locations in the repo. `libm/libm/*` has not moved yet to avoid Git seeing the move as an edit to `Cargo.toml`. Files that remain to be merged somehow are in `etc/libm`.
172 lines
4.1 KiB
Julia
172 lines
4.1 KiB
Julia
"A quick script for plotting a list of floats.
|
|
|
|
Takes a path to a TOML file (Julia has builtin TOML support but not JSON) which
|
|
specifies a list of source files to plot. Plots are done with both a linear and
|
|
a log scale.
|
|
|
|
Requires [Makie] (specifically CairoMakie) for plotting.
|
|
|
|
[Makie]: https://docs.makie.org/stable/
|
|
"
|
|
|
|
using CairoMakie
|
|
using TOML
|
|
|
|
function main()::Nothing
|
|
CairoMakie.activate!(px_per_unit = 10)
|
|
config_path = ARGS[1]
|
|
|
|
cfg = Dict()
|
|
open(config_path, "r") do f
|
|
cfg = TOML.parse(f)
|
|
end
|
|
|
|
out_dir = cfg["out_dir"]
|
|
for input in cfg["input"]
|
|
fn_name = input["function"]
|
|
gen_name = input["generator"]
|
|
input_file = input["input_file"]
|
|
|
|
plot_one(input_file, out_dir, fn_name, gen_name)
|
|
end
|
|
end
|
|
|
|
"Read inputs from a file, create both linear and log plots for one function"
|
|
function plot_one(
|
|
input_file::String,
|
|
out_dir::String,
|
|
fn_name::String,
|
|
gen_name::String,
|
|
)::Nothing
|
|
fig = Figure()
|
|
|
|
lin_out_file = joinpath(out_dir, "plot-$fn_name-$gen_name.png")
|
|
log_out_file = joinpath(out_dir, "plot-$fn_name-$gen_name-log.png")
|
|
|
|
# Map string function names to callable functions
|
|
if fn_name == "cos"
|
|
orig_func = cos
|
|
xlims = (-6.0, 6.0)
|
|
xlims_log = (-pi * 10, pi * 10)
|
|
elseif fn_name == "cbrt"
|
|
orig_func = cbrt
|
|
xlims = (-2.0, 2.0)
|
|
xlims_log = (-1000.0, 1000.0)
|
|
elseif fn_name == "sqrt"
|
|
orig_func = sqrt
|
|
xlims = (-1.1, 6.0)
|
|
xlims_log = (-1.1, 5000.0)
|
|
else
|
|
println("unrecognized function name `$fn_name`; update plot_file.jl")
|
|
exit(1)
|
|
end
|
|
|
|
# Edge cases don't do much beyond +/-1, except for infinity.
|
|
if gen_name == "edge_cases"
|
|
xlims = (-1.1, 1.1)
|
|
xlims_log = (-1.1, 1.1)
|
|
end
|
|
|
|
# Turn domain errors into NaN
|
|
func(x) = map_or(x, orig_func, NaN)
|
|
|
|
# Parse a series of X values produced by the generator
|
|
inputs = readlines(input_file)
|
|
gen_x = map((v) -> parse(Float32, v), inputs)
|
|
|
|
do_plot(
|
|
fig,
|
|
gen_x,
|
|
func,
|
|
xlims[1],
|
|
xlims[2],
|
|
"$fn_name $gen_name (linear scale)",
|
|
lin_out_file,
|
|
false,
|
|
)
|
|
|
|
do_plot(
|
|
fig,
|
|
gen_x,
|
|
func,
|
|
xlims_log[1],
|
|
xlims_log[2],
|
|
"$fn_name $gen_name (log scale)",
|
|
log_out_file,
|
|
true,
|
|
)
|
|
end
|
|
|
|
"Create a single plot"
|
|
function do_plot(
|
|
fig::Figure,
|
|
gen_x::Vector{F},
|
|
func::Function,
|
|
xmin::AbstractFloat,
|
|
xmax::AbstractFloat,
|
|
title::String,
|
|
out_file::String,
|
|
logscale::Bool,
|
|
)::Nothing where {F<:AbstractFloat}
|
|
println("plotting $title")
|
|
|
|
# `gen_x` is the values the generator produces. `actual_x` is for plotting a
|
|
# continuous function.
|
|
input_min = xmin - 1.0
|
|
input_max = xmax + 1.0
|
|
gen_x = filter((v) -> v >= input_min && v <= input_max, gen_x)
|
|
markersize = length(gen_x) < 10_000 ? 6.0 : 4.0
|
|
|
|
steps = 10_000
|
|
if logscale
|
|
r = LinRange(symlog10(input_min), symlog10(input_max), steps)
|
|
actual_x = sympow10.(r)
|
|
xscale = Makie.pseudolog10
|
|
else
|
|
actual_x = LinRange(input_min, input_max, steps)
|
|
xscale = identity
|
|
end
|
|
|
|
gen_y = @. func(gen_x)
|
|
actual_y = @. func(actual_x)
|
|
|
|
ax = Axis(fig[1, 1], xscale = xscale, title = title)
|
|
|
|
lines!(
|
|
ax,
|
|
actual_x,
|
|
actual_y,
|
|
color = (:lightblue, 0.6),
|
|
linewidth = 6.0,
|
|
label = "true function",
|
|
)
|
|
scatter!(
|
|
ax,
|
|
gen_x,
|
|
gen_y,
|
|
color = (:darkblue, 0.9),
|
|
markersize = markersize,
|
|
label = "checked inputs",
|
|
)
|
|
axislegend(ax, position = :rb, framevisible = false)
|
|
|
|
save(out_file, fig)
|
|
delete!(ax)
|
|
end
|
|
|
|
"Apply a function, returning the default if there is a domain error"
|
|
function map_or(input::AbstractFloat, f::Function, default::Any)::Union{AbstractFloat,Any}
|
|
try
|
|
return f(input)
|
|
catch
|
|
return default
|
|
end
|
|
end
|
|
|
|
# Operations for logarithms that are symmetric about 0
|
|
C = 10
|
|
symlog10(x::Number) = sign(x) * (log10(1 + abs(x) / (10^C)))
|
|
sympow10(x::Number) = (10^C) * (10^x - 1)
|
|
|
|
main()
|