# Imports
import numpy as np
import gurobipy as gbp
import datetime as dt

#  Constants
Aij = np.random.randint(5, 50, 250000)
Aij = Aij.reshape(500,500)
AijSum = np.sum(Aij)
Cj = np.random.randint(10, 20, 500)
CjSum = np.sum(Cj)
Bi = np.random.randint(10, 20, 500)
BiSum = np.sum(Bi)

# Matrix Shape
rows = range(len(Aij))
cols = range(len(Aij[0]))


"""
Explanation: <font size='5' face='Courier New'><h1 align="center"><i>The Primal & Dual Linear Programming Problems: Standard Form</i></h1></font> 
<font face='Times New Roman' size='6'><h3 align="center"><u>James&nbsp;D.&nbsp;Gaboardi</u></h3></font>

<font face='Times New Roman' size='5'><h3 align="center">Florida State University &nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp; Department of Geography</h3></font>


<p><font size='4' face='Times New Roman'>Adapted from:</font></p>
<p><font size='4' face='Times New Roman'><b>Daskin, M. S.</b> 1995. <i>Network and Discrete Location: Models, Algorithms, and Applications</i>. Hoboken, NJ, USA: John Wiley & Sons, Inc.</font></p>


<font size='7' face='Times New Roman'><b>0. <u>Imports and Data Creation</u></b></font>
End of explanation
"""


# Instantiate Model
mPrimal_Standard_GUROBI = gbp.Model(' -- Standard Primal Linear Programming Problem -- ')

# Set Focus to Optimality
gbp.setParam('MIPFocus', 2)

# Decision Variables
desc_var = []
for dest in cols:
    desc_var.append([])
    desc_var[dest].append(mPrimal_Standard_GUROBI.addVar(vtype=gbp.GRB.CONTINUOUS, 
                                    name='y'+str(dest+1)))

# Surplus Variables
surp_var = []
for orig in rows:
    surp_var.append([])
    surp_var[orig].append(mPrimal_Standard_GUROBI.addVar(vtype=gbp.GRB.CONTINUOUS, 
                                   name='s'+str(orig+1)))

# Update Model
mPrimal_Standard_GUROBI.update()

#Objective Function
mPrimal_Standard_GUROBI.setObjective(gbp.quicksum(Cj[dest]*desc_var[dest][0] 
                        for dest in cols), 
                        gbp.GRB.MINIMIZE)

# Constraints
for orig in rows:
    mPrimal_Standard_GUROBI.addConstr(gbp.quicksum(Aij[orig][dest]*desc_var[dest][0] 
                                        for dest in cols) 
                                        - surp_var[orig][0] 
                                        - Bi[orig] == 0)

# Optimize
mPrimal_Standard_GUROBI.optimize()
# Write LP file
mPrimal_Standard_GUROBI.write('LP.lp')
print '\n*************************************************************************'
print '    |   Decision Variables'
for v in mPrimal_Standard_GUROBI.getVars():
    print '    |  ', v.VarName, '=', v.x
print '*************************************************************************'
val = mPrimal_Standard_GUROBI.objVal
print '    |   Objective Value ------------------ ', val
print '    |   Aij Sum -------------------------- ', AijSum
print '    |   Cj Sum --------------------------- ', CjSum
print '    |   Bi Sum --------------------------- ', BiSum
print '    |   Matrix Dimensions ---------------- ', Aij.shape
print '    |   Date/Time ------------------------ ', dt.datetime.now()
print '*************************************************************************'
print '-- Gurobi Standard Primal Linear Programming Problem --'
print '\nJames Gaboardi, 2015'

"""
Explanation: <font size='7' face='Times New Roman'><b>1. <u>Primal</u></b></font>
End of explanation
"""


# Instantiate Model
mDual_Standard_GUROBI = gbp.Model(' -- Standard Dual Linear Programming Problem -- ')

# Set Focus to Optimality
gbp.setParam('MIPFocus', 2)

# Decision Variables
desc_var = []
for orig in rows:
    desc_var.append([])
    desc_var[orig].append(mDual_Standard_GUROBI.addVar(vtype=gbp.GRB.CONTINUOUS, 
                                    name='u'+str(orig+1)))
# Slack Variables
slack_var = []
for dest in cols:
    slack_var.append([])
    slack_var[dest].append(mDual_Standard_GUROBI.addVar(vtype=gbp.GRB.CONTINUOUS, 
                                   name='t'+str(dest+1)))
# Update Model
mDual_Standard_GUROBI.update()

#Objective Function
mDual_Standard_GUROBI.setObjective(gbp.quicksum(Bi[orig]*desc_var[orig][0] 
                        for orig in rows), 
                        gbp.GRB.MAXIMIZE)
# Constraints
for dest in cols:
    mDual_Standard_GUROBI.addConstr(gbp.quicksum(Aij[orig][dest]*desc_var[orig][0] 
                        for orig in rows) + 
                        slack_var[dest][0] -
                        Cj[dest] == 0)

# Optimize
mDual_Standard_GUROBI.optimize()
# Write LP file
mDual_Standard_GUROBI.write('LP.lp')
print '\n*************************************************************************'
print '    |   Decision Variables'
for v in mDual_Standard_GUROBI.getVars():
    print '    |  ', v.VarName, '=', v.x
print '*************************************************************************'
val = mDual_Standard_GUROBI.objVal
print '    |   Objective Value ------------------ ', val
print '    |   Aij Sum -------------------------- ', AijSum
print '    |   Cj Sum --------------------------- ', CjSum
print '    |   Bi Sum --------------------------- ', BiSum
print '    |   Matrix Dimensions ---------------- ', Aij.shape
print '    |   Date/Time ------------------------ ', dt.datetime.now()
print '*************************************************************************'
print '-- Gurobi Standard Dual Linear Programming Problem --'
print '\nJames Gaboardi, 2015'

"""
Explanation: <font size='7' face='Times New Roman'><b>2. <u>Dual</u></b></font>
End of explanation
"""


