From 40852e8af0cb751e041e5896b6887f5c088ce2a4 Mon Sep 17 00:00:00 2001 From: d-monnet Date: Fri, 27 Oct 2023 11:33:26 -0400 Subject: [PATCH 01/15] add data matrix export function --- src/performance_profiles.jl | 50 ++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/performance_profiles.jl b/src/performance_profiles.jl index 6c8852b..675e8a7 100644 --- a/src/performance_profiles.jl +++ b/src/performance_profiles.jl @@ -6,6 +6,8 @@ using CSV, Tables +using CSV, Tables + """Compute performance ratios used to produce a performance profile. There is normally no need to call this function directly. @@ -155,6 +157,23 @@ function performance_profile( ) end +""" + function performance_profile_data_mat(T;kwargs...) + +Retruns `performance_profile_data` output (vectors) as matrices. Matrices are padded with NaN if necessary. +""" +function performance_profile_data_mat(T::Matrix{Float64};kwargs...) + x_data, y_data, max_ratio = performance_profile_data(T;kwargs...) + max_elem = maximum(length.(x_data)) + for i in eachindex(x_data) + append!(x_data[i],[NaN for i=1:max_elem-length(x_data[i])]) + append!(y_data[i],[NaN for i=1:max_elem-length(y_data[i])]) + end + x_mat = hcat(x_data...) + y_mat = hcat(y_data...) + return x_mat, y_mat +end + """ export_performance_profile(T, filename; solver_names = [], header, kwargs...) @@ -164,14 +183,14 @@ Export a performance profile plot data as .csv file. Profiles data are padded wi * `T :: Matrix{Float64}`: each column of `T` defines the performance data for a solver (smaller is better). Failures on a given problem are represented by a negative value, an infinite value, or `NaN`. -* `filename :: String` : path to the export file. +* `filename :: String` : path to the exported file. ## Keyword Arguments -* `solver_names :: Vector{S}` : names of the solvers -* `header::Vector{String}`: Contains .csv file column names. Note that `header` value does not change columns order in .csv exported files (see Output). +* `solver_names :: Vector{S}` : names of the solvers. +- `header::Vector{String}`: Contains .csv file column names. Note that `header` value does not change columns order in .csv exported files (see Output). -Other keyword arguments are passed `performance_profile_data`. +Other keyword arguments are passed to `performance_profile_data`. Output: File containing profile data in .csv format. Columns are solver1_x, solver1_y, solver2_x, ... @@ -185,23 +204,26 @@ function export_performance_profile( ) where {S <: AbstractString} nsolvers = size(T)[2] - x_data, y_data, max_ratio = performance_profile_data(T; kwargs...) - max_elem = maximum(length.(x_data)) - for i in eachindex(x_data) - append!(x_data[i], [NaN for i = 1:(max_elem - length(x_data[i]))]) - append!(y_data[i], [NaN for i = 1:(max_elem - length(y_data[i]))]) - end - x_mat = hcat(x_data...) - y_mat = hcat(y_data...) - + x_mat, y_mat = performance_profile_data_mat(T;kwargs...) isempty(solver_names) && (solver_names = ["solver_$i" for i = 1:nsolvers]) + if !isempty(header) + header_l = size(T)[2]*2 + length(header) == header_l || error("Header should contain $(header_l) elements") + header = vcat([[sname*"_x",sname*"_y"] for sname in solver_names]...) + end + data = Matrix{Float64}(undef,size(x_mat,1),nsolvers*2) + for i =0:nsolvers-1 + data[:,2*i+1] .= x_mat[:,i+1] + data[:,2*i+2] .= y_mat[:,i+1] + end + if !isempty(header) header_l = size(T)[2] * 2 length(header) == header_l || error("Header should contain $(header_l) elements") header = vcat([[sname * "_x", sname * "_y"] for sname in solver_names]...) end - data = Matrix{Float64}(undef, max_elem, nsolvers * 2) + data = Matrix{Float64}(undef, size(x_mat,1), nsolvers * 2) for i = 0:(nsolvers - 1) data[:, 2 * i + 1] .= x_mat[:, i + 1] data[:, 2 * i + 2] .= y_mat[:, i + 1] From 705ebd5fccea74e370741fc6eded4456741dd2dc Mon Sep 17 00:00:00 2001 From: d-monnet Date: Fri, 27 Oct 2023 11:34:33 -0400 Subject: [PATCH 02/15] add tikz figure export function --- src/tikz_export.jl | 153 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/tikz_export.jl diff --git a/src/tikz_export.jl b/src/tikz_export.jl new file mode 100644 index 0000000..7954d25 --- /dev/null +++ b/src/tikz_export.jl @@ -0,0 +1,153 @@ +export export_performance_profile_tikz + +""" + function export_performance_profile_tikz(T, filename; kwargs...) + +Export tikz figure of the performance profiles given by `T` in `filename`. + +## Arguments + +* `T :: Matrix{Float64}`: each column of `T` defines the performance data for a solver (smaller is better). + Failures on a given problem are represented by a negative value, an infinite value, or `NaN`. +* `filename :: String` : path to the tikz exported file. + +## Keyword Arguments + +* `solvernames :: Vector{String} = []` : names of the solvers, should have as many elements as the number of columns of `T`. If empty, use the labels returned by `performance_profile_axis_labels`. +* `xlim::AbstractFloat=10.` : size of the figure along the x axis. /!\\ the legend is added on the right hand side of the figure. +* `ylim::AbstractFloat=10.` : size of the figure along the y axis. +* `nxgrad::Int=5` : number of graduations on the x axis. +* `nygrad::Int=5` : number of graduations on the y axis. +* `grid::Bool=true` : display grid if true. +* `colours::Vector{String} = []` : colours of the plots, should have as many elements as the number of columns of `T`. +* `linestyles::Vector{String} = []` : line style (dashed, dotted, ...) of the plots, should have as many elements as the number of columns of `T`. +* `linewidth::AbstractFloat = 1.0` : line with of the plots. +* `xlabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. +* `ylabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. + +Other keyword arguments are passed `performance_profile_data`. + +""" +function export_performance_profile_tikz( + T::Matrix{Float64}, + filename::String; + solvernames::Vector{String}=String[], + xlim::AbstractFloat=10., + ylim::AbstractFloat=10., + nxgrad::Int=5, + nygrad::Int=5, + grid::Bool=true, + # markers::Vector{S} = String[], + colours::Vector{String} = String[], + linestyles::Vector{String} = String[], + linewidth::AbstractFloat = 1.0, + xlabel::String = "", + ylabel::String = "", + kwargs...) + + + + logscale = true + if haskey(kwargs,:logscale) + logscale = kwargs[:logscale] + end + xlabel_def, ylabel_def, solvernames = performance_profile_axis_labels(solvernames, size(T, 2), logscale; kwargs...) + isempty(xlabel) && (xlabel=xlabel_def) + isempty(ylabel) && (ylabel=ylabel_def) + + # some offsets + axis_tik_l = 0.2 # tick length + lgd_offset = 0.5 # space between figure and legend box + lgd_box_length = 3. # legend box length + lgd_plot_length = 0.7 # legend plot length + lgd_v_offset = 0.7 # vertical space between legend items + + label_val = [0.2,0.25,0.5,1] # possible graduation labels along axes are multiples of label_val elements times 10^n + ymax = 1.0 + y_grad = collect(0.:ymax/(nygrad-1):ymax) + + isempty(colours) && (colours = ["black" for _ =1:size(T,2)]) + isempty(linestyles) && (linestyles = ["solid" for _ =1:size(T,2)]) + + x_mat, y_mat = BenchmarkProfiles.performance_profile_data_mat(T;kwargs...) + + # get nice looking graduation on x axis + xmax , _ = findmax(x_mat[.!isnan.(x_mat)]) + dist = xmax/(nxgrad-1) + n=log.(10,dist./label_val) + _, ind = findmin(abs.(n .- round.(n))) + xgrad_dist = label_val[ind]*10^round(n[ind]) + x_grad = [0. , [xgrad_dist*i for i =1 : nxgrad-1]...] + #x_grad[end] <= xmax || (pop!(x_grad)) + xmax=max(x_grad[end],xmax) + to_int(x) = isinteger(x) ? Int(x) : x + + xratio = xlim/xmax + yratio = ylim/ymax + open(filename, "w") do io + println(io, "\\begin{tikzpicture}") + # axes + println(io, "\\draw[line width=$linewidth] (0,0) -- ($xlim,0);") + println(io, "\\node at ($(xlim/2), -1) {$xlabel};") + println(io, "\\draw[line width=$linewidth] (0,0) -- (0,$ylim);") + println(io, "\\node at (-1,$(ylim/2)) [rotate = 90] {$ylabel};") + # axes graduations and labels, + if logscale + for i in eachindex(x_grad) + println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tik_l) node [pos=0, below] {\$2^{$(to_int(x_grad[i]))}\$};") + end + else + for i in eachindex(x_grad) + println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tik_l) node [pos=0, below] {$(to_int(x_grad[i]))};") + end + end + for i in eachindex(y_grad) + println(io, "\\draw[line width=$linewidth] (0,$(y_grad[i]*yratio)) -- ($axis_tik_l,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") + end + # grid + if grid + for i in eachindex(x_grad) + println(io, "\\draw[gray] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$ylim);") + end + for i in eachindex(y_grad) + println(io, "\\draw[gray] (0,$(y_grad[i]*yratio)) -- ($xlim,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") + end + end + + # profiles + for j in eachindex(solvernames) + drawcmd = "\\draw[line width=$linewidth, $(colours[j]), $(linestyles[j]), line width = $linewidth] " + drawcmd *= "($(x_mat[1,j]*xratio),$(y_mat[1,j]*yratio))" + for k in 2:size(x_mat,1) + if isnan(x_mat[k,j]) + break + end + if y_mat[k,j] > 1 # for some reasons last point of profile is set with y=1.1 by data function... + drawcmd *= " -- ($(xmax*xratio),$(y_mat[k-1,j]*yratio)) -- ($(xmax*xratio),$(y_mat[k-1,j]*yratio))" + else + # if !isempty(markers) + # drawcmd *= " -- ($(x_mat[k,j]*xratio),$(y_mat[k-1,j]*yratio)) node[$(colours[j]),draw,$(markers[j]),solid] {} -- ($(x_mat[k,j]*xratio),$(y_mat[k,j]*yratio))" + # else + drawcmd *= " -- ($(x_mat[k,j]*xratio),$(y_mat[k-1,j]*yratio)) -- ($(x_mat[k,j]*xratio),$(y_mat[k,j]*yratio))" + # end + end + end + drawcmd *= ";" + println(io,drawcmd) + end + # legend + for j in eachindex(solvernames) + legcmd = "\\draw[$(colours[j]), $(linestyles[j]), line width = $linewidth] " + legcmd *= "($(xlim+lgd_offset),$(ylim-j*lgd_v_offset)) -- ($(xlim+lgd_offset+lgd_plot_length),$(ylim-j*lgd_v_offset)) node [black,pos=1,right] {$(String(solvernames[j]))}" + # if !isempty(markers) + # legcmd *= " node [midway,draw,$(markers[j]),solid] {}" + # end + legcmd *= ";" + + println(io,legcmd) + end + # legend box + println(io,"\\draw[line width=$linewidth] ($(xlim+lgd_offset-0.1),$ylim) -- ($(xlim+lgd_offset+lgd_box_length),$ylim) -- ($(xlim+lgd_offset+lgd_box_length),$(ylim-lgd_v_offset*(length(solvernames)+1))) -- ($(xlim+lgd_offset-0.1),$(ylim-lgd_v_offset*(length(solvernames)+1))) -- cycle;") + println(io,"\\end{tikzpicture}") + end +end \ No newline at end of file From f804b349ce4feb051bee23643c91a396c1303a65 Mon Sep 17 00:00:00 2001 From: d-monnet Date: Fri, 27 Oct 2023 11:35:31 -0400 Subject: [PATCH 03/15] update module --- src/BenchmarkProfiles.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/BenchmarkProfiles.jl b/src/BenchmarkProfiles.jl index 2668e7d..a95d7ab 100644 --- a/src/BenchmarkProfiles.jl +++ b/src/BenchmarkProfiles.jl @@ -6,6 +6,7 @@ using Requires using Printf export performance_ratios, performance_profile, performance_profile_data, export_performance_profile +export export_performance_profile_tikz export data_ratios, data_profile export bp_backends, PlotsBackend, UnicodePlotsBackend, PGFPlotsXBackend @@ -30,6 +31,7 @@ end include("performance_profiles.jl") include("data_profiles.jl") +include("tikz_export.jl") """ Replace each number by 2^{number} in a string. From 6cd0883e5c990b250c87935fe61d3f38d7ceb020 Mon Sep 17 00:00:00 2001 From: d-monnet Date: Fri, 27 Oct 2023 11:39:50 -0400 Subject: [PATCH 04/15] add tests --- test/runtests.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index d79f968..5dfc80f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -72,4 +72,12 @@ if !Sys.isfreebsd() # GR_jll not available, so Plots won't install @test isfile(filename) rm(filename) end + + @testset "tikz export" begin + T = 10 * rand(25, 3) + filename = "tikz_fig.tex" + export_performance_profile_tikz(T,filename) + @test isfile(filename) + rm(filename) + end end From afdf117c23e8996e707d6678eeb9ef8134f5135c Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:34:11 -0400 Subject: [PATCH 05/15] Update src/tikz_export.jl Co-authored-by: tmigot --- src/tikz_export.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 7954d25..923e68b 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -21,7 +21,7 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `grid::Bool=true` : display grid if true. * `colours::Vector{String} = []` : colours of the plots, should have as many elements as the number of columns of `T`. * `linestyles::Vector{String} = []` : line style (dashed, dotted, ...) of the plots, should have as many elements as the number of columns of `T`. -* `linewidth::AbstractFloat = 1.0` : line with of the plots. +* `linewidth::AbstractFloat = 1.0` : line width of the plots. * `xlabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. * `ylabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. From b2327d7e6dbe1c17f346b8a09038e5c793f244fb Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:34:25 -0400 Subject: [PATCH 06/15] Update src/tikz_export.jl Co-authored-by: tmigot --- src/tikz_export.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 923e68b..81eecae 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -22,7 +22,7 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `colours::Vector{String} = []` : colours of the plots, should have as many elements as the number of columns of `T`. * `linestyles::Vector{String} = []` : line style (dashed, dotted, ...) of the plots, should have as many elements as the number of columns of `T`. * `linewidth::AbstractFloat = 1.0` : line width of the plots. -* `xlabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. +* `xlabel::String = ""` : x-axis label. If empty, uses the one returned by `performance_profile_axis_labels`. * `ylabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. Other keyword arguments are passed `performance_profile_data`. From d0b64e9df5085c88d5d3010f4d0f3f3ef728ce3d Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:34:35 -0400 Subject: [PATCH 07/15] Update src/tikz_export.jl Co-authored-by: tmigot --- src/tikz_export.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 81eecae..0b2bd9c 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -23,7 +23,7 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `linestyles::Vector{String} = []` : line style (dashed, dotted, ...) of the plots, should have as many elements as the number of columns of `T`. * `linewidth::AbstractFloat = 1.0` : line width of the plots. * `xlabel::String = ""` : x-axis label. If empty, uses the one returned by `performance_profile_axis_labels`. -* `ylabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. +* `ylabel::String = ""` : y-axis label. If empty, uses the one returned by `performance_profile_axis_labels`. Other keyword arguments are passed `performance_profile_data`. From 066ab130ef27b90842dbde519ace7949ed89c709 Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:34:45 -0400 Subject: [PATCH 08/15] Update src/tikz_export.jl Co-authored-by: tmigot --- src/tikz_export.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 0b2bd9c..b0feedc 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -25,7 +25,7 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `xlabel::String = ""` : x-axis label. If empty, uses the one returned by `performance_profile_axis_labels`. * `ylabel::String = ""` : y-axis label. If empty, uses the one returned by `performance_profile_axis_labels`. -Other keyword arguments are passed `performance_profile_data`. +Other keyword arguments are passed to `performance_profile_data`. """ function export_performance_profile_tikz( From ba3f5be2c312ca65c1c10e226d9ab6e85a02a839 Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:35:13 -0400 Subject: [PATCH 09/15] Update src/performance_profiles.jl Co-authored-by: tmigot --- src/performance_profiles.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/performance_profiles.jl b/src/performance_profiles.jl index 675e8a7..0adf929 100644 --- a/src/performance_profiles.jl +++ b/src/performance_profiles.jl @@ -160,7 +160,7 @@ end """ function performance_profile_data_mat(T;kwargs...) -Retruns `performance_profile_data` output (vectors) as matrices. Matrices are padded with NaN if necessary. +Returns `performance_profile_data` output (vectors) as matrices. Matrices are padded with NaN if necessary. """ function performance_profile_data_mat(T::Matrix{Float64};kwargs...) x_data, y_data, max_ratio = performance_profile_data(T;kwargs...) From 2ef164c934c46b4276f8fe75849657e71ad06e75 Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:35:22 -0400 Subject: [PATCH 10/15] Update src/performance_profiles.jl Co-authored-by: tmigot --- src/performance_profiles.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/performance_profiles.jl b/src/performance_profiles.jl index 0adf929..5989263 100644 --- a/src/performance_profiles.jl +++ b/src/performance_profiles.jl @@ -187,7 +187,7 @@ Export a performance profile plot data as .csv file. Profiles data are padded wi ## Keyword Arguments -* `solver_names :: Vector{S}` : names of the solvers. +- `solver_names :: Vector{S}` : names of the solvers. - `header::Vector{String}`: Contains .csv file column names. Note that `header` value does not change columns order in .csv exported files (see Output). Other keyword arguments are passed to `performance_profile_data`. From f4cf755cdf1988734b42db604e96c33646f35f0b Mon Sep 17 00:00:00 2001 From: d-monnet Date: Mon, 30 Oct 2023 12:22:35 -0400 Subject: [PATCH 11/15] update keyword arguments --- src/tikz_export.jl | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 7954d25..8b95590 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -24,7 +24,13 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `linewidth::AbstractFloat = 1.0` : line with of the plots. * `xlabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. * `ylabel::String = ""` : x axis label. If empty, uses the one returns by `performance_profile_axis_labels`. - +* `axis_tick_length::AbstractFloat = 0.2` : axis graduation tick length. +* `lgd_pos::Vector = [xlim+0.5,ylim]`, : legend box top left corner coordinates, by default legend is on the left had side of the figure. +* `lgd_plot_length::AbstractFloat = 0.7` : legend curve plot length. +* `lgd_v_offset::AbstractFloat = 0.7` : vertical space between two legend items. +* `lgd_plot_offset::AbstractFloat = 0.1` : space between legend box left side and curve plot. +* `lgd_box_length::AbstractFloat = 3.` : legend box horizontal length. +* `label_val::Vector = [0.2,0.25,0.5,1]` : possible graduation labels along axes are multiples of label_val elements times 10^n (n is automatically selected). Other keyword arguments are passed `performance_profile_data`. """ @@ -43,6 +49,13 @@ function export_performance_profile_tikz( linewidth::AbstractFloat = 1.0, xlabel::String = "", ylabel::String = "", + axis_tick_length::AbstractFloat = 0.2, + lgd_pos::Vector = [xlim+0.5,ylim], + lgd_plot_length::AbstractFloat = 0.7, + lgd_v_offset::AbstractFloat = 0.7, + lgd_plot_offset::AbstractFloat = 0.1, + lgd_box_length::AbstractFloat = 3., + label_val::Vector = [0.2,0.25,0.5,1], kwargs...) @@ -55,16 +68,7 @@ function export_performance_profile_tikz( isempty(xlabel) && (xlabel=xlabel_def) isempty(ylabel) && (ylabel=ylabel_def) - # some offsets - axis_tik_l = 0.2 # tick length - lgd_offset = 0.5 # space between figure and legend box - lgd_box_length = 3. # legend box length - lgd_plot_length = 0.7 # legend plot length - lgd_v_offset = 0.7 # vertical space between legend items - - label_val = [0.2,0.25,0.5,1] # possible graduation labels along axes are multiples of label_val elements times 10^n - ymax = 1.0 - y_grad = collect(0.:ymax/(nygrad-1):ymax) + y_grad = collect(0.:1.0/(nygrad-1):1.0) isempty(colours) && (colours = ["black" for _ =1:size(T,2)]) isempty(linestyles) && (linestyles = ["solid" for _ =1:size(T,2)]) @@ -78,8 +82,16 @@ function export_performance_profile_tikz( _, ind = findmin(abs.(n .- round.(n))) xgrad_dist = label_val[ind]*10^round(n[ind]) x_grad = [0. , [xgrad_dist*i for i =1 : nxgrad-1]...] - #x_grad[end] <= xmax || (pop!(x_grad)) xmax=max(x_grad[end],xmax) + + # get nice looking graduation on y axis + dist = 1.0/(nygrad-1) + n=log.(10,dist./label_val) + _, ind = findmin(abs.(n .- round.(n))) + ygrad_dist = label_val[ind]*10^round(n[ind]) + y_grad = [0. , [ygrad_dist*i for i =1 : nygrad-1]...] + ymax=max(y_grad[end],1.0) + to_int(x) = isinteger(x) ? Int(x) : x xratio = xlim/xmax @@ -94,15 +106,15 @@ function export_performance_profile_tikz( # axes graduations and labels, if logscale for i in eachindex(x_grad) - println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tik_l) node [pos=0, below] {\$2^{$(to_int(x_grad[i]))}\$};") + println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tick_length) node [pos=0, below] {\$2^{$(to_int(x_grad[i]))}\$};") end else for i in eachindex(x_grad) - println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tik_l) node [pos=0, below] {$(to_int(x_grad[i]))};") + println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tick_length) node [pos=0, below] {$(to_int(x_grad[i]))};") end end for i in eachindex(y_grad) - println(io, "\\draw[line width=$linewidth] (0,$(y_grad[i]*yratio)) -- ($axis_tik_l,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") + println(io, "\\draw[line width=$linewidth] (0,$(y_grad[i]*yratio)) -- ($axis_tick_length,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") end # grid if grid @@ -138,7 +150,7 @@ function export_performance_profile_tikz( # legend for j in eachindex(solvernames) legcmd = "\\draw[$(colours[j]), $(linestyles[j]), line width = $linewidth] " - legcmd *= "($(xlim+lgd_offset),$(ylim-j*lgd_v_offset)) -- ($(xlim+lgd_offset+lgd_plot_length),$(ylim-j*lgd_v_offset)) node [black,pos=1,right] {$(String(solvernames[j]))}" + legcmd *= "($(lgd_pos[1]+lgd_plot_offset),$(lgd_pos[2]-j*lgd_v_offset)) -- ($(lgd_pos[1]+lgd_plot_offset+lgd_plot_length),$(lgd_pos[2]-j*lgd_v_offset)) node [black,pos=1,right] {$(String(solvernames[j]))}" # if !isempty(markers) # legcmd *= " node [midway,draw,$(markers[j]),solid] {}" # end @@ -147,7 +159,7 @@ function export_performance_profile_tikz( println(io,legcmd) end # legend box - println(io,"\\draw[line width=$linewidth] ($(xlim+lgd_offset-0.1),$ylim) -- ($(xlim+lgd_offset+lgd_box_length),$ylim) -- ($(xlim+lgd_offset+lgd_box_length),$(ylim-lgd_v_offset*(length(solvernames)+1))) -- ($(xlim+lgd_offset-0.1),$(ylim-lgd_v_offset*(length(solvernames)+1))) -- cycle;") + println(io,"\\draw[line width=$linewidth] ($(lgd_pos[1]),$(lgd_pos[2])) -- ($(lgd_pos[1]+lgd_box_length),$(lgd_pos[2])) -- ($(lgd_pos[1]+lgd_box_length),$(lgd_pos[2]-lgd_v_offset*(length(solvernames)+1))) -- ($(lgd_pos[1]),$(lgd_pos[2]-lgd_v_offset*(length(solvernames)+1))) -- cycle;") println(io,"\\end{tikzpicture}") end end \ No newline at end of file From 4a36c94610a9e318e20995f9a660453053a1072b Mon Sep 17 00:00:00 2001 From: d-monnet Date: Mon, 30 Oct 2023 15:40:19 -0400 Subject: [PATCH 12/15] add figure export options provided by TizkPictures.jl export options are .tikz, .tex, .svg and .pdf --- Project.toml | 3 +- src/tikz_export.jl | 128 +++++++++++++++++++++++---------------------- test/runtests.jl | 16 ++++-- 3 files changed, 81 insertions(+), 66 deletions(-) diff --git a/Project.toml b/Project.toml index 5cfbd64..ea048c9 100644 --- a/Project.toml +++ b/Project.toml @@ -9,14 +9,15 @@ NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Requires = "ae029012-a4dd-5104-9daa-d747884805df" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +TikzPictures = "37f6aa50-8035-52d0-81c2-5a1d08754b2d" [compat] CSV = "0.10" LaTeXStrings = "^1.3" NaNMath = "0.3, 1" Requires = "1" -julia = "^1.6" Tables = "1.11" +julia = "^1.6" [extras] PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925" diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 1ab2437..1f1908b 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -1,3 +1,5 @@ +using TikzPictures + export export_performance_profile_tikz """ @@ -13,6 +15,7 @@ Export tikz figure of the performance profiles given by `T` in `filename`. ## Keyword Arguments +* `file_type = TIKZ` : type of exported file. Options are `TIKZ`(raw tikz code), `TEX`(embeded tikz code, ready to compile), `SVG`, `PDF`. * `solvernames :: Vector{String} = []` : names of the solvers, should have as many elements as the number of columns of `T`. If empty, use the labels returned by `performance_profile_axis_labels`. * `xlim::AbstractFloat=10.` : size of the figure along the x axis. /!\\ the legend is added on the right hand side of the figure. * `ylim::AbstractFloat=10.` : size of the figure along the y axis. @@ -30,13 +33,14 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `lgd_v_offset::AbstractFloat = 0.7` : vertical space between two legend items. * `lgd_plot_offset::AbstractFloat = 0.1` : space between legend box left side and curve plot. * `lgd_box_length::AbstractFloat = 3.` : legend box horizontal length. -* `label_val::Vector = [0.2,0.25,0.5,1]` : possible graduation labels along axes are multiples of label_val elements times 10^n (n is automatically selected). +* `label_val::Vector = [0.2,0.25,0.5,1]` : possible graduation labels along axes are multiples of label_val elements times 10^n (n is automatically selected). Other keyword arguments are passed to `performance_profile_data`. """ function export_performance_profile_tikz( T::Matrix{Float64}, filename::String; + file_type = TIKZ, solvernames::Vector{String}=String[], xlim::AbstractFloat=10., ylim::AbstractFloat=10., @@ -58,8 +62,6 @@ function export_performance_profile_tikz( label_val::Vector = [0.2,0.25,0.5,1], kwargs...) - - logscale = true if haskey(kwargs,:logscale) logscale = kwargs[:logscale] @@ -96,70 +98,72 @@ function export_performance_profile_tikz( xratio = xlim/xmax yratio = ylim/ymax - open(filename, "w") do io - println(io, "\\begin{tikzpicture}") - # axes - println(io, "\\draw[line width=$linewidth] (0,0) -- ($xlim,0);") - println(io, "\\node at ($(xlim/2), -1) {$xlabel};") - println(io, "\\draw[line width=$linewidth] (0,0) -- (0,$ylim);") - println(io, "\\node at (-1,$(ylim/2)) [rotate = 90] {$ylabel};") - # axes graduations and labels, - if logscale - for i in eachindex(x_grad) - println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tick_length) node [pos=0, below] {\$2^{$(to_int(x_grad[i]))}\$};") - end - else - for i in eachindex(x_grad) - println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tick_length) node [pos=0, below] {$(to_int(x_grad[i]))};") - end + io = IOBuffer() + + # axes + println(io, "\\draw[line width=$linewidth] (0,0) -- ($xlim,0);") + println(io, "\\node at ($(xlim/2), -1) {$xlabel};") + println(io, "\\draw[line width=$linewidth] (0,0) -- (0,$ylim);") + println(io, "\\node at (-1,$(ylim/2)) [rotate = 90] {$ylabel};") + # axes graduations and labels, + if logscale + for i in eachindex(x_grad) + println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tick_length) node [pos=0, below] {\$2^{$(to_int(x_grad[i]))}\$};") end - for i in eachindex(y_grad) - println(io, "\\draw[line width=$linewidth] (0,$(y_grad[i]*yratio)) -- ($axis_tick_length,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") + else + for i in eachindex(x_grad) + println(io, "\\draw[line width=$linewidth] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$axis_tick_length) node [pos=0, below] {$(to_int(x_grad[i]))};") end - # grid - if grid - for i in eachindex(x_grad) - println(io, "\\draw[gray] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$ylim);") - end - for i in eachindex(y_grad) - println(io, "\\draw[gray] (0,$(y_grad[i]*yratio)) -- ($xlim,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") - end + end + for i in eachindex(y_grad) + println(io, "\\draw[line width=$linewidth] (0,$(y_grad[i]*yratio)) -- ($axis_tick_length,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") + end + # grid + if grid + for i in eachindex(x_grad) + println(io, "\\draw[gray] ($(x_grad[i]*xratio),0) -- ($(x_grad[i]*xratio),$ylim);") + end + for i in eachindex(y_grad) + println(io, "\\draw[gray] (0,$(y_grad[i]*yratio)) -- ($xlim,$(y_grad[i]*yratio)) node [pos=0, left] {$(to_int(y_grad[i]))};") end + end - # profiles - for j in eachindex(solvernames) - drawcmd = "\\draw[line width=$linewidth, $(colours[j]), $(linestyles[j]), line width = $linewidth] " - drawcmd *= "($(x_mat[1,j]*xratio),$(y_mat[1,j]*yratio))" - for k in 2:size(x_mat,1) - if isnan(x_mat[k,j]) - break - end - if y_mat[k,j] > 1 # for some reasons last point of profile is set with y=1.1 by data function... - drawcmd *= " -- ($(xmax*xratio),$(y_mat[k-1,j]*yratio)) -- ($(xmax*xratio),$(y_mat[k-1,j]*yratio))" - else - # if !isempty(markers) - # drawcmd *= " -- ($(x_mat[k,j]*xratio),$(y_mat[k-1,j]*yratio)) node[$(colours[j]),draw,$(markers[j]),solid] {} -- ($(x_mat[k,j]*xratio),$(y_mat[k,j]*yratio))" - # else - drawcmd *= " -- ($(x_mat[k,j]*xratio),$(y_mat[k-1,j]*yratio)) -- ($(x_mat[k,j]*xratio),$(y_mat[k,j]*yratio))" - # end - end + # profiles + for j in eachindex(solvernames) + drawcmd = "\\draw[line width=$linewidth, $(colours[j]), $(linestyles[j]), line width = $linewidth] " + drawcmd *= "($(x_mat[1,j]*xratio),$(y_mat[1,j]*yratio))" + for k in 2:size(x_mat,1) + if isnan(x_mat[k,j]) + break + end + if y_mat[k,j] > 1 # for some reasons last point of profile is set with y=1.1 by data function... + drawcmd *= " -- ($(xmax*xratio),$(y_mat[k-1,j]*yratio)) -- ($(xmax*xratio),$(y_mat[k-1,j]*yratio))" + else + # if !isempty(markers) + # drawcmd *= " -- ($(x_mat[k,j]*xratio),$(y_mat[k-1,j]*yratio)) node[$(colours[j]),draw,$(markers[j]),solid] {} -- ($(x_mat[k,j]*xratio),$(y_mat[k,j]*yratio))" + # else + drawcmd *= " -- ($(x_mat[k,j]*xratio),$(y_mat[k-1,j]*yratio)) -- ($(x_mat[k,j]*xratio),$(y_mat[k,j]*yratio))" + # end end - drawcmd *= ";" - println(io,drawcmd) - end - # legend - for j in eachindex(solvernames) - legcmd = "\\draw[$(colours[j]), $(linestyles[j]), line width = $linewidth] " - legcmd *= "($(lgd_pos[1]+lgd_plot_offset),$(lgd_pos[2]-j*lgd_v_offset)) -- ($(lgd_pos[1]+lgd_plot_offset+lgd_plot_length),$(lgd_pos[2]-j*lgd_v_offset)) node [black,pos=1,right] {$(String(solvernames[j]))}" - # if !isempty(markers) - # legcmd *= " node [midway,draw,$(markers[j]),solid] {}" - # end - legcmd *= ";" - - println(io,legcmd) end - # legend box - println(io,"\\draw[line width=$linewidth] ($(lgd_pos[1]),$(lgd_pos[2])) -- ($(lgd_pos[1]+lgd_box_length),$(lgd_pos[2])) -- ($(lgd_pos[1]+lgd_box_length),$(lgd_pos[2]-lgd_v_offset*(length(solvernames)+1))) -- ($(lgd_pos[1]),$(lgd_pos[2]-lgd_v_offset*(length(solvernames)+1))) -- cycle;") - println(io,"\\end{tikzpicture}") + drawcmd *= ";" + println(io,drawcmd) end + # legend + for j in eachindex(solvernames) + legcmd = "\\draw[$(colours[j]), $(linestyles[j]), line width = $linewidth] " + legcmd *= "($(lgd_pos[1]+lgd_plot_offset),$(lgd_pos[2]-j*lgd_v_offset)) -- ($(lgd_pos[1]+lgd_plot_offset+lgd_plot_length),$(lgd_pos[2]-j*lgd_v_offset)) node [black,pos=1,right] {$(String(solvernames[j]))}" + # if !isempty(markers) + # legcmd *= " node [midway,draw,$(markers[j]),solid] {}" + # end + legcmd *= ";" + + println(io,legcmd) + end + # legend box + println(io,"\\draw[line width=$linewidth] ($(lgd_pos[1]),$(lgd_pos[2])) -- ($(lgd_pos[1]+lgd_box_length),$(lgd_pos[2])) -- ($(lgd_pos[1]+lgd_box_length),$(lgd_pos[2]-lgd_v_offset*(length(solvernames)+1))) -- ($(lgd_pos[1]),$(lgd_pos[2]-lgd_v_offset*(length(solvernames)+1))) -- cycle;") + + raw_code = String(take!(io)) + tp = TikzPicture(raw_code) + save(file_type(filename),tp) end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 5dfc80f..43f0ae8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,7 @@ using BenchmarkProfiles using LaTeXStrings using Test +using TikzPictures @testset "powertick" begin @test BenchmarkProfiles.powertick("15") == "2¹⁵" @@ -75,9 +76,18 @@ if !Sys.isfreebsd() # GR_jll not available, so Plots won't install @testset "tikz export" begin T = 10 * rand(25, 3) - filename = "tikz_fig.tex" + filename = "tikz_fig" export_performance_profile_tikz(T,filename) - @test isfile(filename) - rm(filename) + @test isfile(filename * ".tikz") + rm(filename * ".tikz") + export_performance_profile_tikz(T,filename,file_type = TEX) + @test isfile(filename * ".tex") + rm(filename * ".tex") + export_performance_profile_tikz(T,filename,file_type = SVG) + @test isfile(filename * ".svg") + rm(filename * ".svg") + export_performance_profile_tikz(T,filename,file_type = PDF) + @test isfile(filename * ".pdf") + rm(filename * ".pdf") end end From fda63a608d2842750ee00b020ea3425ed822778d Mon Sep 17 00:00:00 2001 From: d-monnet <70266099+d-monnet@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:37:07 -0400 Subject: [PATCH 13/15] Update src/performance_profiles.jl Co-authored-by: tmigot --- src/performance_profiles.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/performance_profiles.jl b/src/performance_profiles.jl index 5989263..b301055 100644 --- a/src/performance_profiles.jl +++ b/src/performance_profiles.jl @@ -158,7 +158,7 @@ function performance_profile( end """ - function performance_profile_data_mat(T;kwargs...) + performance_profile_data_mat(T; kwargs...) Returns `performance_profile_data` output (vectors) as matrices. Matrices are padded with NaN if necessary. """ From ce7e135767ba778cbdaff8c176f661de28fcdc08 Mon Sep 17 00:00:00 2001 From: d-monnet Date: Wed, 1 Nov 2023 11:47:42 -0400 Subject: [PATCH 14/15] add TikzPicture.jl compat --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index ea048c9..7bbeea7 100644 --- a/Project.toml +++ b/Project.toml @@ -17,6 +17,7 @@ LaTeXStrings = "^1.3" NaNMath = "0.3, 1" Requires = "1" Tables = "1.11" +TikzPictures = "3.5" julia = "^1.6" [extras] From 095e35802deb46f30f8f23ea36c1c2eeb5914c3f Mon Sep 17 00:00:00 2001 From: d-monnet Date: Thu, 2 Nov 2023 11:09:05 -0400 Subject: [PATCH 15/15] fix logscale argument --- src/tikz_export.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tikz_export.jl b/src/tikz_export.jl index 1f1908b..0a9fa02 100644 --- a/src/tikz_export.jl +++ b/src/tikz_export.jl @@ -34,6 +34,8 @@ Export tikz figure of the performance profiles given by `T` in `filename`. * `lgd_plot_offset::AbstractFloat = 0.1` : space between legend box left side and curve plot. * `lgd_box_length::AbstractFloat = 3.` : legend box horizontal length. * `label_val::Vector = [0.2,0.25,0.5,1]` : possible graduation labels along axes are multiples of label_val elements times 10^n (n is automatically selected). +* `logscale::Bool = true` : produce a logarithmic (base 2) performance plot. + Other keyword arguments are passed to `performance_profile_data`. """ @@ -60,12 +62,9 @@ function export_performance_profile_tikz( lgd_plot_offset::AbstractFloat = 0.1, lgd_box_length::AbstractFloat = 3., label_val::Vector = [0.2,0.25,0.5,1], + logscale::Bool = true, kwargs...) - logscale = true - if haskey(kwargs,:logscale) - logscale = kwargs[:logscale] - end xlabel_def, ylabel_def, solvernames = performance_profile_axis_labels(solvernames, size(T, 2), logscale; kwargs...) isempty(xlabel) && (xlabel=xlabel_def) isempty(ylabel) && (ylabel=ylabel_def)