Skip to content

Commit

Permalink
Add FDH option
Browse files Browse the repository at this point in the history
Add FDH option to some modesl:
- Radial DEA model.
- Additive DEA model.
- DDF DEA model.
- Cost, Revenue, and Profit model.
- Russsell DEA model.
  • Loading branch information
javierbarbero committed Apr 20, 2024
1 parent 48c782a commit 442eae0
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 12 deletions.
5 changes: 4 additions & 1 deletion src/dea.jl
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,11 @@ function dea(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector};
# No contraint to add for constant returns to scale
elseif rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:nref])
else
throw(ArgumentError("`rts` must be :CRS or :VRS"));
throw(ArgumentError("`rts` must be :CRS, :VRS, or :FDH"));
end

# Optimize and return results
Expand Down
5 changes: 4 additions & 1 deletion src/deaadd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,11 @@ function deaadd(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
end
elseif rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:nref])
else
throw(ArgumentError("`rts` must be :CRS or :VRS"));
throw(ArgumentError("`rts` must be :CRS, :VRS or :FDH"));
end

# Fix values of slacks when weight are zero
Expand Down
5 changes: 4 additions & 1 deletion src/deaddf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,11 @@ function deaddf(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector};
# No contraint to add for constant returns to scale
elseif rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:nref])
else
throw(ArgumentError("`rts` must be :CRS or :VRS"));
throw(ArgumentError("`rts` must be :CRS, :VRS or :FDH"));
end

# Optimize and return results
Expand Down
6 changes: 3 additions & 3 deletions src/deaprofit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Alternatively, a vector or matrix with the desired directions can be supplied.
function deaprofit(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
W::Union{Matrix,Vector}, P::Union{Matrix,Vector};
Gx::Union{Symbol,Matrix,Vector}, Gy::Union{Symbol,Matrix,Vector},
monetary::Bool = false,
rts::Symbol = :VRS, monetary::Bool = false,
names::Union{Vector{<: AbstractString},Nothing} = nothing,
optimizer::Union{DEAOptimizer,Nothing} = nothing)::ProfitDEAModel

Expand Down Expand Up @@ -131,14 +131,14 @@ function deaprofit(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
# Get maximum profit targets and lambdas
n = nx

Xtarget, Ytarget, plambdaeff = deamaxprofit(X, Y, W, P, optimizer = optimizer)
Xtarget, Ytarget, plambdaeff = deamaxprofit(X, Y, W, P, rts = rts, optimizer = optimizer)

# Profit, technical and allocative efficiency
maxprofit = sum(P .* Ytarget, dims = 2) .- sum(W .* Xtarget, dims = 2)

pefficiency = vec(maxprofit .- ( sum(P .* Y, dims = 2) .- sum(W .* X, dims = 2)))
normalization = vec(sum(P .* Gy, dims = 2) .+ sum(W .* Gx, dims = 2))
techefficiency = efficiency(deaddf(X, Y, Gx = Gx, Gy = Gy, rts = :VRS, slack = false, optimizer = optimizer))
techefficiency = efficiency(deaddf(X, Y, Gx = Gx, Gy = Gy, rts = rts, slack = false, optimizer = optimizer))

if monetary
techefficiency = techefficiency .* normalization
Expand Down
7 changes: 6 additions & 1 deletion src/dearussell.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ function dearussell(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector};
optimizer = DEAOptimizer(:LP)
else
optimizer = DEAOptimizer(:NLP)

rts != :FDH || throw(ArgumentError("Free Disposal Hull, rts = :FDH, not implemented for orientation :Graph"))
end
end

Expand Down Expand Up @@ -134,8 +136,11 @@ function dearussell(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector};
# No contraint to add for constant returns to scale
elseif rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:nref])
else
throw(ArgumentError("`rts` must be :CRS or :VRS"));
throw(ArgumentError("`rts` must be :CRS, :VRS or :FDH"));
end

# Optimize and return results
Expand Down
22 changes: 18 additions & 4 deletions src/econoptim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ function deamincost(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
# No contraint to add for constant returns to scale
elseif rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:n])
else
throw(ArgumentError("`rts` must be :CRS or :VRS"));
throw(ArgumentError("`rts` must be :CRS, :VRS or :FDH"));
end

