# import dependencies
from pyomo.environ import ConcreteModel, Set, Var, Objective, minimize, maximize, Constraint, Binary
import numpy as np
import pandas as pd
from .constant import CET_ADDI, ORIENT_IO, ORIENT_OO, OPT_DEFAULT, OPT_LOCAL
from .utils import tools
[docs]
class FDH:
"""Free Disposal Hull (FDH)
"""
[docs]
def __init__(self, y, x, orient, yref=None, xref=None):
"""FDH model
Args:
y (float): output variable.
x (float): input variables.
orient (String): ORIENT_IO (input orientation) or ORIENT_OO (output orientation)
yref (String, optional): reference output. Defaults to None.
xref (String, optional): reference inputs. Defaults to None.
"""
# TODO(error/warning handling): Check the configuration of the model exist
self.y, self.x = tools.assert_valid_mupltiple_y_data(y, x)
self.orient = orient
if type(yref) != type(None):
self.yref, self.xref = tools.assert_valid_reference_data(
self.y, self.x, yref, xref)
else:
self.yref, self.xref = self.y, self.x
# Initialize FDH model
self.__model__ = ConcreteModel()
self.__model__.R = Set(initialize=range(len(self.yref)))
# Initialize sets
self.__model__.I = Set(initialize=range(len(self.y)))
self.__model__.J = Set(initialize=range(len(self.x[0])))
self.__model__.K = Set(initialize=range(len(self.y[0])))
# Initialize variable
self.__model__.theta = Var(self.__model__.I, doc='efficiency')
self.__model__.lamda = Var(
self.__model__.I, self.__model__.R, within=Binary, doc='intensity variables')
# Setup the objective function and constraints
if self.orient == ORIENT_IO:
self.__model__.objective = Objective(
rule=self.__objective_rule(), sense=minimize, doc='objective function')
else:
self.__model__.objective = Objective(
rule=self.__objective_rule(), sense=maximize, doc='objective function')
self.__model__.input = Constraint(
self.__model__.I, self.__model__.J, rule=self.__input_rule(), doc='input constraint')
self.__model__.output = Constraint(
self.__model__.I, self.__model__.K, rule=self.__output_rule(), doc='output constraint')
self.__model__.vrs = Constraint(
self.__model__.I, rule=self.__vrs_rule(), doc='variable return to scale rule')
# Optimize model
self.optimization_status, self.problem_status = 0, 0
def __objective_rule(self):
"""Return the proper objective function"""
def objective_rule(model):
return sum(model.theta[i] for i in model.I)
return objective_rule
def __input_rule(self):
"""Return the proper input constraint"""
if self.orient == ORIENT_IO:
def input_rule(model, o, j):
return model.theta[o]*self.x[o][j] >= sum(model.lamda[o, r]*self.xref[r][j] for r in model.R)
return input_rule
elif self.orient == ORIENT_OO:
def input_rule(model, o, j):
return sum(model.lamda[o, r] * self.xref[r][j] for r in model.R) <= self.x[o][j]
return input_rule
def __output_rule(self):
"""Return the proper output constraint"""
if self.orient == ORIENT_IO:
def output_rule(model, o, k):
return sum(model.lamda[o, r] * self.yref[r][k] for r in model.R) >= self.y[o][k]
return output_rule
elif self.orient == ORIENT_OO:
def output_rule(model, o, k):
return model.theta[o]*self.y[o][k] <= sum(model.lamda[o, r]*self.yref[r][k] for r in model.R)
return output_rule
def __vrs_rule(self):
def vrs_rule(model, o):
return sum(model.lamda[o, r] for r in model.R) == 1
return vrs_rule
[docs]
def optimize(self, email=OPT_LOCAL, solver=OPT_DEFAULT):
"""Optimize the function by requested method
Args:
email (string): The email address for remote optimization. It will optimize locally if OPT_LOCAL is given.
solver (string): The solver chosen for optimization. It will optimize with default solver if OPT_DEFAULT is given.
"""
# TODO(error/warning handling): Check problem status after optimization
self.problem_status, self.optimization_status = tools.optimize_model(
self.__model__, email, CET_ADDI, solver)
[docs]
def display_status(self):
"""Display the status of problem"""
print(self.optimization_status)
[docs]
def display_theta(self):
"""Display theta value"""
tools.assert_optimized(self.optimization_status)
self.__model__.theta.display()
[docs]
def display_lamda(self):
"""Display lamda value"""
tools.assert_optimized(self.optimization_status)
self.__model__.lamda.display()
[docs]
def get_status(self):
"""Return status"""
return self.optimization_status
[docs]
def get_theta(self):
"""Return theta value by array"""
tools.assert_optimized(self.optimization_status)
theta = list(self.__model__.theta[:].value)
return np.asarray(theta)
[docs]
def get_lamda(self):
"""Return lamda value by array"""
tools.assert_optimized(self.optimization_status)
lamda = np.asarray([i + tuple([j]) for i, j in zip(list(self.__model__.lamda),
list(self.__model__.lamda[:, :].value))])
lamda = pd.DataFrame(lamda, columns=['Name', 'Key', 'Value'])
lamda = lamda.pivot(index='Name', columns='Key', values='Value')
return lamda.to_numpy()