gpp -- model for the graph partitioning problem
    Parameters:
        - n: number of jobs
        - m: number of machines
        - p[i,j]: processing time of job i on machine j
    Returns a model, ready to be solved.

def permutation_flow_shop(n,m,p):
    """gpp -- model for the graph partitioning problem
    Parameters:
        - n: number of jobs
        - m: number of machines
        - p[i,j]: processing time of job i on machine j
    Returns a model, ready to be solved.
    """
    model = Model("permutation flow shop")

    x,s,f = {},{},{}
    for j in range(1,n+1):
        for k in range(1,n+1):
            x[j,k] = model.addVar(vtype="B", name="x(%s,%s)"%(j,k))

    for i in range(1,m+1):
        for k in range(1,n+1):
            s[i,k] = model.addVar(vtype="C", name="start(%s,%s)"%(i,k))
            f[i,k] = model.addVar(vtype="C", name="finish(%s,%s)"%(i,k))

    for j in range(1,n+1):
        model.addCons(quicksum(x[j,k] for k in range(1,n+1)) == 1, "Assign1(%s)"%(j))
        model.addCons(quicksum(x[k,j] for k in range(1,n+1)) == 1, "Assign2(%s)"%(j))

    for i in range(1,m+1):
        for k in range(1,n+1):
            if k != n:
                model.addCons(f[i,k] <= s[i,k+1], "FinishStart(%s,%s)"%(i,k))
            if i != m:
                model.addCons(f[i,k] <= s[i+1,k], "Machine(%s,%s)"%(i,k))

            model.addCons(s[i,k] + quicksum(p[i,j]*x[j,k] for j in range(1,n+1)) <= f[i,k],
                            "StartFinish(%s,%s)"%(i,k))

    model.setObjective(f[m,n], "minimize")

    model.data = x,s,f
    return model