From 768c2f144ad921405b576cedab4bd7536f03725c Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Tue, 16 Jul 2024 16:41:57 +0100 Subject: [PATCH 01/17] initial commit for plotting recipies --- Project.toml | 6 +- src/plotting/plot_recipes.jl | 178 +++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 src/plotting/plot_recipes.jl diff --git a/Project.toml b/Project.toml index df2b7d2cf..a051fc93f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,9 +1,6 @@ name = "UncertaintyQuantification" uuid = "7183a548-a887-11e9-15ce-a56ab60bad7a" -authors = [ - "Jasper Behrensdorf ", - "Ander Gray ", -] +authors = ["Jasper Behrensdorf ", "Ander Gray "] version = "0.9.0" [deps] @@ -25,6 +22,7 @@ Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl new file mode 100644 index 000000000..1f3f148e4 --- /dev/null +++ b/src/plotting/plot_recipes.jl @@ -0,0 +1,178 @@ +using RecipesBase + +DEFAULT_ALPHA = 0.2 +DEFAULT_LABEL = "" +DEFAULT_GRID = false +DEFAULT_LEGEND = false +DEFAULT_DISTRIBUTION = :pdf +DEFAULT_FILL = :gray +DEFAULT_COLOUR_PDF = :blue +DEFAULT_COLOUR_UPPER = :red +DEFAULT_COLOUR_LOWER = :black + +DEFAULT_PLOT_RANGE_EXTEND = 0.2 +DEFAULT_PLOT_RANGE_INTERVAL = 0.4 +DEFAULT_PLOT_GRID_NUMBER = 500 +DEFAULT_FONT_SIZE = 18 +DEFAULT_TICK_SIZE = 12 + + +@recipe function _plot(x::RandomVariable) + + grid --> DEFAULT_GRID + legend --> DEFAULT_LEGEND + ylabel --> "pdf" + xlabel --> x.name + + lo_grid = quantile(x, 0.001) + hi_grid = quantile(x, 0.999) + + lo_grid = x.lb + hi_grid = x.ub + + width = (hi_grid + lo_grid)/2 + + lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) + hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) + + x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) + pdf_evals = pdf.(Ref(x), x_grid) + + @series begin + fillrange := pdf_evals + color := DEFAULT_FILL + fillalpha := DEFAULT_ALPHA + x_grid, zeros(length(x_grid)) + end + + @series begin + color --> DEFAULT_COLOUR_PDF + alpha := 1 + label := "" + x_grid, pdf_evals + end +end + +@recipe function _plot(x::Interval) + + grid --> DEFAULT_GRID + legend --> DEFAULT_LEGEND + ylabel --> "cdf" + xlabel --> x.name + + lo_grid = x.lb + hi_grid = x.ub + + width = (hi_grid + lo_grid)/2 + + lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_INTERVAL) + hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_INTERVAL) + + x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) + + cdf_lo = x_grid .>= x.ub + cdf_hi = x_grid .>= x.lb + + @series begin + fillrange := cdf_hi + color := DEFAULT_FILL + fillalpha := DEFAULT_ALPHA + x_grid, cdf_lo + end + + @series begin + color --> DEFAULT_COLOUR_LOWER + alpha := 1 + label := "" + x_grid, cdf_lo + end + + @series begin + color --> DEFAULT_COLOUR_UPPER + alpha := 1 + label := "" + x_grid, cdf_hi + end + +end + +@recipe function _plot(x::ProbabilityBox) + + grid --> DEFAULT_GRID + legend --> DEFAULT_LEGEND + ylabel --> "cdf" + xlabel --> x.name + + lo_grid = quantile(x, 0.001)[1] + hi_grid = quantile(x, 0.999)[2] + + width = (hi_grid + lo_grid)/2 + + lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) + hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) + + x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) + cdf_evals = cdf.(Ref(x), x_grid) + + @series begin + fillrange := hi.(cdf_evals) + color := DEFAULT_FILL + fillalpha := DEFAULT_ALPHA + x_grid, lo.(cdf_evals) + end + + @series begin + color --> DEFAULT_COLOUR_LOWER + alpha := 1 + label := "" + x_grid, lo.(cdf_evals) + end + + @series begin + color --> DEFAULT_COLOUR_UPPER + alpha := 1 + label := "" + x_grid, hi.(cdf_evals) + end +end + + +### +# This code is a modified version of the plot recipe from IntervalArithmetic.jl +# https://github.com/JuliaIntervals/IntervalArithmetic.jl +### + +# Plot a 2D IntervalBox: +@recipe function _plot(x::Interval, y::Interval) #; customcolor = :green, alpha=0.5) + + seriesalpha --> DEFAULT_ALPHA + seriestype := :shape + + x = [x.lb, x.ub, x.ub, x.lb] + y = [y.lb, y.lb, y.ub, y.ub] + + x, y +end + +# Plot a vector of 2D IntervalBoxes: +@recipe function _plot(xx::Vector{T}, yy::Vector{T}) where T<:Interval + + seriestype := :shape + + xs = Float64[] + ys = Float64[] + + # build up coords: # (alternative: use @series) + for i = 1:length(xx) + (x, y) = (xx[i], yy[i]) + + # use NaNs to separate + append!(xs, [x.lb, x.ub, x.ub, x.lb, NaN]) + append!(ys, [y.lb, y.lb, y.ub, y.ub, NaN]) + + end + + seriesalpha --> DEFAULT_ALPHA + xs, ys + +end From bdea02ad27d3bf1593b1aa6b96bb4f459c51c4db Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Fri, 19 Jul 2024 17:21:21 +0100 Subject: [PATCH 02/17] working plotting with UQInputs vector --- Project.toml | 6 ++--- src/UncertaintyQuantification.jl | 4 ++++ src/inputs/imprecise/interval.jl | 5 ++++ src/plotting/plot_recipes.jl | 40 +++++++++++++++++++++----------- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Project.toml b/Project.toml index d4d4ab5a8..0ed15b71c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,9 +1,6 @@ name = "UncertaintyQuantification" uuid = "7183a548-a887-11e9-15ce-a56ab60bad7a" -authors = [ - "Jasper Behrensdorf ", - "Ander Gray ", -] +authors = ["Jasper Behrensdorf ", "Ander Gray "] version = "0.10.0" [deps] @@ -23,6 +20,7 @@ KernelDensity = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MeshAdaptiveDirectSearch = "f4d74008-4565-11e9-04bd-4fe404e6a92a" Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/src/UncertaintyQuantification.jl b/src/UncertaintyQuantification.jl index 264880faa..f0db4a0ba 100644 --- a/src/UncertaintyQuantification.jl +++ b/src/UncertaintyQuantification.jl @@ -19,6 +19,8 @@ using QuasiMonteCarlo using Random using Reexport using StatsBase +using Plots +using RecipesBase @reexport using Distributions @@ -186,6 +188,8 @@ include("reliability/probabilityoffailure.jl") include("reliability/probabilityoffailure_imprecise.jl") include("sensitivity/sobolindices.jl") +include("plotting/plot_recipes.jl") + include("util/wrap.jl") include("util/imprecise.jl") diff --git a/src/inputs/imprecise/interval.jl b/src/inputs/imprecise/interval.jl index c60b2385f..08e2ade2d 100644 --- a/src/inputs/imprecise/interval.jl +++ b/src/inputs/imprecise/interval.jl @@ -42,3 +42,8 @@ function bounds(i::Interval) end Base.in(u, i::Interval) = i.lb <= u <= i.ub + +hi(X::Interval) = X.ub +lo(X::Interval) = X.lb +hi(X::Real) = X +lo(X::Real) = X \ No newline at end of file diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 1f3f148e4..e4431beaa 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -1,5 +1,3 @@ -using RecipesBase - DEFAULT_ALPHA = 0.2 DEFAULT_LABEL = "" DEFAULT_GRID = false @@ -10,7 +8,8 @@ DEFAULT_COLOUR_PDF = :blue DEFAULT_COLOUR_UPPER = :red DEFAULT_COLOUR_LOWER = :black -DEFAULT_PLOT_RANGE_EXTEND = 0.2 +DEFAULT_PLOT_RANGE_EXTEND_DENSITY = 0.1 +DEFAULT_PLOT_RANGE_EXTEND = 0.4 DEFAULT_PLOT_RANGE_INTERVAL = 0.4 DEFAULT_PLOT_GRID_NUMBER = 500 DEFAULT_FONT_SIZE = 18 @@ -27,13 +26,10 @@ DEFAULT_TICK_SIZE = 12 lo_grid = quantile(x, 0.001) hi_grid = quantile(x, 0.999) - lo_grid = x.lb - hi_grid = x.ub - width = (hi_grid + lo_grid)/2 - lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) - hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) + lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) + hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) pdf_evals = pdf.(Ref(x), x_grid) @@ -65,13 +61,15 @@ end width = (hi_grid + lo_grid)/2 - lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_INTERVAL) - hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_INTERVAL) + plot_lo = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) + plot_hi = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) cdf_lo = x_grid .>= x.ub - cdf_hi = x_grid .>= x.lb + cdf_hi = x_grid .> x.lb + + xlims --> (plot_lo, plot_hi) @series begin fillrange := cdf_hi @@ -103,8 +101,8 @@ end ylabel --> "cdf" xlabel --> x.name - lo_grid = quantile(x, 0.001)[1] - hi_grid = quantile(x, 0.999)[2] + lo_grid = quantile(x, 0.001).lb + hi_grid = quantile(x, 0.999).ub width = (hi_grid + lo_grid)/2 @@ -136,7 +134,23 @@ end end end +@recipe function _plot(x::Vector{T}) where T <: UQInput + + x_no_params = filter(x -> !isa(x, Parameter), x) + + N_inputs = length(x_no_params) + cols = ceil(Int, sqrt(N_inputs)) # Calculate the number of columns + rows = ceil(Int, N_inputs / cols) # Calculate the number of rows needed + layout := grid(rows, cols) # Create a grid layout + + for i = 1:N_inputs + @series begin + subplot := i + x_no_params[i] + end + end +end ### # This code is a modified version of the plot recipe from IntervalArithmetic.jl # https://github.com/JuliaIntervals/IntervalArithmetic.jl From 10ba8249c69732bfafdd1952efce2a6e9f59725f Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Mon, 22 Jul 2024 10:29:36 +0100 Subject: [PATCH 03/17] fix plotting width --- src/plotting/plot_recipes.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index e4431beaa..329369ca0 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -8,8 +8,8 @@ DEFAULT_COLOUR_PDF = :blue DEFAULT_COLOUR_UPPER = :red DEFAULT_COLOUR_LOWER = :black -DEFAULT_PLOT_RANGE_EXTEND_DENSITY = 0.1 -DEFAULT_PLOT_RANGE_EXTEND = 0.4 +DEFAULT_PLOT_RANGE_EXTEND_DENSITY = 0.2 +DEFAULT_PLOT_RANGE_EXTEND = 0.2 DEFAULT_PLOT_RANGE_INTERVAL = 0.4 DEFAULT_PLOT_GRID_NUMBER = 500 DEFAULT_FONT_SIZE = 18 @@ -26,7 +26,7 @@ DEFAULT_TICK_SIZE = 12 lo_grid = quantile(x, 0.001) hi_grid = quantile(x, 0.999) - width = (hi_grid + lo_grid)/2 + width = hi_grid - lo_grid lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) @@ -59,17 +59,17 @@ end lo_grid = x.lb hi_grid = x.ub - width = (hi_grid + lo_grid)/2 + width = hi_grid - lo_grid - plot_lo = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) - plot_hi = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) + plot_lo = lo_grid - abs(width * DEFAULT_PLOT_RANGE_INTERVAL) + plot_hi = hi_grid + abs(width * DEFAULT_PLOT_RANGE_INTERVAL) + + xlims := (plot_lo, plot_hi) x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) cdf_lo = x_grid .>= x.ub cdf_hi = x_grid .> x.lb - - xlims --> (plot_lo, plot_hi) @series begin fillrange := cdf_hi @@ -104,7 +104,7 @@ end lo_grid = quantile(x, 0.001).lb hi_grid = quantile(x, 0.999).ub - width = (hi_grid + lo_grid)/2 + width = hi_grid - lo_grid lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) From ae2760c0ba780965f0c7afad44a2ae91ef100018 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Mon, 22 Jul 2024 13:01:04 +0100 Subject: [PATCH 04/17] adds option to plot in cdf for random variables --- src/plotting/plot_recipes.jl | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 329369ca0..c9bb8e378 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -2,6 +2,7 @@ DEFAULT_ALPHA = 0.2 DEFAULT_LABEL = "" DEFAULT_GRID = false DEFAULT_LEGEND = false +DEFAULT_CDF = false DEFAULT_DISTRIBUTION = :pdf DEFAULT_FILL = :gray DEFAULT_COLOUR_PDF = :blue @@ -15,13 +16,17 @@ DEFAULT_PLOT_GRID_NUMBER = 500 DEFAULT_FONT_SIZE = 18 DEFAULT_TICK_SIZE = 12 - -@recipe function _plot(x::RandomVariable) +### +# Plots for UQInputs +### +@recipe function _plot(x::RandomVariable; cdf_on = DEFAULT_CDF) grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND - ylabel --> "pdf" xlabel --> x.name + cdf_on ? ylabel --> "cdf" : ylabel --> "pdf" + + # cdf_on --> DEFAULT_CDF lo_grid = quantile(x, 0.001) hi_grid = quantile(x, 0.999) @@ -32,20 +37,28 @@ DEFAULT_TICK_SIZE = 12 hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) - pdf_evals = pdf.(Ref(x), x_grid) + + if cdf_on + distribution_evals = cdf.(Ref(x), x_grid) + else + distribution_evals = pdf.(Ref(x), x_grid) + end - @series begin - fillrange := pdf_evals - color := DEFAULT_FILL - fillalpha := DEFAULT_ALPHA - x_grid, zeros(length(x_grid)) + if ~cdf_on + @series begin + fillrange := distribution_evals + color := DEFAULT_FILL + fillalpha := DEFAULT_ALPHA + x_grid, zeros(length(x_grid)) + end end + @series begin color --> DEFAULT_COLOUR_PDF alpha := 1 label := "" - x_grid, pdf_evals + x_grid, distribution_evals end end From 351efd3fdc89d89f4af5b3ec4e3a64380b04174e Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Mon, 22 Jul 2024 13:35:57 +0100 Subject: [PATCH 05/17] adds plot recipie for sample of a p-box --- src/plotting/plot_recipes.jl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index c9bb8e378..57068a74c 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -203,3 +203,38 @@ end xs, ys end + +### +# Plots for samples of data frames +### + +@recipe function _plot(x::Vector{Interval}) + + if length(unique(x))==1 + return x[1] + else + grid --> DEFAULT_GRID + legend --> DEFAULT_LEGEND + + xlabel --> x[1].name + ylabel --> "cdf" + N_samples = length(x) + + lows = sort(lo.(x)) + his = sort(hi.(x)) + + is = range(0, 1, length = N_samples) + + @series begin + seriestype := :steppre + color --> DEFAULT_COLOUR_LOWER + lows, is + end + + @series begin + seriestype := :steppost + color --> DEFAULT_COLOUR_UPPER + his, is + end + end +end \ No newline at end of file From c5edd4f5e0d5fda6feff0a0c1b3389a6c7ce8def Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Wed, 13 Aug 2025 14:09:02 +0100 Subject: [PATCH 06/17] update Project.toml --- Project.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 41afa98c2..ea0bf3c8a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,15 +1,11 @@ name = "UncertaintyQuantification" uuid = "7183a548-a887-11e9-15ce-a56ab60bad7a" -<<<<<<< HEAD -authors = ["Jasper Behrensdorf ", "Ander Gray "] -version = "0.10.0" -======= + authors = [ "Jasper Behrensdorf ", "Ander Gray ", ] version = "0.12.0" ->>>>>>> master [deps] Bootstrap = "e28b5b4c-05e8-5b66-bc03-6f0c0a0a06e0" From f495fe8f7993f203932edc768fb83d7677f4a6d8 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Wed, 13 Aug 2025 14:26:00 +0100 Subject: [PATCH 07/17] working simple plots after merge --- Project.toml | 9 +++------ demo/plotting/plot_inputs.jl | 28 ++++++++++++++++++++++++++++ src/inputs/imprecise/interval.jl | 12 +++++------- src/plotting/plot_recipes.jl | 8 ++++---- 4 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 demo/plotting/plot_inputs.jl diff --git a/Project.toml b/Project.toml index ea0bf3c8a..d497c1d28 100644 --- a/Project.toml +++ b/Project.toml @@ -1,10 +1,6 @@ name = "UncertaintyQuantification" uuid = "7183a548-a887-11e9-15ce-a56ab60bad7a" - -authors = [ - "Jasper Behrensdorf ", - "Ander Gray ", -] +authors = ["Jasper Behrensdorf ", "Ander Gray "] version = "0.12.0" [deps] @@ -23,8 +19,8 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MeshAdaptiveDirectSearch = "f4d74008-4565-11e9-04bd-4fe404e6a92a" Monomials = "272bfe72-f66c-432f-a94d-600f29493792" Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Optim = "429524aa-4258-5aef-a3af-852621145aeb" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" @@ -49,6 +45,7 @@ MeshAdaptiveDirectSearch = "0.1.0" Monomials = "1.0" Mustache = "1.0" Optim = "1.9.4" +Plots = "1.40.18" Primes = "0.5" QuadGK = "2.11.1" QuasiMonteCarlo = "0.3" diff --git a/demo/plotting/plot_inputs.jl b/demo/plotting/plot_inputs.jl new file mode 100644 index 000000000..50f82d733 --- /dev/null +++ b/demo/plotting/plot_inputs.jl @@ -0,0 +1,28 @@ +using UncertaintyQuantification, Plots + +X1 = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(-1, 2), :σ => 1)), :X1) +X2 = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(-1, 2), :σ => 2)), :X2) +X3 = IntervalVariable(-1, 2, :s) +h = RandomVariable(Normal(0.24, 0.01), :h) # height + +plot(X1) # p-box +plot(X1, color = "red") # red p-box +plot(X3) # Interval +plot(h) # distribution + +inputs = [X1, X2, X3, h] + +plot(inputs) # Everything together + +samples = sample(inputs, 200) + +plot(X1) +plot!(samples.X1) # Samples ecdf samples of X1 + +plot(samples.X1[1], samples.X2[1]) # Plots 2D box + +plot(samples.X1, samples.X2) # Plots bivariate random sets of X1 and X2 + +df = sample(inputs, 200) + + diff --git a/src/inputs/imprecise/interval.jl b/src/inputs/imprecise/interval.jl index f2cf0ccd1..88a146fdd 100644 --- a/src/inputs/imprecise/interval.jl +++ b/src/inputs/imprecise/interval.jl @@ -44,6 +44,11 @@ function bounds(i::Interval) return i.lb, i.ub end +hi(X::Interval) = X.ub +lo(X::Interval) = X.lb +hi(X::Real) = X +lo(X::Real) = X + """ IntervalVariable(lb::Real, ub::Real, name::Symbol) @@ -103,13 +108,6 @@ function bounds(i::IntervalVariable) return i.lb, i.ub end -Base.in(u, i::Interval) = i.lb <= u <= i.ub - -hi(X::Interval) = X.ub -lo(X::Interval) = X.lb -hi(X::Real) = X -lo(X::Real) = X - Base.in(u, i::IntervalVariable) = i.lb <= u <= i.ub mean(i::IntervalVariable) = Interval(i) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 57068a74c..78d513e7e 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -19,7 +19,7 @@ DEFAULT_TICK_SIZE = 12 ### # Plots for UQInputs ### -@recipe function _plot(x::RandomVariable; cdf_on = DEFAULT_CDF) +@recipe function _plot(x::RandomVariable{T}; cdf_on = DEFAULT_CDF) where {T <: UnivariateDistribution} grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND @@ -62,7 +62,7 @@ DEFAULT_TICK_SIZE = 12 end end -@recipe function _plot(x::Interval) +@recipe function _plot(x::IntervalVariable) grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND @@ -107,7 +107,7 @@ end end -@recipe function _plot(x::ProbabilityBox) +@recipe function _plot(x::RandomVariable{T}) where {T <: ProbabilityBox} grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND @@ -216,7 +216,7 @@ end grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND - xlabel --> x[1].name + # xlabel --> x[1].name ylabel --> "cdf" N_samples = length(x) From 0993d66318d41f2ac197310a07fa9a8c8d86cdad Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Thu, 23 Oct 2025 15:34:58 +0100 Subject: [PATCH 08/17] remove plots from deps --- Project.toml | 2 -- src/UncertaintyQuantification.jl | 1 - 2 files changed, 3 deletions(-) diff --git a/Project.toml b/Project.toml index d497c1d28..f906cf512 100644 --- a/Project.toml +++ b/Project.toml @@ -20,7 +20,6 @@ MeshAdaptiveDirectSearch = "f4d74008-4565-11e9-04bd-4fe404e6a92a" Monomials = "272bfe72-f66c-432f-a94d-600f29493792" Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" Optim = "429524aa-4258-5aef-a3af-852621145aeb" -Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" @@ -45,7 +44,6 @@ MeshAdaptiveDirectSearch = "0.1.0" Monomials = "1.0" Mustache = "1.0" Optim = "1.9.4" -Plots = "1.40.18" Primes = "0.5" QuadGK = "2.11.1" QuasiMonteCarlo = "0.3" diff --git a/src/UncertaintyQuantification.jl b/src/UncertaintyQuantification.jl index c59656974..20888bcfb 100644 --- a/src/UncertaintyQuantification.jl +++ b/src/UncertaintyQuantification.jl @@ -21,7 +21,6 @@ using Random using Reexport using Roots using StatsBase -using Plots using RecipesBase @reexport using Distributions From 273e045073085b2960874bfe14556868c928deb0 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Thu, 23 Oct 2025 16:12:06 +0100 Subject: [PATCH 09/17] adds line alpha and width to bivariate intervals --- demo/plotting/plot_inputs.jl | 4 ---- src/plotting/plot_recipes.jl | 21 ++++++++++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/demo/plotting/plot_inputs.jl b/demo/plotting/plot_inputs.jl index 50f82d733..1168c0de9 100644 --- a/demo/plotting/plot_inputs.jl +++ b/demo/plotting/plot_inputs.jl @@ -22,7 +22,3 @@ plot!(samples.X1) # Samples ecdf samples of X1 plot(samples.X1[1], samples.X2[1]) # Plots 2D box plot(samples.X1, samples.X2) # Plots bivariate random sets of X1 and X2 - -df = sample(inputs, 200) - - diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 78d513e7e..7176cec1b 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -9,6 +9,9 @@ DEFAULT_COLOUR_PDF = :blue DEFAULT_COLOUR_UPPER = :red DEFAULT_COLOUR_LOWER = :black +DEFAULT_INTERVAL_WIDTH=1.5 +DEFAULT_INTERVAL_EDGE_ALPHA=1 + DEFAULT_PLOT_RANGE_EXTEND_DENSITY = 0.2 DEFAULT_PLOT_RANGE_EXTEND = 0.2 DEFAULT_PLOT_RANGE_INTERVAL = 0.4 @@ -155,7 +158,7 @@ end cols = ceil(Int, sqrt(N_inputs)) # Calculate the number of columns rows = ceil(Int, N_inputs / cols) # Calculate the number of rows needed - layout := grid(rows, cols) # Create a grid layout + layout := (rows, cols) # Create a grid layout for i = 1:N_inputs @series begin @@ -170,11 +173,17 @@ end ### # Plot a 2D IntervalBox: -@recipe function _plot(x::Interval, y::Interval) #; customcolor = :green, alpha=0.5) +@recipe function _plot(x::Interval, y::Interval) seriesalpha --> DEFAULT_ALPHA seriestype := :shape + label := false + + linecolor --> :black # Explicitly set edge color + linewidth --> DEFAULT_INTERVAL_WIDTH # Make edges more visible + linealpha --> DEFAULT_INTERVAL_EDGE_ALPHA + x = [x.lb, x.ub, x.ub, x.lb] y = [y.lb, y.lb, y.ub, y.ub] @@ -184,8 +193,15 @@ end # Plot a vector of 2D IntervalBoxes: @recipe function _plot(xx::Vector{T}, yy::Vector{T}) where T<:Interval + seriesalpha --> DEFAULT_ALPHA seriestype := :shape + label := false + + linecolor := :black # Explicitly set edge color + linewidth --> DEFAULT_INTERVAL_WIDTH # Make edges more visible + linealpha --> DEFAULT_INTERVAL_EDGE_ALPHA + xs = Float64[] ys = Float64[] @@ -199,7 +215,6 @@ end end - seriesalpha --> DEFAULT_ALPHA xs, ys end From cc319698ba77e60774ed9db91b3fbec5056d7bcd Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Thu, 23 Oct 2025 16:32:24 +0100 Subject: [PATCH 10/17] adds basic plot tests --- test/Project.toml | 1 + test/plotting/plotting.jl | 60 +++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 3 ++ 3 files changed, 64 insertions(+) create mode 100644 test/plotting/plotting.jl diff --git a/test/Project.toml b/test/Project.toml index f36f601fe..af12538e8 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -4,6 +4,7 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" HCubature = "19dc6840-f33b-545b-b366-655c7e3ffd49" HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" diff --git a/test/plotting/plotting.jl b/test/plotting/plotting.jl new file mode 100644 index 000000000..62039e785 --- /dev/null +++ b/test/plotting/plotting.jl @@ -0,0 +1,60 @@ +@testset "Plotting Recipes" begin + + @testset "RandomVariable PDF Plot" begin + rv = RandomVariable(Normal(0, 1), :X) + plt = plot(rv; cdf_on=false) + @test typeof(plt) <: Plots.Plot + @test plt.series_list[1][:seriestype] == :path + @test plt.series_list[2][:seriestype] == :path + end + + @testset "RandomVariable CDF Plot" begin + rv = RandomVariable(Normal(0, 1), :X) + plt = plot(rv; cdf_on=true) + @test typeof(plt) <: Plots.Plot + end + + @testset "IntervalVariable Plot" begin + iv = IntervalVariable(1.0, 2.0, :Y) + plt = plot(iv) + @test typeof(plt) <: Plots.Plot + @test length(plt.series_list) == 3 + @test plt.series_list[1][:fillalpha] == 0.2 + end + + @testset "ProbabilityBox Plot" begin + pb = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(-1, 2), :σ => 2)), :X2) + plt = plot(pb) + @test typeof(plt) <: Plots.Plot + @test length(plt.series_list) == 3 + end + + @testset "Vector of UQInputs Plot" begin + inputs = [RandomVariable(Normal(0,1), :A), IntervalVariable(1.0, 2.0, :B)] + plt = plot(inputs) + @test typeof(plt) <: Plots.Plot + @test plt.layout isa Plots.GridLayout + end + + @testset "2D IntervalBox Plot" begin + x = Interval(1.0, 2.0) + y = Interval(3.0, 4.0) + plt = plot(x, y) + @test typeof(plt) <: Plots.Plot + @test plt.series_list[1][:seriestype] == :shape + end + + @testset "Vector of IntervalBoxes Plot" begin + xs = [Interval(1.0, 2.0), Interval(2.0, 3.0)] + ys = [Interval(3.0, 4.0), Interval(4.0, 5.0)] + plt = plot(xs, ys) + @test typeof(plt) <: Plots.Plot + @test plt.series_list[1][:seriestype] == :shape + end + + @testset "Vector of Intervals Plot" begin + intervals = [Interval(1.0, 2.0), Interval(1.5, 2.5), Interval(2.0, 3.0)] + plt = plot(intervals) + @test typeof(plt) <: Plots.Plot + end +end diff --git a/test/runtests.jl b/test/runtests.jl index eb3e174ef..499938f97 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,6 +7,7 @@ using QuasiMonteCarlo using Random using StatsBase: fit, Histogram, corkendall using Test +using Plots using UncertaintyQuantification include("inputs/empiricaldistribution.jl") @@ -49,6 +50,8 @@ include("simulations/subset.jl") include("util/fourier-transform.jl") +include("plotting/plotting.jl") + if Sys.islinux() HPC = false HPC_account = "HPC_account_1" From 099d6f3cd224f7d36369d0efe1ab74def171fc50 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Tue, 28 Oct 2025 16:04:48 +0000 Subject: [PATCH 11/17] adds default julia colours --- src/plotting/plot_recipes.jl | 176 ++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 76 deletions(-) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 7176cec1b..258d9e77c 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -1,8 +1,9 @@ DEFAULT_ALPHA = 0.2 DEFAULT_LABEL = "" DEFAULT_GRID = false -DEFAULT_LEGEND = false +DEFAULT_LEGEND = true DEFAULT_CDF = false +DEFAULT_FILL_DISTRIBUTION=true DEFAULT_DISTRIBUTION = :pdf DEFAULT_FILL = :gray DEFAULT_COLOUR_PDF = :blue @@ -12,6 +13,8 @@ DEFAULT_COLOUR_LOWER = :black DEFAULT_INTERVAL_WIDTH=1.5 DEFAULT_INTERVAL_EDGE_ALPHA=1 +DEFAULT_DISTRIBUTION_WIDTH=2 + DEFAULT_PLOT_RANGE_EXTEND_DENSITY = 0.2 DEFAULT_PLOT_RANGE_EXTEND = 0.2 DEFAULT_PLOT_RANGE_INTERVAL = 0.4 @@ -22,55 +25,57 @@ DEFAULT_TICK_SIZE = 12 ### # Plots for UQInputs ### -@recipe function _plot(x::RandomVariable{T}; cdf_on = DEFAULT_CDF) where {T <: UnivariateDistribution} - +@recipe function _plot( + x::RandomVariable{T}; cdf_on=DEFAULT_CDF +) where {T<:UnivariateDistribution} grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND - xlabel --> x.name - cdf_on ? ylabel --> "cdf" : ylabel --> "pdf" - - # cdf_on --> DEFAULT_CDF - - lo_grid = quantile(x, 0.001) - hi_grid = quantile(x, 0.999) - - width = hi_grid - lo_grid + label --> String(x.name) + (cdf_on ? ylabel --> "cdf" : ylabel --> "pdf") + seriescolor --> :auto - lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) - hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) + lo = quantile(x, 0.001) + hi = quantile(x, 0.999) + w = hi - lo + lo -= abs(w * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) + hi += abs(w * DEFAULT_PLOT_RANGE_EXTEND_DENSITY) - x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) + xs = range(lo, hi, DEFAULT_PLOT_GRID_NUMBER) + ys = cdf_on ? cdf.(Ref(x), xs) : pdf.(Ref(x), xs) - if cdf_on - distribution_evals = cdf.(Ref(x), x_grid) - else - distribution_evals = pdf.(Ref(x), x_grid) + # Primary line: either cdf or pdf + @series begin + seriestype := :path + fill := nothing + alpha --> 1 + linewidth --> DEFAULT_DISTRIBUTION_WIDTH + label --> String(x.name) + xs, ys end - - if ~cdf_on + + # Optional fill for PDF only: reuse color, don't advance palette + if !cdf_on @series begin - fillrange := distribution_evals - color := DEFAULT_FILL - fillalpha := DEFAULT_ALPHA - x_grid, zeros(length(x_grid)) + primary := false # <-- reuse the color from the primary line + seriestype := :path + fillrange := 0 # fill down to baseline + fillcolor := :match + fillalpha --> DEFAULT_ALPHA + linewidth --> 0 # fill only; no extra line + label := "" + xs, ys end end - - - @series begin - color --> DEFAULT_COLOUR_PDF - alpha := 1 - label := "" - x_grid, distribution_evals - end end @recipe function _plot(x::IntervalVariable) - + # --- plot-level defaults (soft) --- grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND ylabel --> "cdf" - xlabel --> x.name + label --> String(x.name) # single legend entry + + seriescolor --> :auto lo_grid = x.lb hi_grid = x.ub @@ -86,87 +91,111 @@ end cdf_lo = x_grid .>= x.ub cdf_hi = x_grid .> x.lb - + + # Plot upper cdf (primary, inherits colour and label) @series begin - fillrange := cdf_hi - color := DEFAULT_FILL - fillalpha := DEFAULT_ALPHA - x_grid, cdf_lo + seriestype := :path + alpha --> 1 + linewidth --> DEFAULT_DISTRIBUTION_WIDTH + x_grid, cdf_hi end + # Plot lower cdf @series begin - color --> DEFAULT_COLOUR_LOWER - alpha := 1 + primary := false + seriestype := :path + alpha --> 1 label := "" + linewidth --> DEFAULT_DISTRIBUTION_WIDTH x_grid, cdf_lo end + # Plot fill @series begin - color --> DEFAULT_COLOUR_UPPER - alpha := 1 + primary := false + seriestype := :path + fillcolor := :match # match this series' line color + fillrange := cdf_hi + color := DEFAULT_FILL + fillalpha := DEFAULT_ALPHA + linewidth --> 0 # draw fill only here label := "" - x_grid, cdf_hi + x_grid, cdf_lo end - end -@recipe function _plot(x::RandomVariable{T}) where {T <: ProbabilityBox} - +@recipe function _plot(x::RandomVariable{T}) where {T<:ProbabilityBox} + # --- plot-level defaults (soft) --- grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND ylabel --> "cdf" - xlabel --> x.name + label --> String(x.name) # single legend entry + + seriescolor --> :auto lo_grid = quantile(x, 0.001).lb hi_grid = quantile(x, 0.999).ub - width = hi_grid - lo_grid - lo_grid = lo_grid - abs(width * DEFAULT_PLOT_RANGE_EXTEND) hi_grid = hi_grid + abs(width * DEFAULT_PLOT_RANGE_EXTEND) x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) cdf_evals = cdf.(Ref(x), x_grid) + # Plot upper cdf (primary, inherits colour and label) @series begin - fillrange := hi.(cdf_evals) - color := DEFAULT_FILL - fillalpha := DEFAULT_ALPHA - x_grid, lo.(cdf_evals) + seriestype := :path + alpha --> 1 + linewidth --> DEFAULT_DISTRIBUTION_WIDTH + x_grid, hi.(cdf_evals) end + # Plot lower cdf @series begin - color --> DEFAULT_COLOUR_LOWER - alpha := 1 + primary := false + seriestype := :path + alpha --> 1 + linewidth --> DEFAULT_DISTRIBUTION_WIDTH label := "" x_grid, lo.(cdf_evals) end + # Plot fill @series begin - color --> DEFAULT_COLOUR_UPPER - alpha := 1 + primary := false + seriestype := :path + fillcolor := :match # match this series' line color + fillrange := hi.(cdf_evals) + fillalpha --> DEFAULT_ALPHA + linewidth --> 0 # draw fill only here label := "" - x_grid, hi.(cdf_evals) + x_grid, lo.(cdf_evals) end end -@recipe function _plot(x::Vector{T}) where T <: UQInput +using RecipesBase - x_no_params = filter(x -> !isa(x, Parameter), x) +@recipe function _plot(x::Vector{T}) where {T<:UQInput} + # Filter out Parameter objects + x_no_params = filter(xi -> !isa(xi, Parameter), x) - N_inputs = length(x_no_params) + N = length(x_no_params) + cols = ceil(Int, sqrt(N)) + rows = ceil(Int, N / cols) + layout := (rows, cols) - cols = ceil(Int, sqrt(N_inputs)) # Calculate the number of columns - rows = ceil(Int, N_inputs / cols) # Calculate the number of rows needed - layout := (rows, cols) # Create a grid layout + # Choose a grid palette once (users can still override via plot(...; palette=...)) + # palette --> :default # or :default, :Dark2_8, etc. - for i = 1:N_inputs + for i in 1:N @series begin subplot := i + seriescolor --> i # <-- panel i uses the i-th color from the current palette x_no_params[i] end end end + ### # This code is a modified version of the plot recipe from IntervalArithmetic.jl # https://github.com/JuliaIntervals/IntervalArithmetic.jl @@ -174,7 +203,6 @@ end # Plot a 2D IntervalBox: @recipe function _plot(x::Interval, y::Interval) - seriesalpha --> DEFAULT_ALPHA seriestype := :shape @@ -191,8 +219,7 @@ end end # Plot a vector of 2D IntervalBoxes: -@recipe function _plot(xx::Vector{T}, yy::Vector{T}) where T<:Interval - +@recipe function _plot(xx::Vector{T}, yy::Vector{T}) where {T<:Interval} seriesalpha --> DEFAULT_ALPHA seriestype := :shape @@ -206,17 +233,15 @@ end ys = Float64[] # build up coords: # (alternative: use @series) - for i = 1:length(xx) + for i in 1:length(xx) (x, y) = (xx[i], yy[i]) # use NaNs to separate append!(xs, [x.lb, x.ub, x.ub, x.lb, NaN]) append!(ys, [y.lb, y.lb, y.ub, y.ub, NaN]) - end xs, ys - end ### @@ -224,7 +249,6 @@ end ### @recipe function _plot(x::Vector{Interval}) - if length(unique(x))==1 return x[1] else @@ -238,7 +262,7 @@ end lows = sort(lo.(x)) his = sort(hi.(x)) - is = range(0, 1, length = N_samples) + is = range(0, 1, length=N_samples) @series begin seriestype := :steppre @@ -252,4 +276,4 @@ end his, is end end -end \ No newline at end of file +end From 8c060a4ba250d1c014726c6d59d280d8b5bd7f2d Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Tue, 28 Oct 2025 16:13:28 +0000 Subject: [PATCH 12/17] adds option for plot fill --- src/plotting/plot_recipes.jl | 53 ++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 258d9e77c..9ffb0a383 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -4,6 +4,7 @@ DEFAULT_GRID = false DEFAULT_LEGEND = true DEFAULT_CDF = false DEFAULT_FILL_DISTRIBUTION=true +DEFAULT_FILL_IMPRECISE=true DEFAULT_DISTRIBUTION = :pdf DEFAULT_FILL = :gray DEFAULT_COLOUR_PDF = :blue @@ -26,7 +27,7 @@ DEFAULT_TICK_SIZE = 12 # Plots for UQInputs ### @recipe function _plot( - x::RandomVariable{T}; cdf_on=DEFAULT_CDF + x::RandomVariable{T}; cdf_on=DEFAULT_CDF, shade=DEFAULT_FILL_DISTRIBUTION ) where {T<:UnivariateDistribution} grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND @@ -54,7 +55,7 @@ DEFAULT_TICK_SIZE = 12 end # Optional fill for PDF only: reuse color, don't advance palette - if !cdf_on + if !cdf_on && shade @series begin primary := false # <-- reuse the color from the primary line seriestype := :path @@ -68,7 +69,7 @@ DEFAULT_TICK_SIZE = 12 end end -@recipe function _plot(x::IntervalVariable) +@recipe function _plot(x::IntervalVariable; shade=DEFAULT_FILL_IMPRECISE) # --- plot-level defaults (soft) --- grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND @@ -111,20 +112,24 @@ end end # Plot fill - @series begin - primary := false - seriestype := :path - fillcolor := :match # match this series' line color - fillrange := cdf_hi - color := DEFAULT_FILL - fillalpha := DEFAULT_ALPHA - linewidth --> 0 # draw fill only here - label := "" - x_grid, cdf_lo + if shade + @series begin + primary := false + seriestype := :path + fillcolor := :match # match this series' line color + fillrange := cdf_hi + color := DEFAULT_FILL + fillalpha := DEFAULT_ALPHA + linewidth --> 0 # draw fill only here + label := "" + x_grid, cdf_lo + end end end -@recipe function _plot(x::RandomVariable{T}) where {T<:ProbabilityBox} +@recipe function _plot( + x::RandomVariable{T}; shade=DEFAULT_FILL_IMPRECISE +) where {T<:ProbabilityBox} # --- plot-level defaults (soft) --- grid --> DEFAULT_GRID legend --> DEFAULT_LEGEND @@ -161,15 +166,17 @@ end end # Plot fill - @series begin - primary := false - seriestype := :path - fillcolor := :match # match this series' line color - fillrange := hi.(cdf_evals) - fillalpha --> DEFAULT_ALPHA - linewidth --> 0 # draw fill only here - label := "" - x_grid, lo.(cdf_evals) + if shade + @series begin + primary := false + seriestype := :path + fillcolor := :match # match this series' line color + fillrange := hi.(cdf_evals) + fillalpha --> DEFAULT_ALPHA + linewidth --> 0 # draw fill only here + label := "" + x_grid, lo.(cdf_evals) + end end end From 0c93a012206d4ffc80f28d70f70eb9c724e5b1c7 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Tue, 28 Oct 2025 16:21:58 +0000 Subject: [PATCH 13/17] update plotting tests, and add hi/lo tests --- test/inputs/imprecise/interval.jl | 8 ++++++++ test/plotting/plotting.jl | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/inputs/imprecise/interval.jl b/test/inputs/imprecise/interval.jl index 54d202a10..6c0fe10a9 100644 --- a/test/inputs/imprecise/interval.jl +++ b/test/inputs/imprecise/interval.jl @@ -1,3 +1,5 @@ +using UncertaintyQuantification: hi, lo + @testset "Interval" begin name = :l lb = 0.14 @@ -16,6 +18,12 @@ @test !(0.17 ∈ interval) @test sprint(show, interval) == "[0.14, 0.16]" + + @test hi(interval) == interval.ub + @test lo(interval) == interval.lb + + @test hi(2.0) == 2.0 + @test lo(2.0) == 2.0 end @testset "IntervalVariable" begin diff --git a/test/plotting/plotting.jl b/test/plotting/plotting.jl index 62039e785..200743349 100644 --- a/test/plotting/plotting.jl +++ b/test/plotting/plotting.jl @@ -19,7 +19,6 @@ plt = plot(iv) @test typeof(plt) <: Plots.Plot @test length(plt.series_list) == 3 - @test plt.series_list[1][:fillalpha] == 0.2 end @testset "ProbabilityBox Plot" begin From 59b06272d8ae6b2fe471ef2b3afee9fcc74041e7 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Tue, 28 Oct 2025 16:51:24 +0000 Subject: [PATCH 14/17] allow user to change grid layout, and remove xlim setting for intervals --- src/plotting/plot_recipes.jl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/plotting/plot_recipes.jl b/src/plotting/plot_recipes.jl index 9ffb0a383..eeb3ac1d4 100644 --- a/src/plotting/plot_recipes.jl +++ b/src/plotting/plot_recipes.jl @@ -83,11 +83,9 @@ end width = hi_grid - lo_grid - plot_lo = lo_grid - abs(width * DEFAULT_PLOT_RANGE_INTERVAL) + plot_lo = lo_grid - abs(width * DEFAULT_PLOT_RANGE_INTERVAL) # For adding a slight width to the left and right side of the interval plot plot_hi = hi_grid + abs(width * DEFAULT_PLOT_RANGE_INTERVAL) - xlims := (plot_lo, plot_hi) - x_grid = range(lo_grid, hi_grid, DEFAULT_PLOT_GRID_NUMBER) cdf_lo = x_grid .>= x.ub @@ -111,6 +109,16 @@ end x_grid, cdf_lo end + # Add invisible lines so interval bounds don't touch plot boundaries + @series begin + primary := false + seriestype := :path + alpha --> 0 + label := "" + linewidth --> 0 + range(plot_lo, plot_hi, 100), zeros(100) + end + # Plot fill if shade @series begin @@ -180,8 +188,6 @@ end end end -using RecipesBase - @recipe function _plot(x::Vector{T}) where {T<:UQInput} # Filter out Parameter objects x_no_params = filter(xi -> !isa(xi, Parameter), x) @@ -189,7 +195,7 @@ using RecipesBase N = length(x_no_params) cols = ceil(Int, sqrt(N)) rows = ceil(Int, N / cols) - layout := (rows, cols) + layout --> (rows, cols) # Choose a grid palette once (users can still override via plot(...; palette=...)) # palette --> :default # or :default, :Dark2_8, etc. From 5d5ab17d0f0ac728751329b3c191fdecb3cdb8bd Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Wed, 4 Mar 2026 15:14:06 +0000 Subject: [PATCH 15/17] remove series length test --- test/plotting/plotting.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/plotting/plotting.jl b/test/plotting/plotting.jl index 200743349..9d456e0a6 100644 --- a/test/plotting/plotting.jl +++ b/test/plotting/plotting.jl @@ -18,14 +18,12 @@ iv = IntervalVariable(1.0, 2.0, :Y) plt = plot(iv) @test typeof(plt) <: Plots.Plot - @test length(plt.series_list) == 3 end @testset "ProbabilityBox Plot" begin pb = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(-1, 2), :σ => 2)), :X2) plt = plot(pb) @test typeof(plt) <: Plots.Plot - @test length(plt.series_list) == 3 end @testset "Vector of UQInputs Plot" begin From e5b69a4e4b0071a27c52c85850fb9abde46687fc Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Wed, 4 Mar 2026 16:11:43 +0000 Subject: [PATCH 16/17] adds initial plots to docs --- docs/src/manual/gettingstarted.md | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/src/manual/gettingstarted.md b/docs/src/manual/gettingstarted.md index c2da09cb3..c3da62343 100644 --- a/docs/src/manual/gettingstarted.md +++ b/docs/src/manual/gettingstarted.md @@ -47,6 +47,23 @@ to_standard_normal_space!(x, samples) to_physical_space!(x, samples) ``` +Examples of some distributions available from *Distributions.jl* are shown below. + +```@example rv +using Plots #hide + +inputs = [ #hide + RandomVariable(Normal(0, 1), :normal), #hide + RandomVariable(Beta(2, 5), :beta), #hide + RandomVariable(Gamma(2, 1), :gamma), #hide + RandomVariable(Exponential(1), :exponential), #hide + RandomVariable(Uniform(0, 0.3), :uniform), #hide + RandomVariable(LogNormal(0, 1), :lognormal), #hide +]#hide + +plot(inputs)#hide +``` + ### Imprecise Probabilities To represent purely epistemic uncertainty we provide the [`IntervalVariable`](@ref) type. @@ -73,6 +90,21 @@ Truncated p-boxes can be created by passing optional lower and upper bounds to t x = RandomVariable(pbox, :x) ``` +Examples of some parametric p-boxes are shown below. + +```@example rv +inputs = [ #hide + RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(-1, 2), :σ => Interval(0.5, 2))), :normal), #hide + RandomVariable(ProbabilityBox{Beta}(Dict(:α => Interval(1, 3), :β => Interval(2, 5))), :beta), #hide + RandomVariable(ProbabilityBox{Gamma}(Dict(:α => Interval(1, 3), :θ => Interval(0.5, 2))), :gamma), #hide + RandomVariable(ProbabilityBox{Exponential}(Dict(:θ => Interval(0.5, 2))), :exponential), #hide + RandomVariable(ProbabilityBox{Uniform}(Dict(:a => Interval(-1, 0), :b => Interval(1, 2))), :uniform), #hide + RandomVariable(ProbabilityBox{LogNormal}(Dict(:μ => Interval(-1, 1), :σ => Interval(0.5, 2))), :lognormal), #hide +]#hide + +plot(inputs)#hide + +``` ## Dependencies Modelling of dependent variables is exposed through the [`JointDistribution`](@ref). In its simplest form this is a wrapper around any implementation of the `MultivariateDistribution` interface defined by *Distributions.jl*. To construct a [`JointDistribution`](@ref) pass the `MultivariateDistribution` and a `vector{`Symbol`}` of matching dimension. From d53bb90cccd688a3c4874509d2a84f6589e0c172 Mon Sep 17 00:00:00 2001 From: Ander Gray Date: Wed, 4 Mar 2026 16:19:24 +0000 Subject: [PATCH 17/17] adds visualisation of random slicing --- demo/plotting/Visualise_random_slicing.jl | 80 +++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 demo/plotting/Visualise_random_slicing.jl diff --git a/demo/plotting/Visualise_random_slicing.jl b/demo/plotting/Visualise_random_slicing.jl new file mode 100644 index 000000000..8823090c8 --- /dev/null +++ b/demo/plotting/Visualise_random_slicing.jl @@ -0,0 +1,80 @@ +using UncertaintyQuantification, DataFrames, Plots, ColorSchemes +using UncertaintyQuantification: lo, hi + +X1 = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(0, 2), :σ => 4)), :X1) +X2 = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(0, 2), :σ => Interval(5, 5.5))), :X2) + +function limitstate(df) + return 300 .- 2* df.X2 .^2 + df.X1 .+ df.X2 .^3 - 3 *df.X1.^2 .+ df.X1 .*df.X2 +end + +model = Model(limitstate, :g) + +inputs =[X1; X2] + +@time samples = sample(inputs, SobolSampling(1000)) + +propagate_intervals!(model, samples) + +gs = samples.g + +safe = lo.(gs) .>= 0 +fail = hi.(gs) .<= 0 +unsure = lo.(gs) .<= 0 .&& 0 .<= hi.(gs) + +N_grid = 1000 + +xs_physical = range(-15, 12, length = N_grid) +ys_physical = range(-10, 10, length = N_grid) + +XY_physical = [[x; y] for x in xs_physical, y in ys_physical] +XY_physical = reduce(hcat, XY_physical[:]) + +samples_physical = DataFrame(:X1 => XY_physical[1,:], :X2 => XY_physical[2,:]) + +evaluate!(model, samples_physical) +contour(xs_physical, ys_physical, samples_physical.g .<= 0, color = :red, linewidth = 2, levels = 1, label = "g", colorbar =false) + + +plot(samples.X1[safe], samples.X2[safe], alpha = 0.2, color = "blue", label = "safe") +plot!(samples.X1[fail], samples.X2[fail], alpha = 0.2, color = "red", label = "failed") +plot!(samples.X1[unsure], samples.X2[unsure], alpha = 0.2, color = "yellow", label = "indetermined") +contour!(xs_physical, ys_physical, samples_physical.g .<= 0, color = :red, linewidth = 2, levels = 1, label = "g", colorbar =false) + + +### Subset viz, with tighter dists + +X1_ = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(0, 2), :σ => 2)), :X1) +X2_ = RandomVariable(ProbabilityBox{Normal}(Dict(:μ => Interval(-1, 1), :σ => 1.5)), :X2) + +inputs2 = [X1_, X2_] + +ss = SubSetInfinity(1000, 0.1, 10, 0.5) +@time pf_ss, outputs_ss1, outputs_ss2 = probability_of_failure(limitstate, inputs2, RandomSlicing(ss)) + +samples_lo = outputs_ss1[2] +N_levels = maximum(samples_lo.level) + +colour = colormap("RdBu", N_levels) + +p1 = plot(samples_lo.X1[samples_lo.level .== 1], samples_lo.X2[samples_lo.level .==1], color = colour[1], label ="level1", alpha = 0.2) +for i = 2:N_levels + plot!(p1, samples_lo.X1[samples_lo.level .==i], samples_lo.X2[samples_lo.level .==i], color = colour[i], label ="level$i", alpha = 0.2) +end +contour!(p1, xs_physical, ys_physical, samples_physical.g .<= 0, color = :red, linewidth = 2, levels = 1, label = true, colorbar =false, title = "lower bound") + +## Lower bound + +samples_lo = outputs_ss2[2] +N_levels = maximum(samples_lo.level) + +# colour = colormap("RdBu", N_levels) + +p2 = plot(samples_lo.X1[samples_lo.level .== 1], samples_lo.X2[samples_lo.level .==1], color = colour[1], label ="level1", alpha = 0.2) +for i = 2:N_levels + plot!(p2, samples_lo.X1[samples_lo.level .==i], samples_lo.X2[samples_lo.level .==i], color = colour[i], label ="level$i", alpha = 0.2) +end +contour!(p2, xs_physical, ys_physical, samples_physical.g .<= 0, color = :red, linewidth = 2, levels = 1, label = true, colorbar =false, title = "upper bound") + +# Side by side +p = plot(p1, p2, layout = (1, 2), size = (1200, 500)) \ No newline at end of file