# Optimize and return results
Expand Down Expand Up @@ -146,8 +149,11 @@ function deamaxrevenue(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
# No contraint to add for constant returns to scale
elseif rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:n])
else
throw(ArgumentError("`rts` must be :CRS or :VRS"));
throw(ArgumentError("`rts` must be :CRS, :VRS or :FDH"));
end

# Optimize and return results
Expand All @@ -169,7 +175,7 @@ end


function deamaxprofit(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
W::Union{Matrix,Vector}, P::Union{Matrix,Vector};
W::Union{Matrix,Vector}, P::Union{Matrix,Vector}; rts::Symbol = :VRS,
optimizer::Union{DEAOptimizer,Nothing} = nothing)

# Check parameters
Expand Down Expand Up @@ -224,7 +230,15 @@ function deamaxprofit(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector},
@constraint(deamodel, [j in 1:m], sum(X[t,j] * lambda[t] for t in 1:n) <= Xeff[j])
@constraint(deamodel, [j in 1:s], sum(Y[t,j] * lambda[t] for t in 1:n) >= Yeff[j])

@constraint(deamodel, sum(lambda) == 1)
# Add return to scale constraints
if rts == :VRS
@constraint(deamodel, sum(lambda) == 1)
elseif rts == :FDH
@constraint(deamodel, sum(lambda) == 1)
set_binary.(lambda[1:n])
else
throw(ArgumentError("`rts` must be :VRS or :FDH"));
end

# Optimize and return results
JuMP.optimize!(deamodel)
Expand Down
51 changes: 50 additions & 1 deletion test/dea.jl
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,56 @@
@test efficiency(dea(targets(deaoovrs, :X), targets(deaoovrs, :Y), orient = :Output, rts = :VRS, slack = false)) ones(11)
@test efficiency(deaadd(targets(deaoovrs, :X), targets(deaoovrs, :Y))) zeros(11) atol=1e-12

# Test no slacks
# Input oriented FDH
deafdh = dea(X, Y, orient = :Input, rts = :FDH)

@test typeof(deafdh) == RadialDEAModel

@test nobs(deafdh) == 11
@test ninputs(deafdh) == 2
@test noutputs(deafdh) == 1
@test efficiency(deafdh) [1.0000000000;
1.0000000000;
1.0000000000;
1.0000000000;
0.8888888889;
1.0000000000;
1.0000000000;
1.0000000000;
1.0000000000;
0.5952380952;
1.0000000000]
@test convert(Matrix, peers(deafdh))
[1 0 0 0 0 0 0 0 0 0 0;
0 1 0 0 0 0 0 0 0 0 0;
0 0 1 0 0 0 0 0 0 0 0;
0 0 0 1 0 0 0 0 0 0 0;
0 1 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 1 0 0 0 0 0;
0 0 0 0 0 0 1 0 0 0 0;
0 0 0 0 0 0 0 1 0 0 0;
0 0 0 0 0 0 0 0 1 0 0;
0 0 0 0 0 0 1 0 0 0 0;
0 0 0 0 0 0 0 0 0 0 1]
@test slacks(deafdh, :X) [0.0 0.0;
0.0 0.0;
0.0 0.0;
0.0 0.0;
0.0 0.44444444444444287;
0.0 0.0;
0.0 0.0;
0.0 0.0;
0.0 0.0;
3.552713678800501e-15 4.880952380952383;
0.0 4.0]
@test slacks(deafdh, :Y) [0.0; 0.0; 0.0; 0.0; 6.0; 0.0; 0.0; 0.0; 0.0; 1.0; 0.0];

@test efficiency(dea(targets(deafdh, :X), targets(deafdh, :Y), orient = :Input, rts = :FDH, slack = false)) ones(11)
@test efficiency(deaadd(targets(deafdh, :X), targets(deafdh, :Y), rts = :FDH)) zeros(11) atol=1e-14

@test peersmatrix(deafdh) == deafdh.lambda

## Test no slacks
deaionoslack = dea(X, Y, slack = false)
@test efficiency(deaionoslack) == efficiency(deaio)
@test isempty(slacks(deaionoslack, :X)) == 1
Expand Down
57 changes: 57 additions & 0 deletions test/deaadd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,63 @@
5;
0]

# FDH
deaaddfdh = deaadd(X, Y, rts = :FDH)

@test typeof(deaaddfdh) == AdditiveDEAModel

@test nobs(deaaddfdh) == 11
@test ninputs(deaaddfdh) == 2
@test noutputs(deaaddfdh) == 1
@test efficiency(deaaddfdh) [0;
0.0;
0.0;
0.0;
18.0;
0.0;
0.0;
0.0;
0.0;
35.0;
4.0]

@test convert(Matrix, peers(deaaddfdh))
[ 1 0 0 0 0 0 0 0 0 0 0;
0 1 0 0 0 0 0 0 0 0 0;
0 0 1 0 0 0 0 0 0 0 0;
0 0 0 1 0 0 0 0 0 0 0;
1 0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 1 0 0 0 0 0;
0 0 0 0 0 0 1 0 0 0 0;
0 0 0 0 0 0 0 1 0 0 0;
0 0 0 0 0 0 0 0 1 0 0;
0 0 0 1 0 0 0 0 0 0 0;
1 0 0 0 0 0 0 0 0 0 0]

@test deaaddfdh.slackX [0 0;
0 0;
0 0;
0 0;
13.0 1.0;
0 0;
0 0;
0 0;
0 0;
25.0 10.0;
0 4.0]

@test deaaddfdh.slackY [0;
0;
0;
0;
4.0 ;
0;
0;
0;
0;
0;
0]

# Test model with Custom weights
deaddcustomcrs = deaadd(X, Y, rhoX = 1 ./ X, rhoY = 1 ./ Y, rts = :CRS)
@test deaddcustomcrs.weights == :Custom
Expand Down
20 changes: 20 additions & 0 deletions test/deacost.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@

@test efficiency(deacost(targets(deacostvrs, :X), targets(deacostvrs, :Y), W, rts = :VRS)) ones(5)

# Cost FDH
deacostfdh = deacost(X, Y, W, rts = :FDH)
@test efficiency(deacostfdh) [0.6153846154;
1.0000000000;
1.0000000000;
0.5000000000;
0.3478260870]
@test efficiency(deacostfdh, :Technical) [0.8000;
1.0000;
1.0000;
0.5000;
0.4444] atol = 1e-3
@test efficiency(deacostfdh, :Allocative) [0.7692307692;
1.0000000000;
1.0000000000;
1.0000000000;
0.7826086957]

@test efficiency(deacost(targets(deacostfdh, :X), targets(deacostfdh, :Y), W, rts = :FDH)) ones(5)

# Check defaults
@test efficiency(deacost(X, Y, W)) == efficiency(deacostvrs)
@test efficiency(deacostvrs, :Economic) == efficiency(deacostvrs)
Expand Down
30 changes: 30 additions & 0 deletions test/deaddf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,36 @@

@test efficiency(deaddfmeanvrs) [0; 0.08056567388; 0; 0;0.23098350118; 0; 0; 0; 0; 0.24886877828; 0]

# Observed FDH
deaddffdh = deaddf(X, Y, Gx = X, Gy = Y, rts = :FDH)

@test efficiency(deaddffdh) [0.0000000000;
0.0000000000;
0.0000000000;
0.0000000000;
0.1111111111;
0.0000000000;
0.0000000000;
0.0000000000;
0.0000000000;
0.1200000000;
0.0000000000]
@test slacks(deaddffdh, :X) [0.000000000 0;
0.000000000 0;
0.000000000 0;
0.000000000 0;
0.000000000 0.4444444444444464;
0.000000000 0;
0.000000000 0;
0.000000000 0;
0.000000000 0;
9.96 0;
0.000000000 4]
@test slacks(deaddffdh, :Y) [0.0; 0.0; 0.0; 0.0; 5.11111111111112; 0.0; 0.0; 0.0; 0.0; 0.879999999999999; 0.0]

@test efficiency(deaddf(targets(deaddffdh, :X), targets(deaddffdh, :Y), Gx = :Observed, Gy = :Observed, rts = :FDH, slack = false)) zeros(11) atol=1e-15
@test efficiency(deaadd(targets(deaddffdh, :X), targets(deaddffdh, :Y), rts = :FDH)) zeros(11) atol=1e-13

# Test no slacks
deaddfnoslack = deaddf(X, Y, Gx = X, Gy = Y, slack = false)
@test efficiency(deaddfnoslack) == efficiency(deaddfobs)
Expand Down
7 changes: 7 additions & 0 deletions test/deaprofit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ end

@test peersmatrix(deaprofitdollar) == deaprofitdollar.lambda

# FDH Profit model
deaprofitdollarfdh = deaprofit(X, Y, W, P, Gx = GxGydollar, Gy = GxGydollar, rts = :FDH)

