From 66954ef3ca6c9709456b637d5c460b7fcaae08c4 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:13:07 +1000 Subject: [PATCH 01/34] Add JMcDM to ADRIA dependencies --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index dbe63cd0e..d8dd64500 100644 --- a/Project.toml +++ b/Project.toml @@ -26,6 +26,7 @@ Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +JMcDM = "358108f5-d052-4d0a-8344-d5384e00c0e5" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" From b5fe689ac58dac4e2c4d5e7da63d3dd82d03b0c6 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:17:17 +1000 Subject: [PATCH 02/34] Change ADRIA mcda method names to avoid clash with JMcDM function names --- src/sites/dMCDA.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 00a66521c..26082e8fb 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -635,7 +635,7 @@ end """ - topsis(S::Array{Float64, 2}) + adria_topsis(S::Array{Float64, 2}) Calculates ranks using the aggregation method of the TOPSIS MCDA algorithm. Rank for a particular site is calculated as a ratio @@ -665,7 +665,7 @@ S_p = √{∑(criteria .- NIS)²} 2. calculated site rank score (higher values = higher ranked) 3. site order id """ -function topsis(S::Array{Float64,2})::Array{Union{Float64,Int64},2} +function adria_topsis(S::Array{Float64,2})::Array{Union{Float64,Int64},2} # compute the set of positive ideal solutions for each criteria PIS = maximum(S[:, 2:end], dims=1) @@ -695,7 +695,7 @@ end """ - vikor(S; v=0.5) + adria_vikor(S; v=0.5) Calculates ranks using the aggregation method of the VIKOR MCDA algorithm. Rank for a particular site is calculated as a linear combination of ratios, @@ -738,7 +738,7 @@ Details of this aggregation method in, for example [1] 2. calculated site rank score (higher values = higher ranked) 3. site order id """ -function vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Union{Float64,Int64},2} +function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Union{Float64,Int64},2} F_s = maximum(S[:, 2:end]) From 74f0db69cff47b1bdd32338cb69703a0102bf90c Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:21:51 +1000 Subject: [PATCH 03/34] Changes to retrieve_ranks to allow compatibility with JMcDM functions --- src/sites/dMCDA.jl | 47 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 26082e8fb..a16a6dcb9 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -168,10 +168,7 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup weights = weights[selector] S = S[:, Bool[1, selector...]] - # Skip first column as this holds site index ids - S[:, 2:end] = mcda_normalize(S[:, 2:end]) - S[:, 2:end] .= S[:, 2:end] .* weights' - s_order = mcda_func(S) + s_order = retrieve_ranks(S, weights, mcda_func) last_idx = min(n_site_int, size(s_order, 1)) prefsites = Int.(s_order[1:last_idx, 1]) @@ -599,35 +596,21 @@ function distance_sorting(pref_sites::AbstractArray{Int}, s_order::Matrix{Union{ return rep_sites, rankings end -""" - order_ranking(S::Array{Float64, 2}) - -Uses simple summation as aggregation method for decision criteria. -Then orders sites from highest aggregate score to lowest. - -# Arguments -- `S` : Decision matrix (seeding or shading) - -# Returns -- `s_order` : nsites × 3 matrix with columns - 1. site ids - 2. calculated site rank score (higher values = higher ranked) - 3. site order id -""" -function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} - n::Int64 = size(S, 1) - s_order::Array = Union{Float64,Int64}[zeros(Int, n) zeros(Float64, n) zeros(Int, n)] - - # Simple ranking - add criteria weighted values for each sites - # Third column is derived from the number of sites for situations where - # a subset of sites are being investigated (and so using their site IDs - # will be inappropriate) - @views s_order[:, 1] .= Int.(S[:, 1]) - @views s_order[:, 2] .= sum(S[:, 2:end], dims=2) - - # Reorder ranks (highest to lowest) - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=true) +function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Function) + S[:, 2:end] = mcda_normalize(S[:, 2:end]) + S[:, 2:end] .= S[:, 2:end] .* repeat(weights', size(S[:, 2:end], 1), 1) + s_order = mcda_func(S) + return retrieve_ranks(S, s_order, true) +end +function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector) + fns = repeat([maximum], length(weights)) + results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) + s_order = Union{Float64,Int64}[Int.(site_ids) results.scores 1:size(S, 1)] + return retrieve_ranks(S, s_order, mcda_func[2]) +end +function retrieve_ranks(S::Matrix, s_order::Array{Union{Float64,Int64},2}, rev_val::Bool) + s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) @views s_order[:, 3] .= Int.(1:size(S, 1)) return s_order From 8cd9e8a52be2792d9a45bce4170b78dd5747ce97 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:30:13 +1000 Subject: [PATCH 04/34] Add method struct which allows selection of JMcDM funcs for mcda_func --- src/ecosystem/Ecosystem.jl | 2 +- src/sites/dMCDA.jl | 61 +++++++++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/ecosystem/Ecosystem.jl b/src/ecosystem/Ecosystem.jl index b1cce95fc..fd14dd2cd 100644 --- a/src/ecosystem/Ecosystem.jl +++ b/src/ecosystem/Ecosystem.jl @@ -45,7 +45,7 @@ Base.@kwdef struct Intervention{N,P,P2} <: EcoModel # Integer values have a +1 offset to allow for discrete value mapping # (see `set()` and `map_to_discrete()` methods) # Bounds are defined as floats to maintain type stability - guided::N = Param(0, ptype="integer", bounds=(-1.0, 3.0 + 1.0), dists="unif", + guided::N = Param(0, ptype="integer", bounds=(-1.0, 18.0 + 1.0), dists="unif", name="Guided", description="Choice of MCDA approach.") seed_TA::N = Param(0, ptype="integer", bounds=(0.0, 1000000.0 + 1.0), dists="unif", name="Seeded Tabular Acropora", description="Number of enhanced Tabular Acropora to seed per deployment year.") diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index a16a6dcb9..52ba06365 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -3,6 +3,29 @@ using StatsBase using Distances using Combinatorics +using JMcDM + +global method = [ + [ArasMethod(), true], + [CocosoMethod(), true], + [CodasMethod(), true], + [CoprasMethod(), false], + [EdasMethod(), true], + [GreyMethod(), true], + [MabacMethod(), true], + [MaircaMethod(), false], + [MarcosMethod(), true], + [MooraMethod(), true], + [MoosraMethod(), true], + [PIVMethod(), true], + [PSIMethod(), true], + [ROVMethod(), true], + [SawMethod(), true], + [TopsisMethod(), true], + [VikorMethod(), false], + [WPMMethod(), true], + [WaspasMethod(), true] +] struct DMCDA_vars # {V, I, F, M} where V <: Vector site_ids # ::V @@ -160,7 +183,7 @@ end # Returns - `prefsites` : sites in order of their rankings """ -function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} +function rank_sites!(S, weights, rankings, n_site_int, alg_ind, rank_col)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} # Filter out all non-preferred sites selector = vec(.!all(S[:, 2:end] .== 0, dims=1)) @@ -170,6 +193,19 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup s_order = retrieve_ranks(S, weights, mcda_func) + if in(alg_ind, [1, 2, 3]) + # Skip first column as this holds site index ids + S[:, 2:end] = mcda_normalize(S[:, 2:end]) + S[:, 2:end] .= S[:, 2:end] .* repeat(weights', size(S[:, 2:end], 1), 1) + s_order = mcda_func(S) + elseif in(alg_ind, [16, 17]) + fns = repeat([maximum], length(weights)) + results = mcdm(MCDMSetting(S[:, 2:end], weights, fns), mcda_func[1]) + s_order = Union{Float64,Int64}[Int.(S[:, 1]) results.scores 1:size(S, 1)] + + # Reorder ranks (highest to lowest) + s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=mcda_func[2]) + end last_idx = min(n_site_int, size(s_order, 1)) prefsites = Int.(s_order[1:last_idx, 1]) @@ -179,11 +215,11 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup return prefsites, s_order end -function rank_seed_sites!(S, weights, rankings, n_site_int, mcda_func)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} - rank_sites!(S, weights, rankings, n_site_int, mcda_func, 2) +function rank_seed_sites!(S, weights, rankings, n_site_int, alg_ind)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} + rank_sites!(S, weights, rankings, n_site_int, alg_ind, 2) end -function rank_shade_sites!(S, weights, rankings, n_site_int, mcda_func)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} - rank_sites!(S, weights, rankings, n_site_int, mcda_func, 3) +function rank_shade_sites!(S, weights, rankings, n_site_int, alg_ind)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} + rank_sites!(S, weights, rankings, n_site_int, alg_ind, 3) end """ @@ -482,20 +518,11 @@ function guided_site_selection( SH, wsh = create_shade_matrix(A, max_area, w_shade_conn, w_waves, w_heat, w_predec_shade, w_predec_zones_shade, w_high_cover) end - if alg_ind == 1 - mcda_func = order_ranking - elseif alg_ind == 2 - mcda_func = topsis - elseif alg_ind == 3 - mcda_func = vikor - else - error("Unknown MCDA algorithm selected. Valid options are 1 (Order Ranking), 2 (TOPSIS) and 3 (VIKOR).") - end - if log_seed && isempty(SE) prefseedsites = zeros(Int64, n_site_int) elseif log_seed - prefseedsites, s_order_seed = rank_seed_sites!(SE, wse, rankings, n_site_int, mcda_func) + + prefseedsites, s_order_seed = rank_seed_sites!(SE, wse, rankings, n_site_int, alg_ind) if use_dist != 0 prefseedsites, rankings = distance_sorting(prefseedsites, s_order_seed, d_vars.dist, min_dist, Int64(d_vars.top_n), rankings, 2) end @@ -504,7 +531,7 @@ function guided_site_selection( if log_shade && isempty(SH) prefshadesites = zeros(Int64, n_site_int) elseif log_shade - prefshadesites, s_order_shade = rank_shade_sites!(SH, wsh, rankings, n_site_int, mcda_func) + prefshadesites, s_order_shade = rank_shade_sites!(SH, wsh, rankings, n_site_int, alg_ind) if use_dist != 0 prefshadesites, rankings = distance_sorting(prefshadesites, s_order_shade, d_vars.dist, min_dist, Int64(d_vars.top_n), rankings, 3) end From d47a73512a740d0f29f9a47492a2cf24ce5e764a Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:34:32 +1000 Subject: [PATCH 05/34] Type changes to retrieve_ranks inputs --- src/sites/dMCDA.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 52ba06365..af43225ce 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -623,11 +623,12 @@ function distance_sorting(pref_sites::AbstractArray{Int}, s_order::Matrix{Union{ return rep_sites, rankings end -function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Function) - S[:, 2:end] = mcda_normalize(S[:, 2:end]) - S[:, 2:end] .= S[:, 2:end] .* repeat(weights', size(S[:, 2:end], 1), 1) - s_order = mcda_func(S) - return retrieve_ranks(S, s_order, true) +function retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) + S = mcda_normalize(S) + S .= S .* repeat(weights', size(S, 1), 1) + scores = mcda_func(S) + + return retrieve_ranks(S, scores, true, site_ids) end function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector) fns = repeat([maximum], length(weights)) @@ -636,7 +637,8 @@ function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector) return retrieve_ranks(S, s_order, mcda_func[2]) end -function retrieve_ranks(S::Matrix, s_order::Array{Union{Float64,Int64},2}, rev_val::Bool) +function retrieve_ranks(S::Matrix, scores::Array{Float64}, rev_val::Bool, site_ids::Array{Int64}) + s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) @views s_order[:, 3] .= Int.(1:size(S, 1)) From ab3e8a5e8f1dbd10cb4605ac6ce99cd8fcedccd3 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:39:45 +1000 Subject: [PATCH 06/34] Remove moosra method, needs more research to be implemented properly --- src/ecosystem/Ecosystem.jl | 2 +- src/sites/dMCDA.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ecosystem/Ecosystem.jl b/src/ecosystem/Ecosystem.jl index fd14dd2cd..03868998c 100644 --- a/src/ecosystem/Ecosystem.jl +++ b/src/ecosystem/Ecosystem.jl @@ -45,7 +45,7 @@ Base.@kwdef struct Intervention{N,P,P2} <: EcoModel # Integer values have a +1 offset to allow for discrete value mapping # (see `set()` and `map_to_discrete()` methods) # Bounds are defined as floats to maintain type stability - guided::N = Param(0, ptype="integer", bounds=(-1.0, 18.0 + 1.0), dists="unif", + guided::N = Param(0, ptype="integer", bounds=(-1.0, 21.0 + 1.0), dists="unif", name="Guided", description="Choice of MCDA approach.") seed_TA::N = Param(0, ptype="integer", bounds=(0.0, 1000000.0 + 1.0), dists="unif", name="Seeded Tabular Acropora", description="Number of enhanced Tabular Acropora to seed per deployment year.") diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index af43225ce..1885d6fa9 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -16,7 +16,7 @@ global method = [ [MaircaMethod(), false], [MarcosMethod(), true], [MooraMethod(), true], - [MoosraMethod(), true], + #[MoosraMethod(), true], [PIVMethod(), true], [PSIMethod(), true], [ROVMethod(), true], From 9067d090c8ec22ffb4c904ae178e8b0fe3e0e422 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 4 Apr 2023 10:58:54 +1000 Subject: [PATCH 07/34] Add docs for `retrieve_ranks` function --- src/sites/dMCDA.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 1885d6fa9..9a3ee8d12 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -623,6 +623,21 @@ function distance_sorting(pref_sites::AbstractArray{Int}, s_order::Matrix{Union{ return rep_sites, rankings end + +""" + retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) + +Get location ranks using mcda technique specified in mcda_func, weights and a decision matrix S. + +# Arguments +- `S` : decision matrix containing criteria values for each location (n locations)*(m criteria) +- `weights` : importance weights for each criteria. +- `mcda_func` : function to use for mcda, specified as an element from mcda_methods. +- `site_ids` : array of integers indicating site ids still remaining after filtering. + +# Returns +- `s_order` : [site_ids, criteria values, ranks] +""" function retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) S = mcda_normalize(S) S .= S .* repeat(weights', size(S, 1), 1) From 614c6118369d5dfdfd2e06f8a2dea355f492c7dd Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 11:45:35 +1000 Subject: [PATCH 08/34] Add ADRIA mcda methods to methods vector --- src/sites/dMCDA.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 9a3ee8d12..b5b8d9192 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -6,6 +6,9 @@ using Combinatorics using JMcDM global method = [ + order_ranking, + adria_vikor, + adria_topsis, [ArasMethod(), true], [CocosoMethod(), true], [CodasMethod(), true], From 8a97cd0b622630716de73529fa84f9ce8c6424ae Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 12:00:24 +1000 Subject: [PATCH 09/34] Remove ranking from inside ADRIA mcda ranking functions and remove reference to site ids in decision matrix --- src/sites/dMCDA.jl | 128 +++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 68 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index b5b8d9192..24289afa5 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -225,6 +225,43 @@ function rank_shade_sites!(S, weights, rankings, n_site_int, alg_ind)::Tuple{Vec rank_sites!(S, weights, rankings, n_site_int, alg_ind, 3) end +""" + retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) + +Get location ranks using mcda technique specified in mcda_func, weights and a decision matrix S. + +# Arguments +- `S` : decision matrix containing criteria values for each location (n locations)*(m criteria) +- `weights` : importance weights for each criteria. +- `mcda_func` : function to use for mcda, specified as an element from mcda_methods. +- `site_ids` : array of integers indicating site ids still remaining after filtering. + +# Returns +- `s_order` : [site_ids, criteria values, ranks] +""" +function retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) + S = mcda_normalize(S) + S .= S .* repeat(weights', size(S, 1), 1) + scores = mcda_func(S) + + return retrieve_ranks(S, scores, true, site_ids) +end +function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector) + fns = repeat([maximum], length(weights)) + results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) + s_order = Union{Float64,Int64}[Int.(site_ids) results.scores 1:size(S, 1)] + + return retrieve_ranks(S, s_order, mcda_func[2]) +end +function retrieve_ranks(S::Matrix, scores::Array{Float64}, rev_val::Bool, site_ids::Array{Int64}) + s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] + s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) + @views s_order[:, 3] .= Int.(1:size(S, 1)) + + return s_order +end + + """ create_decision_matrix(site_ids, in_conn, out_conn, sum_cover, max_cover, area, wave_stress, heat_stress, predec, risk_tol) @@ -626,44 +663,22 @@ function distance_sorting(pref_sites::AbstractArray{Int}, s_order::Matrix{Union{ return rep_sites, rankings end - """ - retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) + order_ranking(S::Array{Float64, 2}) -Get location ranks using mcda technique specified in mcda_func, weights and a decision matrix S. +Uses simple summation as aggregation method for decision criteria. +Then orders sites from highest aggregate score to lowest. # Arguments -- `S` : decision matrix containing criteria values for each location (n locations)*(m criteria) -- `weights` : importance weights for each criteria. -- `mcda_func` : function to use for mcda, specified as an element from mcda_methods. -- `site_ids` : array of integers indicating site ids still remaining after filtering. +- `S` : Decision matrix (seeding or shading) # Returns -- `s_order` : [site_ids, criteria values, ranks] +- aggregate score for ranking. """ -function retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) - S = mcda_normalize(S) - S .= S .* repeat(weights', size(S, 1), 1) - scores = mcda_func(S) - - return retrieve_ranks(S, scores, true, site_ids) -end -function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector) - fns = repeat([maximum], length(weights)) - results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) - s_order = Union{Float64,Int64}[Int.(site_ids) results.scores 1:size(S, 1)] - - return retrieve_ranks(S, s_order, mcda_func[2]) -end -function retrieve_ranks(S::Matrix, scores::Array{Float64}, rev_val::Bool, site_ids::Array{Int64}) - s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) - @views s_order[:, 3] .= Int.(1:size(S, 1)) - - return s_order +function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} + return sum(S, dims=2) end - """ adria_topsis(S::Array{Float64, 2}) @@ -690,37 +705,25 @@ S_p = √{∑(criteria .- NIS)²} - `S` : Decision matrix (seeding or shading) # Returns -- `s_order` : - 1. site ids - 2. calculated site rank score (higher values = higher ranked) - 3. site order id +- `C` : aggregate score for ranking. + """ function adria_topsis(S::Array{Float64,2})::Array{Union{Float64,Int64},2} # compute the set of positive ideal solutions for each criteria - PIS = maximum(S[:, 2:end], dims=1) + PIS = maximum(S, dims=1) # compute the set of negative ideal solutions for each criteria - NIS = minimum(S[:, 2:end], dims=1) + NIS = minimum(S, dims=1) # calculate separation distance from the ideal and non-ideal solutions - S_p = sqrt.(sum((S[:, 2:end] .- PIS) .^ 2, dims=2)) - S_n = sqrt.(sum((S[:, 2:end] .- NIS) .^ 2, dims=2)) + S_p = sqrt.(sum((S .- PIS) .^ 2, dims=2)) + S_n = sqrt.(sum((S .- NIS) .^ 2, dims=2)) # final ranking measure of relative closeness C C = S_n ./ (S_p + S_n) - # Create matrix where rank ids are integers (for use as indexers later) - # Third column is derived from the number of sites for situations where - # a subset of sites are being investigated (and so using their site IDs - # will be inappropriate) - s_order = Union{Float64,Int64}[Int.(S[:, 1]) C 1:size(S, 1)] - - # Reorder ranks (highest to lowest) - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=true) - @views s_order[:, 3] .= Int.(1:size(S, 1)) - - return s_order + return C end @@ -763,36 +766,25 @@ Details of this aggregation method in, for example [1] - `v` : Real # Returns -- `s_order` : - 1. site ids - 2. calculated site rank score (higher values = higher ranked) - 3. site order id +- `Q` : aggregate score for ranking. """ function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Union{Float64,Int64},2} - F_s = maximum(S[:, 2:end]) + F_s = maximum(S) # Compute utility of the majority Sr (Manhatten Distance) # Compute individual regret R (Chebyshev distance) - sr_arg = (F_s .- S[:, 2:end]) - Sr = [S[:, 1] sum(sr_arg, dims=2)] - R = [S[:, 1] maximum(sr_arg, dims=2)] + sr_arg = (F_s .- S) + Sr = sum(sr_arg, dims=2) + R = maximum(sr_arg, dims=2) # Compute the VIKOR compromise Q - S_s, S_h = maximum(Sr[:, 2]), minimum(Sr[:, 2]) - R_s, R_h = maximum(R[:, 2]), minimum(R[:, 2]) - Q = @. v * (Sr[:, 2] - S_h) / (S_s - S_h) + (1 - v) * (R[:, 2] - R_h) / (R_s - R_h) + S_s, S_h = maximum(Sr), minimum(Sr) + R_s, R_h = maximum(R), minimum(R) + Q = @. v * (Sr - S_h) / (S_s - S_h) + (1 - v) * (R - R_h) / (R_s - R_h) Q .= 1.0 .- Q # Invert rankings so higher values = higher rank - # Create matrix where rank ids are integers (for use as indexers later) - # Third column is necessary as a subset of sites will not match their Index IDs. - s_order = Union{Float64,Int64}[Int.(S[:, 1]) Q zeros(size(Q, 1))] - - # sort Q by rank score in descending order - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=true) - @views s_order[:, 3] .= Int.(1:size(S, 1)) - - return s_order + return Q end """ From c54bf1dd0901449370bdf0e001364b3a3144acda Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 13:50:15 +1000 Subject: [PATCH 10/34] Remove elseif statements for deciding mcda method and select from method vector instead --- src/sites/dMCDA.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 24289afa5..fb7e620d9 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -509,6 +509,7 @@ function guided_site_selection( # site_id, seeding rank, shading rank rankings = Int64[site_ids zeros(Int64, n_sites) zeros(Int64, n_sites)] + mcda_func = mcda_methods[alg_ind] # work out which priority predecessors are connected to priority sites predec::Matrix{Float64} = zeros(n_sites, 3) From a5d26d138faaa722ceba7ea4fed550c09d0baa1e Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 13:54:52 +1000 Subject: [PATCH 11/34] Changes to unify mcda method types. Remove site_ids from S and use only in final sorting step --- src/sites/dMCDA.jl | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index fb7e620d9..5f5a936a5 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -188,7 +188,7 @@ end """ function rank_sites!(S, weights, rankings, n_site_int, alg_ind, rank_col)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} # Filter out all non-preferred sites - selector = vec(.!all(S[:, 2:end] .== 0, dims=1)) + selector = vec(.!all(S .== 0, dims=1)) # weights in order of: in_conn, out_conn, wave, heat, predecessors, low cover weights = weights[selector] @@ -666,6 +666,15 @@ end """ order_ranking(S::Array{Float64, 2}) +function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Function, site_ids::Array{Int64}) + S = mcda_normalize(S) + S .= S .* repeat(weights', size(S, 1), 1) + scores = mcda_func(S) + return retrieve_ranks(S, scores, true, site_ids) +end +function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector, site_ids::Array{Int64}) + fns = repeat([maximum], length(weights)) + results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) Uses simple summation as aggregation method for decision criteria. Then orders sites from highest aggregate score to lowest. @@ -678,7 +687,12 @@ Then orders sites from highest aggregate score to lowest. """ function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} return sum(S, dims=2) + return retrieve_ranks(S, results.scores, mcda_func[2], site_ids) end +function retrieve_ranks(S::Matrix, scores::Array{Float64}, rev_val::Bool, site_ids::Array{Int64}) + s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] + s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) + @views s_order[:, 3] .= Int.(1:size(S, 1)) """ adria_topsis(S::Array{Float64, 2}) From b4a1f116c620580b1c238cfd54cd91ac65579946 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 14:14:29 +1000 Subject: [PATCH 12/34] Changes to rank_sites to allow simple interchange between ADRIA and JMcDM funcs --- src/sites/dMCDA.jl | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 5f5a936a5..fbdc53ef0 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -186,29 +186,16 @@ end # Returns - `prefsites` : sites in order of their rankings """ -function rank_sites!(S, weights, rankings, n_site_int, alg_ind, rank_col)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} +function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} # Filter out all non-preferred sites - selector = vec(.!all(S .== 0, dims=1)) + selector = vec(.!all(S[:, 2:end] .== 0, dims=1)) # weights in order of: in_conn, out_conn, wave, heat, predecessors, low cover weights = weights[selector] S = S[:, Bool[1, selector...]] - s_order = retrieve_ranks(S, weights, mcda_func) + s_order = retrieve_ranks(S[:, 2:end], weights, mcda_func, S[:, 1]) - if in(alg_ind, [1, 2, 3]) - # Skip first column as this holds site index ids - S[:, 2:end] = mcda_normalize(S[:, 2:end]) - S[:, 2:end] .= S[:, 2:end] .* repeat(weights', size(S[:, 2:end], 1), 1) - s_order = mcda_func(S) - elseif in(alg_ind, [16, 17]) - fns = repeat([maximum], length(weights)) - results = mcdm(MCDMSetting(S[:, 2:end], weights, fns), mcda_func[1]) - s_order = Union{Float64,Int64}[Int.(S[:, 1]) results.scores 1:size(S, 1)] - - # Reorder ranks (highest to lowest) - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=mcda_func[2]) - end last_idx = min(n_site_int, size(s_order, 1)) prefsites = Int.(s_order[1:last_idx, 1]) @@ -509,7 +496,6 @@ function guided_site_selection( # site_id, seeding rank, shading rank rankings = Int64[site_ids zeros(Int64, n_sites) zeros(Int64, n_sites)] - mcda_func = mcda_methods[alg_ind] # work out which priority predecessors are connected to priority sites predec::Matrix{Float64} = zeros(n_sites, 3) From cafc1ad010f3bb045bdf55c6754affc220bbccd6 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 14:15:24 +1000 Subject: [PATCH 13/34] Change location of rank_sites! --- src/sites/dMCDA.jl | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index fbdc53ef0..8121edf85 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -205,13 +205,6 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup return prefsites, s_order end -function rank_seed_sites!(S, weights, rankings, n_site_int, alg_ind)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} - rank_sites!(S, weights, rankings, n_site_int, alg_ind, 2) -end -function rank_shade_sites!(S, weights, rankings, n_site_int, alg_ind)::Tuple{Vector{Int64},Matrix{Union{Float64,Int64}}} - rank_sites!(S, weights, rankings, n_site_int, alg_ind, 3) -end - """ retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) @@ -652,15 +645,6 @@ end """ order_ranking(S::Array{Float64, 2}) -function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Function, site_ids::Array{Int64}) - S = mcda_normalize(S) - S .= S .* repeat(weights', size(S, 1), 1) - scores = mcda_func(S) - return retrieve_ranks(S, scores, true, site_ids) -end -function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector, site_ids::Array{Int64}) - fns = repeat([maximum], length(weights)) - results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) Uses simple summation as aggregation method for decision criteria. Then orders sites from highest aggregate score to lowest. @@ -673,12 +657,7 @@ Then orders sites from highest aggregate score to lowest. """ function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} return sum(S, dims=2) - return retrieve_ranks(S, results.scores, mcda_func[2], site_ids) end -function retrieve_ranks(S::Matrix, scores::Array{Float64}, rev_val::Bool, site_ids::Array{Int64}) - s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) - @views s_order[:, 3] .= Int.(1:size(S, 1)) """ adria_topsis(S::Array{Float64, 2}) From d2bc7d750188a654f3ff6984ab6a5b5f5f6f4297 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 14:30:50 +1000 Subject: [PATCH 14/34] Put mcda methods for ADRIA into separate file so they can be called in methods vector --- src/ADRIA.jl | 6 +- src/sites/mcda_methods.jl | 124 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 src/sites/mcda_methods.jl diff --git a/src/ADRIA.jl b/src/ADRIA.jl index ac8d9e23b..47a4a09e5 100644 --- a/src/ADRIA.jl +++ b/src/ADRIA.jl @@ -37,6 +37,7 @@ include("ExtInterface/ADRIA/Domain.jl") include("io/inputs.jl") include("sites/connectivity.jl") +include("sites/mcda_methods.jl") include("sites/dMCDA.jl") include("interventions/seeding.jl") @@ -81,12 +82,11 @@ if ccall(:jl_generating_output, Cint, ()) == 1 Base.precompile(Tuple{typeof(combine_results),Vector{String}}) # time: 4.0178256 Base.precompile(Tuple{typeof(growthODE),Matrix{Float64},Matrix{Float64},NamedTuple{(:r, :k, :mb, :comp, :sm_comp, :small_massives, :small, :mid, :large, :acr_5_11, :acr_6_12, :rec, :sigma, :M_sm, :sXr, :X_mb, :cover),Tuple{Matrix{Float64},Vector{Float64},Matrix{Float64},Float64,Matrix{Float64},SVector{3,Int64},SVector{6,Int64},SVector{19,Int64},SVector{4,Int64},SVector{2,Int64},SVector{2,Int64},Matrix{Float64},Matrix{Float64},Matrix{Float64},Matrix{Float64},Matrix{Float64},Vector{Float64}}},Float64}) # time: 1.4354926 Base.precompile(Tuple{typeof(combine_results),ResultSet{String,Vector{Any},Vector{Any},Vector{Float64},NamedDimsArray{(:timesteps, :sites, :intervention, :scenarios),Float32,4,ZArray{Float32,4,Zarr.BloscCompressor,DirectoryStore}},NamedDimsArray{(:timesteps, :coral_id, :sites, :scenarios),Float32,4,ZArray{Float32,4,Zarr.BloscCompressor,DirectoryStore}},NamedDimsArray{(:timesteps, :sites, :scenarios),Float32,3,ZArray{Float32,3,Zarr.BloscCompressor,DirectoryStore}},Dict{String,AbstractArray},DataFrame}}) # time: 0.9439985 - Base.precompile(Tuple{typeof(rank_sites!),Matrix{Float64},Vector{Float64},Matrix{Int64},Int64,typeof(topsis),Int64}) # time: 0.3518593 - Base.precompile(Tuple{typeof(rank_sites!),Matrix{Float64},Vector{Float64},Matrix{Int64},Int64,typeof(vikor),Int64}) # time: 0.3170264 + Base.precompile(Tuple{typeof(rank_sites!),Matrix{Float64},Vector{Float64},Matrix{Int64},Int64,typeof(adria_topsis),Int64}) # time: 0.3518593 + Base.precompile(Tuple{typeof(rank_sites!),Matrix{Float64},Vector{Float64},Matrix{Int64},Int64,typeof(adria_vikor),Int64}) # time: 0.3170264 Base.precompile(Tuple{typeof(scenario_attributes),String,String,Vector{String},String,EnvLayer{String,Vector{Int64}},SimConstants,Vector{String},Vector{Float64},Vector{Float64},Vector{Tuple{Float64,Float64}}}) # time: 0.2140636 Base.precompile(Tuple{typeof(model_spec),Model}) # time: 0.1997914 Base.precompile(Tuple{typeof(bleaching_mortality!),Matrix{Float64},Matrix{Float64},Vector{Float64},Int64,Vector{Float64},Vector{Float64},Vector{Float64},Vector{Float64},Float64}) # time: 0.1940948 - Base.precompile(Tuple{typeof(rank_seed_sites!),Matrix{Float64},Vector{Float64},Matrix{Int64},Int64,Function}) # time: 0.1931881 Base.precompile(Tuple{typeof(create_decision_matrix),Vector{Int64},Vector{Float64},Vector{Float64},Vector{Float64},Vector{Float64},Vector{Float64},Vector{Float64},Vector{Float64},Vector{Float64},Matrix{Float64},Matrix{Float64},Float64}) # time: 0.1929096 Base.precompile(Tuple{typeof(scenario_attributes),String,String,Vector{String},String,EnvLayer{String,Vector{Any}},Dict{String,Any},Vector{Any},Vector{Float64},Vector{Float64},Vector{Any}}) # time: 0.1755622 Base.precompile(Tuple{typeof(proportional_adjustment!),Matrix{Float64},Vector{Float64},Vector{Float64}}) # time: 0.1680073 diff --git a/src/sites/mcda_methods.jl b/src/sites/mcda_methods.jl new file mode 100644 index 000000000..46e00fd9c --- /dev/null +++ b/src/sites/mcda_methods.jl @@ -0,0 +1,124 @@ + +""" + order_ranking(S::Array{Float64, 2}) + +Uses simple summation as aggregation method for decision criteria. +Then orders sites from highest aggregate score to lowest. + +# Arguments +- `S` : Decision matrix (seeding or shading) + +# Returns +- aggregate score for ranking. +""" +function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} + return sum(S, dims=2) +end + +""" + adria_topsis(S::Array{Float64, 2}) + +Calculates ranks using the aggregation method of the TOPSIS MCDA algorithm. +Rank for a particular site is calculated as a ratio + + C = S_n/(S_p + S_n) + +S_n = √{∑(criteria .- NIS)²} + is the squareroot of the summed differences between the criteria for a site and the + Negative Ideal Solution (NIS), or worst performing site in each criteria. +S_p = √{∑(criteria .- NIS)²} + is the squareroot of the summed differences between the criteria for a site and the + Positive Ideal Solution (PIS), or best performing site in each criteria. + + Details of this aggregation method in, for example [1]. + +# References +1. Opricovic, Serafim & Tzeng, Gwo-Hshiung. (2004) European Journal of Operational Research. + Vol. 156. pp. 445. + https://doi.org/10.1016/S0377-2217(03)00020-1. + +# Arguments +- `S` : Decision matrix (seeding or shading) + +# Returns +- `C` : aggregate score for ranking. + +""" +function adria_topsis(S::Array{Float64,2})::Array{Union{Float64,Int64},2} + + # compute the set of positive ideal solutions for each criteria + PIS = maximum(S, dims=1) + + # compute the set of negative ideal solutions for each criteria + NIS = minimum(S, dims=1) + + # calculate separation distance from the ideal and non-ideal solutions + S_p = sqrt.(sum((S .- PIS) .^ 2, dims=2)) + S_n = sqrt.(sum((S .- NIS) .^ 2, dims=2)) + + # final ranking measure of relative closeness C + C = S_n ./ (S_p + S_n) + + return C +end + + +""" + adria_vikor(S; v=0.5) + +Calculates ranks using the aggregation method of the VIKOR MCDA algorithm. +Rank for a particular site is calculated as a linear combination of ratios, +weighted by v: + Q = v(Sr - S_h) / (S_s - S_h) + (1 - v)(R - R_h) / (R_s - R_h) + +where +- Sr = ∑(PIS-criteria) for each site, summed over criteria. +- R = max(PIS-criteria) for each site, with the max over criteria. +- S_h = min(∑(PIS-criteria)) over sites, the minimum summed distance from + the positive ideal solution. +- S_s = max(∑(PIS-criteria)) over sites, maximum summed distance from + the positive ideal solution. +- R_h = min(max(PIS-criteria)) over sites, the minimum max distance from + the positive ideal solution. +- R_s = max(max(PIS-criteria)) over sites, the maximum max distance from + the positive ideal solution. +- v = weighting, representing different decision-making strategies, + or level of compromise between utility (overall performance) + and regret (risk of performing very badly in one criteria despite + exceptional performance in others) + - v = 0.5 is consensus + - v < 0.5 is minimal regret + - v > 0.5 is max group utility (majority rules) + +Details of this aggregation method in, for example [1] + +# References +1. Alidrisi H. (2021) Journal of Risk and Financial Management. + Vol. 14. No. 6. pp. 271. + https://doi.org/10.3390/jrfm14060271 + +# Arguments +- `S` : Matrix +- `v` : Real + +# Returns +- `Q` : aggregate score for ranking. +""" +function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Union{Float64,Int64},2} + + F_s = maximum(S) + + # Compute utility of the majority Sr (Manhatten Distance) + # Compute individual regret R (Chebyshev distance) + sr_arg = (F_s .- S) + Sr = sum(sr_arg, dims=2) + R = maximum(sr_arg, dims=2) + + # Compute the VIKOR compromise Q + S_s, S_h = maximum(Sr), minimum(Sr) + R_s, R_h = maximum(R), minimum(R) + Q = @. v * (Sr - S_h) / (S_s - S_h) + (1 - v) * (R - R_h) / (R_s - R_h) + Q .= 1.0 .- Q # Invert rankings so higher values = higher rank + + return Q +end \ No newline at end of file From 7f5885b81a0845a80b3dc15f85ec63753dffe562 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 14:31:37 +1000 Subject: [PATCH 15/34] Change how mcda_func is used in rank_sites, remove mcda method funcs --- src/sites/dMCDA.jl | 129 ++------------------------------------------- 1 file changed, 3 insertions(+), 126 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 8121edf85..282fed517 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -4,6 +4,7 @@ using StatsBase using Distances using Combinatorics using JMcDM +using ADRIA: order_ranking, adria_vikor, adria_topsis global method = [ order_ranking, @@ -541,8 +542,7 @@ function guided_site_selection( if log_seed && isempty(SE) prefseedsites = zeros(Int64, n_site_int) elseif log_seed - - prefseedsites, s_order_seed = rank_seed_sites!(SE, wse, rankings, n_site_int, alg_ind) + prefseedsites, s_order_seed = rank_sites!(SE, wse, rankings, n_site_int, mcda_func, 2) if use_dist != 0 prefseedsites, rankings = distance_sorting(prefseedsites, s_order_seed, d_vars.dist, min_dist, Int64(d_vars.top_n), rankings, 2) end @@ -551,7 +551,7 @@ function guided_site_selection( if log_shade && isempty(SH) prefshadesites = zeros(Int64, n_site_int) elseif log_shade - prefshadesites, s_order_shade = rank_shade_sites!(SH, wsh, rankings, n_site_int, alg_ind) + prefshadesites, s_order_shade = rank_shade_sites!(SH, wsh, rankings, n_site_int, mcda_func, 3) if use_dist != 0 prefshadesites, rankings = distance_sorting(prefshadesites, s_order_shade, d_vars.dist, min_dist, Int64(d_vars.top_n), rankings, 3) end @@ -643,129 +643,6 @@ function distance_sorting(pref_sites::AbstractArray{Int}, s_order::Matrix{Union{ return rep_sites, rankings end -""" - order_ranking(S::Array{Float64, 2}) - -Uses simple summation as aggregation method for decision criteria. -Then orders sites from highest aggregate score to lowest. - -# Arguments -- `S` : Decision matrix (seeding or shading) - -# Returns -- aggregate score for ranking. -""" -function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} - return sum(S, dims=2) -end - -""" - adria_topsis(S::Array{Float64, 2}) - -Calculates ranks using the aggregation method of the TOPSIS MCDA algorithm. -Rank for a particular site is calculated as a ratio - - C = S_n/(S_p + S_n) - -S_n = √{∑(criteria .- NIS)²} - is the squareroot of the summed differences between the criteria for a site and the - Negative Ideal Solution (NIS), or worst performing site in each criteria. -S_p = √{∑(criteria .- NIS)²} - is the squareroot of the summed differences between the criteria for a site and the - Positive Ideal Solution (PIS), or best performing site in each criteria. - - Details of this aggregation method in, for example [1]. - -# References -1. Opricovic, Serafim & Tzeng, Gwo-Hshiung. (2004) European Journal of Operational Research. - Vol. 156. pp. 445. - https://doi.org/10.1016/S0377-2217(03)00020-1. - -# Arguments -- `S` : Decision matrix (seeding or shading) - -# Returns -- `C` : aggregate score for ranking. - -""" -function adria_topsis(S::Array{Float64,2})::Array{Union{Float64,Int64},2} - - # compute the set of positive ideal solutions for each criteria - PIS = maximum(S, dims=1) - - # compute the set of negative ideal solutions for each criteria - NIS = minimum(S, dims=1) - - # calculate separation distance from the ideal and non-ideal solutions - S_p = sqrt.(sum((S .- PIS) .^ 2, dims=2)) - S_n = sqrt.(sum((S .- NIS) .^ 2, dims=2)) - - # final ranking measure of relative closeness C - C = S_n ./ (S_p + S_n) - - return C -end - - -""" - adria_vikor(S; v=0.5) - -Calculates ranks using the aggregation method of the VIKOR MCDA algorithm. -Rank for a particular site is calculated as a linear combination of ratios, -weighted by v: - Q = v(Sr - S_h) / (S_s - S_h) + (1 - v)(R - R_h) / (R_s - R_h) - -where -- Sr = ∑(PIS-criteria) for each site, summed over criteria. -- R = max(PIS-criteria) for each site, with the max over criteria. -- S_h = min(∑(PIS-criteria)) over sites, the minimum summed distance from - the positive ideal solution. -- S_s = max(∑(PIS-criteria)) over sites, maximum summed distance from - the positive ideal solution. -- R_h = min(max(PIS-criteria)) over sites, the minimum max distance from - the positive ideal solution. -- R_s = max(max(PIS-criteria)) over sites, the maximum max distance from - the positive ideal solution. -- v = weighting, representing different decision-making strategies, - or level of compromise between utility (overall performance) - and regret (risk of performing very badly in one criteria despite - exceptional performance in others) - - v = 0.5 is consensus - - v < 0.5 is minimal regret - - v > 0.5 is max group utility (majority rules) - -Details of this aggregation method in, for example [1] - -# References -1. Alidrisi H. (2021) Journal of Risk and Financial Management. - Vol. 14. No. 6. pp. 271. - https://doi.org/10.3390/jrfm14060271 - -# Arguments -- `S` : Matrix -- `v` : Real - -# Returns -- `Q` : aggregate score for ranking. -""" -function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Union{Float64,Int64},2} - - F_s = maximum(S) - - # Compute utility of the majority Sr (Manhatten Distance) - # Compute individual regret R (Chebyshev distance) - sr_arg = (F_s .- S) - Sr = sum(sr_arg, dims=2) - R = maximum(sr_arg, dims=2) - - # Compute the VIKOR compromise Q - S_s, S_h = maximum(Sr), minimum(Sr) - R_s, R_h = maximum(R), minimum(R) - Q = @. v * (Sr - S_h) / (S_s - S_h) + (1 - v) * (R - R_h) / (R_s - R_h) - Q .= 1.0 .- Q # Invert rankings so higher values = higher rank - - return Q -end """ run_site_selection(domain::Domain, scenarios::DataFrame, sum_cover::AbstractArray, area_to_seed::Float64, time_step::Int64) From 4eb7cc27fabcc51741b787f39f9595b9e693ece4 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 16:09:50 +1000 Subject: [PATCH 16/34] Fix rank_sites! documentation --- src/sites/dMCDA.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 282fed517..83578fcba 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -173,15 +173,14 @@ function align_rankings!(rankings::Array, s_order::Matrix, col::Int64)::Nothing end """ - rank_sites!(S, weights, rankings, n_site_int, rank_col) - rank_seed_sites!(S, weights, rankings, n_site_int) - rank_shade_sites!(S, weights, rankings, n_site_int) + rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col) # Arguments - `S` : Matrix, Site preference values - `weights` : weights to apply - `rankings` : vector of site ranks to update - `n_site_int` : number of sites to select for interventions +- `mcda_func` : element of method, designates mcda method to use - `rank_col` : column to fill with rankings (2 for seed, 3 for shade) # Returns From 5e946b4dff9d1e53b0dcd9b295cdd4949f065fdd Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 16:10:46 +1000 Subject: [PATCH 17/34] Fix retrieve_ranks docs and changes to input types to unify across JMcDM and adria mcda method types --- src/sites/dMCDA.jl | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 83578fcba..6de004cc0 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -206,35 +206,40 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup end """ - retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) + retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Function, site_ids::Vector) + retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Vector{Any}, site_ids::Vector) + retrieve_ranks(S::Matrix, scores::Vector, rev_val::Bool, site_ids::Vector) Get location ranks using mcda technique specified in mcda_func, weights and a decision matrix S. # Arguments - `S` : decision matrix containing criteria values for each location (n locations)*(m criteria) - `weights` : importance weights for each criteria. -- `mcda_func` : function to use for mcda, specified as an element from mcda_methods. -- `site_ids` : array of integers indicating site ids still remaining after filtering. +- `mcda_func` : function/[function bool] array to use for mcda, specified as an element from method. +- `site_ids` : array of site ids still remaining after filtering. +- `scores` : set of scores derived from applying an mcda ranking method. +- `rev_val` : Boolean indicating whether a mcda method is maximising score (true), or minimising (false). # Returns - `s_order` : [site_ids, criteria values, ranks] """ -function retrieve_ranks(S::Matrix, weights::Array{Float64}, mcda_func::Function, site_ids::Array{Int64}) +function retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Function, site_ids::Vector) S = mcda_normalize(S) S .= S .* repeat(weights', size(S, 1), 1) scores = mcda_func(S) - return retrieve_ranks(S, scores, true, site_ids) + return retrieve_ranks(S, vec(scores), true, site_ids) end -function retrieve_ranks(S::Matrix, weights::Array, mcda_func::Vector) +function retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Vector{Any}, site_ids::Vector) fns = repeat([maximum], length(weights)) results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) - s_order = Union{Float64,Int64}[Int.(site_ids) results.scores 1:size(S, 1)] - return retrieve_ranks(S, s_order, mcda_func[2]) + return retrieve_ranks(S, results.scores, mcda_func[2], site_ids) end -function retrieve_ranks(S::Matrix, scores::Array{Float64}, rev_val::Bool, site_ids::Array{Int64}) +function retrieve_ranks(S::Matrix, scores::Vector, rev_val::Bool, site_ids::Vector) + s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] + s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) @views s_order[:, 3] .= Int.(1:size(S, 1)) @@ -281,8 +286,8 @@ function create_decision_matrix(site_ids, in_conn, out_conn, sum_cover, max_cove # Wave damage, account for cases where no chance of damage or heat stress # if max > 0 then use damage probability from wave exposure - # A[:, 4] .= maximum(wave_stress) != 0.0 ? (wave_stress .- minimum(wave_stress)) ./ (maximum(wave_stress) - minimum(wave_stress)) : 0.0 - A[:, 4] .= wave_stress ./ maximum(wave_stress) + A[:, 4] .= maximum(wave_stress) != 0.0 ? (wave_stress .- minimum(wave_stress)) ./ (maximum(wave_stress) - minimum(wave_stress)) : 0.0 + #A[:, 4] .= wave_stress ./ maximum(wave_stress) # risk from heat exposure # A[:, 5] .= maximum(heat_stress) != 0.0 ? (heat_stress .- minimum(heat_stress)) ./ (maximum(heat_stress) - minimum(heat_stress)) : 0.0 @@ -518,6 +523,8 @@ function guided_site_selection( # add weights for strongest predecessors and zones to get zone criteria zones_criteria = zone_preds .+ zone_sites + mcda_func = method[alg_ind] + A, filtered_sites = create_decision_matrix(site_ids, in_conn, out_conn, sum_cover, max_cover, area, wave_stress, heat_stress, site_depth, predec, zones_criteria, risk_tol) if isempty(A) # if all rows have nans and A is empty, abort mission @@ -550,7 +557,7 @@ function guided_site_selection( if log_shade && isempty(SH) prefshadesites = zeros(Int64, n_site_int) elseif log_shade - prefshadesites, s_order_shade = rank_shade_sites!(SH, wsh, rankings, n_site_int, mcda_func, 3) + prefshadesites, s_order_shade = rank_sites!(SH, wsh, rankings, n_site_int, mcda_func, 3) if use_dist != 0 prefshadesites, rankings = distance_sorting(prefshadesites, s_order_shade, d_vars.dist, min_dist, Int64(d_vars.top_n), rankings, 3) end From d0d9e196c1d678b706c2dee352c79c26f099bfa2 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 17 May 2023 16:11:37 +1000 Subject: [PATCH 18/34] Change output type of mcda methods as no longer uses site ids in S --- src/sites/mcda_methods.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sites/mcda_methods.jl b/src/sites/mcda_methods.jl index 46e00fd9c..c2db07468 100644 --- a/src/sites/mcda_methods.jl +++ b/src/sites/mcda_methods.jl @@ -11,7 +11,7 @@ Then orders sites from highest aggregate score to lowest. # Returns - aggregate score for ranking. """ -function order_ranking(S::Array{Float64,2})::Array{Union{Float64,Int64},2} +function order_ranking(S::Array{Float64,2})::Array{Float64} return sum(S, dims=2) end @@ -44,7 +44,7 @@ S_p = √{∑(criteria .- NIS)²} - `C` : aggregate score for ranking. """ -function adria_topsis(S::Array{Float64,2})::Array{Union{Float64,Int64},2} +function adria_topsis(S::Array{Float64,2})::Array{Float64} # compute the set of positive ideal solutions for each criteria PIS = maximum(S, dims=1) @@ -104,7 +104,7 @@ Details of this aggregation method in, for example [1] # Returns - `Q` : aggregate score for ranking. """ -function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Union{Float64,Int64},2} +function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Float64} F_s = maximum(S) From 849224d3ab1bb40c4cc3fd150de8bf6480c37a82 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Thu, 18 May 2023 15:31:45 +1000 Subject: [PATCH 19/34] Simplifications to retrieve_ranks + change order of inputs --- src/sites/dMCDA.jl | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 6de004cc0..e636e9575 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -194,7 +194,7 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup weights = weights[selector] S = S[:, Bool[1, selector...]] - s_order = retrieve_ranks(S[:, 2:end], weights, mcda_func, S[:, 1]) + s_order = retrieve_ranks(S[:, 2:end], S[:, 1], weights, mcda_func) last_idx = min(n_site_int, size(s_order, 1)) prefsites = Int.(s_order[1:last_idx, 1]) @@ -223,25 +223,21 @@ Get location ranks using mcda technique specified in mcda_func, weights and a de # Returns - `s_order` : [site_ids, criteria values, ranks] """ -function retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Function, site_ids::Vector) - S = mcda_normalize(S) - S .= S .* repeat(weights', size(S, 1), 1) +function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Function) + S = mcda_normalize(S) .* weights' scores = mcda_func(S) - return retrieve_ranks(S, vec(scores), true, site_ids) + return retrieve_ranks(S, site_ids, vec(scores), true) end -function retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Vector{Any}, site_ids::Vector) - fns = repeat([maximum], length(weights)) +function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Vector{Any}) + fns = fill(maximum, length(weights)) results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) - return retrieve_ranks(S, results.scores, mcda_func[2], site_ids) + return retrieve_ranks(S, site_ids, results.scores, mcda_func[2]) end -function retrieve_ranks(S::Matrix, scores::Vector, rev_val::Bool, site_ids::Vector) - - s_order = Union{Float64,Int64}[Int.(site_ids) scores 1:size(S, 1)] - +function retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, rev_val::Bool) + s_order = Union{Float64,Int64}[Int64.(site_ids) scores Int64.(1:size(S, 1))] s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) - @views s_order[:, 3] .= Int.(1:size(S, 1)) return s_order end From 2e520fccda3709fbf17381cb3d56237c9373b9d0 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Mon, 22 May 2023 15:30:53 +1000 Subject: [PATCH 20/34] Add InteractiveUtils to dependencies --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index d8dd64500..a953eefad 100644 --- a/Project.toml +++ b/Project.toml @@ -25,6 +25,7 @@ GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5" IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" JMcDM = "358108f5-d052-4d0a-8344-d5384e00c0e5" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" From 99d88a264b374356ac90a3d992d6b626e5800d62 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 09:24:38 +1000 Subject: [PATCH 21/34] Add usage of JMcDM function list --- src/sites/dMCDA.jl | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index e636e9575..841559204 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -4,32 +4,9 @@ using StatsBase using Distances using Combinatorics using JMcDM +using InteractiveUtils: subtypes using ADRIA: order_ranking, adria_vikor, adria_topsis -global method = [ - order_ranking, - adria_vikor, - adria_topsis, - [ArasMethod(), true], - [CocosoMethod(), true], - [CodasMethod(), true], - [CoprasMethod(), false], - [EdasMethod(), true], - [GreyMethod(), true], - [MabacMethod(), true], - [MaircaMethod(), false], - [MarcosMethod(), true], - [MooraMethod(), true], - #[MoosraMethod(), true], - [PIVMethod(), true], - [PSIMethod(), true], - [ROVMethod(), true], - [SawMethod(), true], - [TopsisMethod(), true], - [VikorMethod(), false], - [WPMMethod(), true], - [WaspasMethod(), true] -] struct DMCDA_vars # {V, I, F, M} where V <: Vector site_ids # ::V @@ -64,6 +41,11 @@ struct DMCDA_vars # {V, I, F, M} where V <: Vector wt_zones_shade # ::F end +const methods_mcda = [order_ranking, + adria_vikor, + adria_topsis, + subtypes(MCDMMethod)[(subtypes(MCDMMethod).!=[JMcDM.CRITIC.CriticMethod]).&(subtypes(MCDMMethod).!=[JMcDM.MOOSRA.MoosraMethod]).&(subtypes(MCDMMethod).!=[JMcDM.MEREC.MERECMethod]).&(subtypes(MCDMMethod).!=[JMcDM.ELECTRE.ElectreMethod])]...] + """ DMCDA_vars(domain::Domain, criteria::NamedDimsArray, site_ids::AbstractArray, sum_cover::AbstractArray, area_to_seed::Float64, @@ -229,11 +211,14 @@ function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, m return retrieve_ranks(S, site_ids, vec(scores), true) end -function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Vector{Any}) +function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::JMcDM.MCDMMethod) fns = fill(maximum, length(weights)) results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) + results = mcdm(MCDMSetting(S, weights, fns), mcda_func) + + rev_val = results.bestIndex == findall(results.scores .== maximum(results.scores)) - return retrieve_ranks(S, site_ids, results.scores, mcda_func[2]) + return retrieve_ranks(S, site_ids, results.scores, rev_val) end function retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, rev_val::Bool) s_order = Union{Float64,Int64}[Int64.(site_ids) scores Int64.(1:size(S, 1))] @@ -450,7 +435,8 @@ function guided_site_selection( rankingsin::Matrix{T}, in_conn::Vector{Float64}, out_conn::Vector{Float64}, - strong_pred::Vector{Int64} + strong_pred::Vector{Int64}; + methods_mcda=methods_mcda )::Tuple where {T<:Int64,IA<:AbstractArray{<:Int64},IB<:AbstractArray{<:Int64},B<:Bool} use_dist::Int64 = d_vars.use_dist @@ -519,7 +505,7 @@ function guided_site_selection( # add weights for strongest predecessors and zones to get zone criteria zones_criteria = zone_preds .+ zone_sites - mcda_func = method[alg_ind] + mcda_func = methods_mcda[alg_ind] A, filtered_sites = create_decision_matrix(site_ids, in_conn, out_conn, sum_cover, max_cover, area, wave_stress, heat_stress, site_depth, predec, zones_criteria, risk_tol) if isempty(A) From b119fa91911edd4a957cfa859197df82f1219aed Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 09:26:23 +1000 Subject: [PATCH 22/34] Remove use of vector for setting JMcDM sorting methods --- src/sites/dMCDA.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 841559204..e72ef1d63 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -213,7 +213,6 @@ function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, m end function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::JMcDM.MCDMMethod) fns = fill(maximum, length(weights)) - results = mcdm(MCDMSetting(S, weights, fns), mcda_func[1]) results = mcdm(MCDMSetting(S, weights, fns), mcda_func) rev_val = results.bestIndex == findall(results.scores .== maximum(results.scores)) @@ -504,7 +503,6 @@ function guided_site_selection( # add weights for strongest predecessors and zones to get zone criteria zones_criteria = zone_preds .+ zone_sites - mcda_func = methods_mcda[alg_ind] A, filtered_sites = create_decision_matrix(site_ids, in_conn, out_conn, sum_cover, max_cover, area, wave_stress, heat_stress, site_depth, predec, zones_criteria, risk_tol) From 6f48895a949d7d7e3ea74cc64a3d82b213cbcbc9 Mon Sep 17 00:00:00 2001 From: Takuya Iwanaga Date: Tue, 23 May 2023 11:27:51 +1000 Subject: [PATCH 23/34] Spacing and standardize comment format --- src/sites/dMCDA.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index e72ef1d63..990296150 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -387,11 +387,11 @@ Tuple (SH, wsh) 4. shade zones (weights importance of sites highly connected to or within priority zones) 5. high cover (weights importance of sites with high cover of coral to shade) """ - function create_shade_matrix(A, max_area, wt_conn_shade, wt_waves, wt_heat, wt_predec_shade, wt_predec_zones_shade, wt_hi_cover) # Set up decision matrix to be same size as A SH = copy(A) - # remove consideration of site depth as shading not accounted for in bleaching model yet + + # Remove consideration of site depth as shading not accounted for in bleaching model yet SH = SH[:, 1:end-1] wsh = [wt_conn_shade, wt_conn_shade, wt_waves, wt_heat, wt_predec_shade, wt_predec_zones_shade, wt_hi_cover] From 3e7ee069cec846a0a71b3934ba8744514e35f72b Mon Sep 17 00:00:00 2001 From: Takuya Iwanaga Date: Tue, 23 May 2023 11:29:29 +1000 Subject: [PATCH 24/34] Expected argument is a DataType The constructed method should be passed into `mcdm()`, not the datatype itself. --- src/sites/dMCDA.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 990296150..909d18940 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -211,9 +211,9 @@ function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, m return retrieve_ranks(S, site_ids, vec(scores), true) end -function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::JMcDM.MCDMMethod) +function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Type{<:MCDMMethod}) fns = fill(maximum, length(weights)) - results = mcdm(MCDMSetting(S, weights, fns), mcda_func) + results = mcdm(MCDMSetting(S, weights, fns), mcda_func()) rev_val = results.bestIndex == findall(results.scores .== maximum(results.scores)) From ea5762f5b1f050a1f031ef9a538acb3741237329 Mon Sep 17 00:00:00 2001 From: Takuya Iwanaga Date: Tue, 23 May 2023 11:31:07 +1000 Subject: [PATCH 25/34] Clean up wall of text --- src/sites/dMCDA.jl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 909d18940..ad8bb9fe3 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -41,10 +41,18 @@ struct DMCDA_vars # {V, I, F, M} where V <: Vector wt_zones_shade # ::F end -const methods_mcda = [order_ranking, +const jmcdm_methods = subtypes(MCDMMethod) +const methods_mcda = [ + order_ranking, adria_vikor, adria_topsis, - subtypes(MCDMMethod)[(subtypes(MCDMMethod).!=[JMcDM.CRITIC.CriticMethod]).&(subtypes(MCDMMethod).!=[JMcDM.MOOSRA.MoosraMethod]).&(subtypes(MCDMMethod).!=[JMcDM.MEREC.MERECMethod]).&(subtypes(MCDMMethod).!=[JMcDM.ELECTRE.ElectreMethod])]...] + jmcdm_methods[( + (jmcdm_methods.!=[JMcDM.CRITIC.CriticMethod]) + .&(jmcdm_methods.!=[JMcDM.MOOSRA.MoosraMethod]) + .&(jmcdm_methods.!=[JMcDM.MEREC.MERECMethod]) + .&(jmcdm_methods.!=[JMcDM.ELECTRE.ElectreMethod]) + )]... +] """ DMCDA_vars(domain::Domain, criteria::NamedDimsArray, From b5093fdf59cddc98993cf983ccfe2b4c2f6aa7d6 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 11:46:59 +1000 Subject: [PATCH 26/34] Add PrometheeMethod to list of excluded methods --- src/sites/dMCDA.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index ad8bb9fe3..959ff9164 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -51,6 +51,7 @@ const methods_mcda = [ .&(jmcdm_methods.!=[JMcDM.MOOSRA.MoosraMethod]) .&(jmcdm_methods.!=[JMcDM.MEREC.MERECMethod]) .&(jmcdm_methods.!=[JMcDM.ELECTRE.ElectreMethod]) + .&(jmcdm_methods.!=[JMcDM.PROMETHEE.PrometheeMethod]) )]... ] From ca434a3e8585f449006e89b5d0987ea04bee2636 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 11:49:40 +1000 Subject: [PATCH 27/34] Update docs for retrieve_ranks --- src/sites/dMCDA.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 959ff9164..2570fd432 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -171,7 +171,7 @@ end - `weights` : weights to apply - `rankings` : vector of site ranks to update - `n_site_int` : number of sites to select for interventions -- `mcda_func` : element of method, designates mcda method to use +- `mcda_func` : function or JMcDM DataType, designates mcda method to use - `rank_col` : column to fill with rankings (2 for seed, 3 for shade) # Returns @@ -197,7 +197,7 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup end """ - retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Function, site_ids::Vector) + retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::DataType, site_ids::Vector) retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Vector{Any}, site_ids::Vector) retrieve_ranks(S::Matrix, scores::Vector, rev_val::Bool, site_ids::Vector) @@ -206,7 +206,7 @@ Get location ranks using mcda technique specified in mcda_func, weights and a de # Arguments - `S` : decision matrix containing criteria values for each location (n locations)*(m criteria) - `weights` : importance weights for each criteria. -- `mcda_func` : function/[function bool] array to use for mcda, specified as an element from method. +- `mcda_func` : function/JMcDM DataType to use for mcda, specified as an element from methods_mcda. - `site_ids` : array of site ids still remaining after filtering. - `scores` : set of scores derived from applying an mcda ranking method. - `rev_val` : Boolean indicating whether a mcda method is maximising score (true), or minimising (false). From dc47465ef50e6ab31a9f3a66dc238c2aa0f24359 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 12:11:32 +1000 Subject: [PATCH 28/34] Add indexation to compare scalar max not vector (will always give false) --- src/sites/dMCDA.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 2570fd432..6d2f2b1c4 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -223,8 +223,7 @@ end function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Type{<:MCDMMethod}) fns = fill(maximum, length(weights)) results = mcdm(MCDMSetting(S, weights, fns), mcda_func()) - - rev_val = results.bestIndex == findall(results.scores .== maximum(results.scores)) + rev_val = results.bestIndex == findall(results.scores .== maximum(results.scores))[1] return retrieve_ranks(S, site_ids, results.scores, rev_val) end From e5ea28c32bf36d837e32a8617c4c5b9b8178f2b0 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 12:48:36 +1000 Subject: [PATCH 29/34] Change order of docs input values to match change in order --- src/sites/dMCDA.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 6d2f2b1c4..70e9a0f6c 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -197,17 +197,17 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup end """ - retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::DataType, site_ids::Vector) - retrieve_ranks(S::Matrix, weights::Vector{Float64}, mcda_func::Vector{Any}, site_ids::Vector) - retrieve_ranks(S::Matrix, scores::Vector, rev_val::Bool, site_ids::Vector) + retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::DataType) + retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Vector{Any}) + retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, rev_val::Bool) Get location ranks using mcda technique specified in mcda_func, weights and a decision matrix S. # Arguments - `S` : decision matrix containing criteria values for each location (n locations)*(m criteria) +- `site_ids` : array of site ids still remaining after filtering. - `weights` : importance weights for each criteria. - `mcda_func` : function/JMcDM DataType to use for mcda, specified as an element from methods_mcda. -- `site_ids` : array of site ids still remaining after filtering. - `scores` : set of scores derived from applying an mcda ranking method. - `rev_val` : Boolean indicating whether a mcda method is maximising score (true), or minimising (false). From 462e8ad76590f96f49482171ecf98a961e1e79ce Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Tue, 23 May 2023 12:51:53 +1000 Subject: [PATCH 30/34] Change Int to Int64 in rank_sites! --- src/sites/dMCDA.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index 70e9a0f6c..cb4d267e7 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -188,7 +188,7 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup s_order = retrieve_ranks(S[:, 2:end], S[:, 1], weights, mcda_func) last_idx = min(n_site_int, size(s_order, 1)) - prefsites = Int.(s_order[1:last_idx, 1]) + prefsites = Int64.(s_order[1:last_idx, 1]) # Match by site_id and assign rankings to log align_rankings!(rankings, s_order, rank_col) From 8e3059d77390b8aa2ee99e746682090afc870eb7 Mon Sep 17 00:00:00 2001 From: Takuya Iwanaga Date: Tue, 23 May 2023 18:22:12 +1000 Subject: [PATCH 31/34] Simplify removal of JMcDM methods to ignore --- src/sites/dMCDA.jl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index cb4d267e7..cb4bff361 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -42,17 +42,20 @@ struct DMCDA_vars # {V, I, F, M} where V <: Vector end const jmcdm_methods = subtypes(MCDMMethod) + +jmcdm_ignore = [ + JMcDM.CRITIC.CriticMethod, + JMcDM.MOOSRA.MoosraMethod, + JMcDM.MEREC.MERECMethod, + JMcDM.ELECTRE.ElectreMethod, + JMcDM.PROMETHEE.PrometheeMethod +] + const methods_mcda = [ order_ranking, adria_vikor, adria_topsis, - jmcdm_methods[( - (jmcdm_methods.!=[JMcDM.CRITIC.CriticMethod]) - .&(jmcdm_methods.!=[JMcDM.MOOSRA.MoosraMethod]) - .&(jmcdm_methods.!=[JMcDM.MEREC.MERECMethod]) - .&(jmcdm_methods.!=[JMcDM.ELECTRE.ElectreMethod]) - .&(jmcdm_methods.!=[JMcDM.PROMETHEE.PrometheeMethod]) - )]... + setdiff(jmcdm_methods, jmcdm_ignore)... ] """ From 72e7846d1f6c3bab1d03a65593932b9b416a2941 Mon Sep 17 00:00:00 2001 From: Rose Crocker Date: Wed, 24 May 2023 08:47:27 +1000 Subject: [PATCH 32/34] Add line at end of mcda_methods() --- src/sites/mcda_methods.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sites/mcda_methods.jl b/src/sites/mcda_methods.jl index c2db07468..8b11ef28b 100644 --- a/src/sites/mcda_methods.jl +++ b/src/sites/mcda_methods.jl @@ -121,4 +121,4 @@ function adria_vikor(S::Array{Float64,2}; v::Float64=0.5)::Array{Float64} Q .= 1.0 .- Q # Invert rankings so higher values = higher rank return Q -end \ No newline at end of file +end From 0531dfa2d5f22297470b45eeb76e2dff84518569 Mon Sep 17 00:00:00 2001 From: Takuya Iwanaga Date: Wed, 24 May 2023 12:18:01 +1000 Subject: [PATCH 33/34] whitespace --- src/sites/dMCDA.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index cb4bff361..dca935a71 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -44,7 +44,7 @@ end const jmcdm_methods = subtypes(MCDMMethod) jmcdm_ignore = [ - JMcDM.CRITIC.CriticMethod, + JMcDM.CRITIC.CriticMethod, JMcDM.MOOSRA.MoosraMethod, JMcDM.MEREC.MERECMethod, JMcDM.ELECTRE.ElectreMethod, From 6b7a72f83d8faa3b5d3fad8ab6ddae4ed83284c8 Mon Sep 17 00:00:00 2001 From: Takuya Iwanaga Date: Wed, 24 May 2023 12:18:21 +1000 Subject: [PATCH 34/34] Update docstring and use more intuitive argument name --- src/sites/dMCDA.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sites/dMCDA.jl b/src/sites/dMCDA.jl index dca935a71..be94a07d2 100644 --- a/src/sites/dMCDA.jl +++ b/src/sites/dMCDA.jl @@ -200,9 +200,9 @@ function rank_sites!(S, weights, rankings, n_site_int, mcda_func, rank_col)::Tup end """ - retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::DataType) - retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Vector{Any}) - retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, rev_val::Bool) + retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Function) + retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Type{<:MCDMMethod}) + retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, maximize::Bool) Get location ranks using mcda technique specified in mcda_func, weights and a decision matrix S. @@ -212,7 +212,7 @@ Get location ranks using mcda technique specified in mcda_func, weights and a de - `weights` : importance weights for each criteria. - `mcda_func` : function/JMcDM DataType to use for mcda, specified as an element from methods_mcda. - `scores` : set of scores derived from applying an mcda ranking method. -- `rev_val` : Boolean indicating whether a mcda method is maximising score (true), or minimising (false). +- `maximize` : Boolean indicating whether a mcda method is maximizing score (true), or minimizing (false). # Returns - `s_order` : [site_ids, criteria values, ranks] @@ -226,13 +226,13 @@ end function retrieve_ranks(S::Matrix, site_ids::Vector, weights::Vector{Float64}, mcda_func::Type{<:MCDMMethod}) fns = fill(maximum, length(weights)) results = mcdm(MCDMSetting(S, weights, fns), mcda_func()) - rev_val = results.bestIndex == findall(results.scores .== maximum(results.scores))[1] + maximize = results.bestIndex == findall(results.scores .== maximum(results.scores))[1] - return retrieve_ranks(S, site_ids, results.scores, rev_val) + return retrieve_ranks(S, site_ids, results.scores, maximize) end -function retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, rev_val::Bool) +function retrieve_ranks(S::Matrix, site_ids::Vector, scores::Vector, maximize::Bool) s_order = Union{Float64,Int64}[Int64.(site_ids) scores Int64.(1:size(S, 1))] - s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=rev_val) + s_order .= sortslices(s_order, dims=1, by=x -> x[2], rev=maximize) return s_order end