Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Primes = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
QuasiMonteCarlo = "8a4e6c94-4038-4cdc-81c3-7e6ffdb2a71b"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Expand Down
24 changes: 24 additions & 0 deletions demo/plotting/plot_inputs.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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
8 changes: 8 additions & 0 deletions src/UncertaintyQuantification.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ using Random
using Reexport
using Roots
using StatsBase
using RecipesBase

@reexport using Distributions

Expand Down Expand Up @@ -231,4 +232,11 @@ include("reliability/probabilityoffailure.jl")
include("reliability/probabilityoffailure_imprecise.jl")
include("sensitivity/sobolindices.jl")

include("plotting/plot_recipes.jl")

include("util/fourier-transform.jl")
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you move these util includes down here? It's probably not to important, but I think they can stay at the top.

Copy link
Contributor

Choose a reason for hiding this comment

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

You defined a lo(X::Real) and hi(X::Real) function but I don't think you use it anywhere.

Do you really need a function for the .lb and .ub of the Interval? To get the bounds of a Vector{Interval} you could instead use getproperty.(X, :lb) and getproperty.(X, :ub).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review 👍 I think you're right

On the last one, I do find it convenient to have hi and lo functions for intervals and reals. It lets you ask for bounds on random sample of inputs, without knowing which elements are interval or real.

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case, keep them 👍

include("util/wrap.jl")
include("util/imprecise.jl")
include("util/kde.jl")

end
5 changes: 5 additions & 0 deletions src/inputs/imprecise/interval.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
255 changes: 255 additions & 0 deletions src/plotting/plot_recipes.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
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
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
DEFAULT_PLOT_GRID_NUMBER = 500
DEFAULT_FONT_SIZE = 18
DEFAULT_TICK_SIZE = 12

###
# Plots for UQInputs
###
@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

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)

if cdf_on
distribution_evals = cdf.(Ref(x), x_grid)
else
distribution_evals = pdf.(Ref(x), x_grid)
end

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, distribution_evals
end
end

@recipe function _plot(x::IntervalVariable)

grid --> DEFAULT_GRID
legend --> DEFAULT_LEGEND
ylabel --> "cdf"
xlabel --> x.name

lo_grid = x.lb
hi_grid = x.ub

width = hi_grid - lo_grid

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

@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::RandomVariable{T}) where {T <: ProbabilityBox}

grid --> DEFAULT_GRID
legend --> DEFAULT_LEGEND
ylabel --> "cdf"
xlabel --> x.name

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)

@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

@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 := (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
###

# Plot a 2D IntervalBox:
@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]

x, y
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[]

# 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

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
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
60 changes: 60 additions & 0 deletions test/plotting/plotting.jl
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading