Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • b7953a0
  • /
  • dearussell.jl
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:1bd50bb80f9ec787b862c87377c28d08456f4482
directory badge Iframe embedding
swh:1:dir:b7953a0181984616831ccdfbbba622e10eb82cca
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
dearussell.jl
# This file contains functions for the Russell DEA model
"""
    RussellDEAModel
An data structure representing a Russell DEA model.
"""
struct RussellDEAModel <: AbstractTechnicalDEAModel
    n::Int64
    m::Int64
    s::Int64
    orient::Symbol
    rts::Symbol
    dmunames::Union{Vector{AbstractString},Nothing}
    eff::Vector
    thetaX::Matrix
    thetaY::Matrix
    slackX::Matrix
    slackY::Matrix
    lambda::SparseMatrixCSC{Float64, Int64}
    Xtarget::Matrix
    Ytarget::Matrix
end

"""
    dearussell(X, Y)
Compute the Russell model using data envelopment analysis for inputs X and outputs Y.

# Optional Arguments
- `orient=:Input`: chooses the Russell input mode. For the Russell output model choose `:Output`. For the Russell graph model choose `:Graph`.
- `rts=:CRS`: chooses constant returns to scale. For variable returns to scale choose `:VRS`.
- `slack=true`: computes input and output slacks.
- `Xref=X`: Identifies the reference set of inputs against which the units are evaluated.
- `Yref=Y`: Identifies the reference set of outputs against which the units are evaluated.
- `names`: a vector of strings with the names of the decision making units.
"""
function dearussell(X::Union{Matrix,Vector}, Y::Union{Matrix,Vector};
    orient::Symbol = :Input, rts::Symbol = :CRS, slack::Bool = true,
    Xref::Union{Matrix,Vector,Nothing} = nothing, Yref::Union{Matrix,Vector,Nothing} = nothing,
    names::Union{Vector{<: AbstractString},Nothing} = nothing,
    optimizer::Union{DEAOptimizer,Nothing} = nothing)::RussellDEAModel

    # Check parameters
    nx, m = size(X, 1), size(X, 2)
    ny, s = size(Y, 1), size(Y, 2)

    if Xref === nothing Xref = X end
    if Yref === nothing Yref = Y end

    nrefx, mref = size(Xref, 1), size(Xref, 2)
    nrefy, sref = size(Yref, 1), size(Yref, 2)

    if nx != ny
        throw(DimensionMismatch("number of rows in X and Y ($nx, $ny) are not equal"));
    end
    if nrefx != nrefy
        throw(DimensionMismatch("number of rows in Xref and Yref ($nrefx, $nrefy) are not equal"));
    end
    if m != mref
        throw(DimensionMismatch("number of columns in X and Xref ($m, $mref) are not equal"));
    end
    if s != sref
        throw(DimensionMismatch("number of columns in Y and Yref ($s, $sref) are not equal"));
    end

    # Default optimizer
    if optimizer === nothing 
        if orient == :Input || orient == :Output
            optimizer = DEAOptimizer(:LP)
        else
            optimizer = DEAOptimizer(:NLP)
        end
    end

    # Compute efficiency for each DMU
    n = nx
    nref = nrefx

    effi = zeros(n)
    thetaXi = zeros(n, m)
    thetaYi = zeros(n, s)
    lambdaeff = spzeros(n, nref)

    for i=1:n
        # Value of inputs and outputs to evaluate
        x0 = X[i,:]
        y0 = Y[i,:]

        # Create the optimization model
        if orient == :Input
            # Input orientation
            deamodel = newdeamodel(optimizer)
            
            mno0 = sum(x0 .!= 0)

            @variable(deamodel, lambda[1:nref] >= 0)
            @variable(deamodel, theta[1:m] <= 1)
            @objective(deamodel, Min, 1 / mno0 * sum(theta[t] for t in 1:m if x0[t] != 0 ))

            @constraint(deamodel, [j in 1:m], sum(Xref[t,j] * lambda[t] for t in 1:nref) <= theta[j] * x0[j])
            @constraint(deamodel, [j in 1:s], sum(Yref[t,j] * lambda[t] for t in 1:nref) >= y0[j])
        elseif orient == :Output
            # Output orientation
            deamodel = newdeamodel(optimizer)
            
            sno0 = sum(y0 .!= 0)

            @variable(deamodel, lambda[1:nref] >= 0)
            @variable(deamodel, theta[1:s] >= 1)
            @objective(deamodel, Max, 1 / sno0 * sum(theta[t] for t in 1:s if y0[t] != 0 ))

            @constraint(deamodel, [j in 1:m], sum(Xref[t,j] * lambda[t] for t in 1:nref) <= x0[j])
            @constraint(deamodel, [j in 1:s], sum(Yref[t,j] * lambda[t] for t in 1:nref) >= theta[j] * y0[j])
        elseif orient == :Graph
            # Graph orientation
            deamodel = newdeamodel(optimizer)
            set_silent(deamodel)            

            mno0 = sum(x0 .!= 0)
            sno0 = sum(y0 .!= 0)

            @variable(deamodel, lambda[1:nref] >= 0)
            @variable(deamodel, theta[1:m] <= 1)
            @variable(deamodel, phi[1:s] >= 1)

            @NLobjective(deamodel, Min, 1 / (mno0 + sno0) * (sum(theta[t] for t in 1:m if x0[t] != 0 ) + sum(1/phi[t] for t in 1:s if y0[t] != 0) ))

            @constraint(deamodel, [j in 1:m], sum(Xref[t,j] * lambda[t] for t in 1:nref) == theta[j] * x0[j])
            @constraint(deamodel, [j in 1:s], sum(Yref[t,j] * lambda[t] for t in 1:nref) == phi[j] * y0[j])
        else
            throw(ArgumentError("`orient` must be :Input, :Output or :Graph"));
        end

        # Add return to scale constraints
        if rts == :CRS
            # No contraint to add for constant returns to scale
        elseif rts == :VRS
            @constraint(deamodel, sum(lambda) == 1)
        else
            throw(ArgumentError("`rts` must be :CRS or :VRS"));
        end

        # Optimize and return results
        JuMP.optimize!(deamodel)

        effi[i]  = JuMP.objective_value(deamodel)
        lambdaeff[i,:] = JuMP.value.(lambda)

        if orient == :Input
            thetaXi[i,:] = JuMP.value.(theta)
        elseif orient == :Output
            thetaYi[i,:] = JuMP.value.(theta)
        elseif orient == :Graph
            thetaXi[i,:] = JuMP.value.(theta)
            thetaYi[i,:] = JuMP.value.(phi)
        end

        # Check termination status
        if (termination_status(deamodel) != MOI.OPTIMAL) && (termination_status(deamodel) != MOI.LOCALLY_SOLVED)
            @warn ("DMU $i termination status: $(termination_status(deamodel)). Primal status: $(primal_status(deamodel)). Dual status: $(dual_status(deamodel))")
        end

    end

    # Get first-stage X and Y targets
    if orient == :Input
        Xtarget = X .* thetaXi
        Ytarget = Y
    elseif orient == :Output
        Xtarget = X
        Ytarget = Y .* thetaYi
    elseif orient == :Graph
        Xtarget = X .* thetaXi
        Ytarget = Y .* thetaYi
    end

    # Compute slacks
    if (slack == true) && (orient != :Graph)

        # Use additive model with Russell efficient X and Y to get slacks
        russellSlacks = deaadd(Xtarget, Ytarget, :Ones, rts = rts, Xref = Xref, Yref = Yref, optimizer = optimizer)
        slackX = slacks(russellSlacks, :X)
        slackY = slacks(russellSlacks, :Y)

        # Get second-stage X and Y targets
        Xtarget = Xtarget - slackX
        Ytarget = Ytarget + slackY
    else
        if typeof(Xtarget) <: AbstractVector    Xtarget = Xtarget[:,:]  end
        if typeof(Ytarget) <: AbstractVector    Ytarget = Ytarget[:,:]  end

        slackX = Array{Float64}(undef, 0, 0)
        slackY = Array{Float64}(undef, 0, 0)
    end

    if orient == :Input
        thetaYi = Array{Float64}(undef, 0, 0)
    elseif orient == :Output
        thetaXi = Array{Float64}(undef, 0, 0)
    end

    return RussellDEAModel(n, m, s, orient, rts, names, effi, thetaXi, thetaYi, slackX, slackY, lambdaeff, Xtarget, Ytarget)

end

function Base.show(io::IO, x::RussellDEAModel)
    compact = get(io, :compact, false)

    n = nobs(x)
    m = ninputs(x)
    s = noutputs(x)
    dmunames = names(x)

    eff = efficiency(x)
    slackX = slacks(x, :X)
    slackY = slacks(x, :Y)
    hasslacks = ! isempty(slackX)

    if !compact
        print(io, "Russell DEA Model \n")
        print(io, "DMUs = ", n)
        print(io, "; Inputs = ", m)
        print(io, "; Outputs = ", s)
        print(io, "\n")
        print(io, "Orientation = ", string(x.orient))
        print(io, "; Returns to Scale = ", string(x.rts))
        print(io, "\n")
        if x.orient == :Input
            thetaX = x.thetaX
            if hasslacks == true
                show(io, CoefTable(hcat(eff, thetaX, slackY), ["efficiency"; ["effX$i" for i in 1:m ]; ["slackY$i" for i in 1:s ]], dmunames))
            else
                show(io, CoefTable(hcat(eff, thetaX), ["efficiency"; ["effX$i" for i in 1:m ] ], dmunames))
            end
        elseif x.orient == :Output
            thetaY = x.thetaY
            if hasslacks == true
                show(io, CoefTable(hcat(eff, thetaY, slackX), ["efficiency"; ["effY$i" for i in 1:s ]; ["slackX$i" for i in 1:m ]], dmunames))
            else
                show(io, CoefTable(hcat(eff, thetaY), ["efficiency"; ["effY$i" for i in 1:s ] ], dmunames))
            end
        elseif x.orient == :Graph
            thetaX = x.thetaX
            thetaY = x.thetaY
            show(io, CoefTable(hcat(eff, thetaX, thetaY), ["efficiency"; ["effX$i" for i in 1:m ]; ["effY$i" for i in 1:s ] ], dmunames))
        end
    end

end

function efficiency(model::RussellDEAModel, type::Symbol)::Matrix

    if (type == :X && (model.orient == :Input  || model.orient == :Graph)) return model.thetaX end
    if (type == :Y && (model.orient == :Output || model.orient == :Graph)) return model.thetaY end
        
    throw(ArgumentError("$(typeof(model)) with orienation $(model.orient) has no efficiency $(type)"));

end

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API