"""
weber_soco.py:  model for solving the weber problem using soco.

Copyright (c) by Joao Pedro PEDROSO, Masahiro MURAMATSU and Mikio KUBO, 2012
"""
from gurobipy import *

def weber(I,x,y,w):
    """weber: model for solving the single source weber problem using soco.
    Parameters:
        - I: set of customers
        - x[i]: x position of customer i
        - y[i]: y position of customer i
        - w[i]: weight of customer i
    Returns a model, ready to be solved.
    """

    model = Model("weber")
    X,Y,z,xaux,yaux = {},{},{},{},{}
    X = model.addVar(lb=-GRB.INFINITY, vtype="C", name="X")
    Y = model.addVar(lb=-GRB.INFINITY, vtype="C", name="Y")
    for i in I:
        z[i] = model.addVar(vtype="C", name="z(%s)"%(i))    
        xaux[i] = model.addVar(lb=-GRB.INFINITY, vtype="C", name="xaux(%s)"%(i))
        yaux[i] = model.addVar(lb=-GRB.INFINITY, vtype="C", name="yaux(%s)"%(i))    
    model.update()
    
    for i in I:
        model.addConstr(xaux[i]*xaux[i] + yaux[i]*yaux[i] <= z[i]*z[i], "MinDist(%s)"%(i))
        model.addConstr(xaux[i] == (x[i]-X), "xAux(%s)"%(i))
        model.addConstr(yaux[i] == (y[i]-Y), "yAux(%s)"%(i))

    model.setObjective(quicksum(w[i]*z[i] for i in I), GRB.MINIMIZE)

    model.update()
    model.__data = X,Y,z
    return model


import random
def make_data(n,m):
    I = range(1,n+1)
    J = range(1,m+1)
    x,y,w = {},{},{}
    for i in I:
        x[i] = random.randint(0,100)
        y[i] = random.randint(0,100)
        w[i] = random.randint(1,5)
    return I,J,x,y,w

                
if __name__ == "__main__":
    import sys
    random.seed(3)
    n = 7
    m = 1
    I,J,x,y,w = make_data(n,m)
    print "data:"
    print "%s\t%8s\t%8s\t%8s" % ("i","x[i]","y[i]","w[i]")
    for i in I:
        print "%s\t%8g\t%8g\t%8g" % (i,x[i],y[i],w[i])
    print
    
    model = weber(I,x,y,w)
    model.optimize()
    X,Y,z = model.__data
    print "Optimal value=",model.ObjVal
    print "Selected position:",
    print "\t",(round(X.X),round(Y.X))
    print
    print "Solution:"
    print "%s\t%8s" % ("i","z[i]")
    for i in I:
        print "%s\t%8g" % (i,z[i].X)
    print
    try: # plot the result using networkx and matplotlib
        import networkx as NX
        import matplotlib.pyplot as P 
        P.clf() 
        G = NX.Graph()

        G.add_nodes_from(I)
        G.add_nodes_from(["D"])

        position = {}
        for i in I:
            position[i] = (x[i],y[i])
        position["D"] = (round(X.X),round(Y.X))

        NX.draw(G,pos=position,node_size=200,node_color="g",nodelist=I)
        NX.draw(G,pos=position,node_size=400,node_color="w",nodelist=["D"],alpha=0.5)
        P.savefig("weber.pdf",format="pdf",dpi=300)
        P.show()

    except ImportError:
        print "install 'networkx' and 'matplotlib' for plotting"





def weber_MS(I,J,x,y,w):
    """weber -- model for solving the weber problem using soco (multiple source version).
    Parameters:
        - I: set of customers
        - J: set of potential facilities
        - x[i]: x position of customer i
        - y[i]: y position of customer i
        - w[i]: weight of customer i
    Returns a model, ready to be solved.
    """
    M = max([((x[i]-x[j])**2 + (y[i]-y[j])**2) for i in I for j in I])
    model = Model("weber - multiple source")
    X,Y,v,u = {},{},{},{}
    xaux,yaux,uaux = {},{},{}
    for j in J:
        X[j] = model.addVar(lb=-GRB.INFINITY, vtype="C", name="X(%s)"%j)
        Y[j] = model.addVar(lb=-GRB.INFINITY, vtype="C", name="Y(%s)"%j)
        for i in I:
            v[i,j] = model.addVar(vtype="C", name="v(%s,%s)"%(i,j))    
            u[i,j] = model.addVar(vtype="B", name="u(%s,%s)"%(i,j))
            xaux[i,j] = model.addVar(lb=-GRB.INFINITY, vtype="C", name="xaux(%s,%s)"%(i,j))    
            yaux[i,j] = model.addVar(lb=-GRB.INFINITY, vtype="C", name="yaux(%s,%s)"%(i,j))    
            uaux[i,j] = model.addVar(vtype="C", name="uaux(%s,%s)"%(i,j))    
            
    model.update()
    
    for i in I:
        model.addConstr(quicksum(u[i,j] for j in J) == 1, "Assign(%s)"%i)
        for j in J:
            model.addConstr(xaux[i,j]*xaux[i,j] + yaux[i,j]*yaux[i,j] <= v[i,j]*v[i,j], "MinDist(%s,%s)"%(i,j))
            model.addConstr(xaux[i,j] == (x[i]-X[j]), "xAux(%s,%s)"%(i,j))
            model.addConstr(yaux[i,j] == (y[i]-Y[j]), "yAux(%s,%s)"%(i,j))
            model.addConstr(uaux[i,j] >= v[i,j] - M*(1-u[i,j]), "uAux(%s,%s)"%(i,j))

    model.setObjective(quicksum(w[i]*uaux[i,j] for i in I for j in J), GRB.MINIMIZE)

    model.update()
    model.__data = X,Y,v,u
    return model


if __name__ == "__main__":
    random.seed(3)
    n = 7
    m = 1
    I,J,x,y,w = make_data(n,m)
    model = weber_MS(I,J,x,y,w)
    model.optimize()
    X,Y,w,z = model.__data
    print "Optimal value=",model.ObjVal
    print "Selected positions:"
    for j in J:
        print "\t",(X[j].X,Y[j].X)
    for (i,j) in sorted(w.keys()):
        print "\t",(i,j),w[i,j].X,z[i,j].X
     
    EPS = 1.e-4
    edges = [(i,j) for (i,j) in z if z[i,j].X > EPS]
 
    try: # plot the result using networkx and matplotlib
        import networkx as NX
        import matplotlib.pyplot as P 
        P.clf() 
        G = NX.Graph()
     
        G.add_nodes_from(I)
        G.add_nodes_from("%s"%j for j in J)     # for distinguishing J from I, make nodes as strings
        for (i,j) in edges:
            G.add_edge(i,"%s"%j)
     
        position = {}
        for i in I:
            position[i] = (x[i],y[i])
        for j in J:
            position["%s"%j] = (X[j].X,Y[j].X)
        print position
     
        NX.draw(G,position,node_color="g",nodelist=I)
        NX.draw(G,position,node_color="w",nodelist=["%s"%j for j in J])
        P.show()
    except ImportError:
        print "install 'networkx' and 'matplotlib' for plotting"