@test efficiency(deaprofitdollarfdh, :Economic) [2; 2; 0; 2; 2; 8; 12; 4] atol = 1e-3
@test efficiency(deaprofitdollarfdh, :Technical) [0; 0; 0; 0; 0; 6; 12; 1.5] atol = 1e-3
@test efficiency(deaprofitdollarfdh, :Allocative) [2; 2; 0; 2; 2; 2; 0; 2.5] atol = 1e-3

# Check directions checking technical efficiency
@test efficiency(deaprofit(X, Y, W, P, Gx = :Zeros, Gy = :Ones), :Technical) == efficiency(deaddf(X, Y, Gx = :Zeros, Gy = :Ones, rts = :VRS))
@test efficiency(deaprofit(X, Y, W, P, Gx = :Ones, Gy = :Zeros), :Technical) == efficiency(deaddf(X, Y, Gx = :Ones, Gy = :Zeros, rts = :VRS))
Expand Down
20 changes: 20 additions & 0 deletions test/dearevenue.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@

@test efficiency(dearevenue(targets(dearevenuevrs, :X), targets(dearevenuevrs, :Y), P, rts = :VRS)) ones(5)

# Revnue FDH
dearevenuefdh = dearevenue(X, Y, P, rts = :FDH)
@test efficiency(dearevenuefdh) [0.6590909091;
1.0000000000;
1.0000000000;
0.5000000000;
0.4565217391]
@test efficiency(dearevenuefdh, :Technical) [0.875;
1.000;
1.000;
0.500;
0.600] atol = 1e-3
@test efficiency(dearevenuefdh, :Allocative) [0.7532467532;
1.0000000000;
1.0000000000;
1.0000000000;
0.7608695652]

@test efficiency(dearevenue(targets(dearevenuefdh, :X), targets(dearevenuefdh, :Y), P, rts = :FDH)) ones(5)

# Check defaults
@test efficiency(dearevenue(X, Y, P)) == efficiency(dearevenuevrs)
@test efficiency(dearevenuevrs, :Economic) == efficiency(dearevenuevrs)
Expand Down
20 changes: 20 additions & 0 deletions test/dearussell.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,25 @@
dearusselloovrs = dearussell(X, Y, orient = :Output, rts = :VRS)
@test efficiency(dearusselloovrs) [1.0; 1.0; 1.0; 1.8666666666666665; 2.333333333333333; 1.5; 1.4583333333333335; 3.0555555555555554]

# Output oriented FDH
dearussellfdh = dearussell(X, Y, orient = :Output, rts = :FDH)

@test efficiency(dearussellfdh, :Y) [1 1; 1 1; 1 1; 7/3 1.4; 7/3 7/3; 1 2; 7/6 7/4; 14/3 1.4]
@test efficiency(dearussellfdh) [1.0; 1.0; 1.0; 1.8666666666666665; 2.333333333333333; 1.5; 1.4583333333333335; 3.033333333333333]
@test convert(Matrix, peers(dearussellfdh))
[ 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0] atol = 1e-10
@test slacks(dearussellfdh, :X) zeros(8,1)
@test slacks(dearussellfdh, :Y) zeros(8,2) atol = 1e-14

@test efficiency(dearussell(targets(dearussellfdh, :X), targets(dearussellfdh, :Y), orient = :Output, rts = :FDH)) ones(8,1)

## Test if one-by-one DEA using evaluation and reference sets match initial results
dearussell_oo_ref_eff = zeros(size(X, 1))
dearussell_oovrs_ref_eff = zeros(size(X, 1))
Expand Down Expand Up @@ -242,6 +261,7 @@
@test_throws DimensionMismatch dearussell([1 12 2], [4 45 5], Yref = [4 4 4; 5 5 5]) # Different number of inputs
@test_throws ArgumentError dearussell([1; 2; 3], [4; 5; 6], orient = :Error) # Invalid orientation
@test_throws ArgumentError dearussell([1; 2; 3], [4; 5; 6], rts = :Error) # Invalid returns to scale
@test_throws ArgumentError dearussell([1; 2; 3], [4; 5; 6], orient = :Graph, rts = :FDH) # FDH not implemented for orientation :Graph

@test_throws ArgumentError efficiency(dearussellio, :Error) # Invalid efficiency type

Expand Down

0 comments on commit 442eae0

Please sign in to comment.