scheduling_disjunctive: model for the one machine total weighted completion time problem

    Disjunctive optimization model for the one machine total weighted
    completion time problem with release times.

    Parameters:
        - J: set of jobs
        - p[j]: processing time of job j
        - r[j]: earliest start time of job j
        - w[j]: weighted of job j; the objective is the sum of the weighted completion time

    Returns a model, ready to be solved.

def scheduling_disjunctive(J,p,r,w):
    """
    scheduling_disjunctive: model for the one machine total weighted completion time problem

    Disjunctive optimization model for the one machine total weighted
    completion time problem with release times.

    Parameters:
        - J: set of jobs
        - p[j]: processing time of job j
        - r[j]: earliest start time of job j
        - w[j]: weighted of job j; the objective is the sum of the weighted completion time

    Returns a model, ready to be solved.
    """
    model = Model("scheduling: disjunctive")

    M = max(r.values()) + sum(p.values())       # big M
    s,x = {},{}      # start time variable, x[j,k] = 1 if job j precedes job k, 0 otherwise
    for j in J:
        s[j] = model.addVar(lb=r[j], vtype="C", name="s(%s)"%j)
        for k in J:
            if j != k:
                x[j,k] = model.addVar(vtype="B", name="x(%s,%s)"%(j,k))


    for j in J:
        for k in J:
            if j != k:
                model.addCons(s[j] - s[k] + M*x[j,k] <= (M-p[j]), "Bound(%s,%s)"%(j,k))

            if j < k:
                model.addCons(x[j,k] + x[k,j] == 1, "Disjunctive(%s,%s)"%(j,k))

    model.setObjective(quicksum(w[j]*s[j] for j in J), "minimize")

    model.data = s,x
    return model