mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	 911a70381a
			
		
	
	
		911a70381a
		
	
	
	
	
		
			
			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()
 |