Esempio n. 1
0
    def create_model():
        # create solver instance
        s = Model()

        # add some variables
        x = s.addVar("x", obj = -1.0, vtype = "I", lb=-10)
        y = s.addVar("y", obj = 1.0, vtype = "I", lb=-1000)
        z = s.addVar("z", obj = 1.0, vtype = "I", lb=-1000)

        # add some constraint
        s.addCons(314*x + 867*y + 860*z == 363)
        s.addCons(87*x + 875*y - 695*z == 423)

        # create conshdlr and include it to SCIP
        conshdlr = MyConshdlr(shouldtrans=True, shouldcopy=False)
        s.includeConshdlr(conshdlr, "PyCons", "custom constraint handler implemented in python",
                          sepapriority = 1, enfopriority = -1, chckpriority = 1, sepafreq = 10, propfreq = 50,
                          eagerfreq = 1, maxprerounds = -1, delaysepa = False, delayprop = False, needscons = True,
                          presoltiming = SCIP_PRESOLTIMING.FAST, proptiming = SCIP_PROPTIMING.BEFORELP)

        cons1 = s.createCons(conshdlr, "cons1name")
        ids.append(id(cons1))
        cons2 = s.createCons(conshdlr, "cons2name")
        ids.append(id(cons2))
        conshdlr.createData(cons1, 10, "cons1_anothername")
        conshdlr.createData(cons2, 12, "cons2_anothername")

        # add these constraints
        s.addPyCons(cons1)
        s.addPyCons(cons2)
        return s
Esempio n. 2
0
def create_sudoku():
    scip = Model("Sudoku")

    x = {}  # values of squares
    for row in range(9):
        for col in range(9):
            # some variables are fix
            if init[row * 9 + col] != 0:
                x[row, col] = scip.addVar(vtype="I",
                                          lb=init[row * 9 + col],
                                          ub=init[row * 9 + col],
                                          name="x(%s,%s)" % (row, col))
            else:
                x[row, col] = scip.addVar(vtype="I",
                                          lb=1,
                                          ub=9,
                                          name="x(%s,%s)" % (row, col))
            var = x[row, col]
            #print("built var ", var.name, " with bounds: (%d,%d)"%(var.getLbLocal(), var.getUbLocal()))

    conshdlr = ALLDIFFconshdlr()

    # hoping to get called when all vars have integer values
    scip.includeConshdlr(conshdlr,
                         "ALLDIFF",
                         "All different constraint",
                         propfreq=1,
                         enfopriority=-10,
                         chckpriority=-10)

    # row constraints; also we specify the domain of all variables here
    # TODO/QUESTION: in principle domain is of course associated to the var and not the constraint. it should be "var.data"
    # But ideally that information would be handle by SCIP itself... the reason we can't is because domain holes is not implemented, right?
    domains = {}
    for row in range(9):
        vars = []
        for col in range(9):
            var = x[row, col]
            vars.append(var)
            vals = set(
                range(int(round(var.getLbLocal())),
                      int(round(var.getUbLocal())) + 1))
            domains[var.ptr()] = vals
        # this is kind of ugly, isn't it?
        cons = scip.createCons(conshdlr, "row_%d" % row)
        #print("in test: received a constraint with id ", id(cons)) ### DELETE
        cons.data = SimpleNamespace(
        )  # so that data behaves like an instance of a class (ie, cons.data.whatever is allowed)
        cons.data.vars = vars
        cons.data.domains = domains
        scip.addPyCons(cons)

    # col constraints
    for col in range(9):
        vars = []
        for row in range(9):
            var = x[row, col]
            vars.append(var)
        cons = scip.createCons(conshdlr, "col_%d" % col)
        cons.data = SimpleNamespace()
        cons.data.vars = vars
        cons.data.domains = domains
        scip.addPyCons(cons)

    # square constraints
    for idx1 in range(3):
        for idx2 in range(3):
            vars = []
            for row in range(3):
                for col in range(3):
                    var = x[3 * idx1 + row, 3 * idx2 + col]
                    vars.append(var)
            cons = scip.createCons(conshdlr, "square_%d-%d" % (idx1, idx2))
            cons.data = SimpleNamespace()
            cons.data.vars = vars
            cons.data.domains = domains
            scip.addPyCons(cons)

    #scip.setObjective()

    return scip, x
Esempio n. 3
0
def create_sudoku():
    scip = Model("Sudoku")

    x = {} # values of squares
    for row in range(9):
        for col in range(9):
            # some variables are fix
            if init[row*9 + col] != 0:
                x[row,col] = scip.addVar(vtype = "I", lb = init[row*9 + col], ub = init[row*9 + col], name = "x(%s,%s)" % (row,col))
            else:
                x[row,col] = scip.addVar(vtype = "I", lb = 1, ub = 9, name = "x(%s,%s)" % (row,col))
            var = x[row,col]
            #print("built var ", var.name, " with bounds: (%d,%d)"%(var.getLbLocal(), var.getUbLocal()))

    conshdlr = ALLDIFFconshdlr()

    # hoping to get called when all vars have integer values
    scip.includeConshdlr(conshdlr, "ALLDIFF", "All different constraint", propfreq = 1, enfopriority = -10, chckpriority = -10)

    # row constraints; also we specify the domain of all variables here
    # TODO/QUESTION: in principle domain is of course associated to the var and not the constraint. it should be "var.data"
    # But ideally that information would be handle by SCIP itself... the reason we can't is because domain holes is not implemented, right?
    domains = {}
    for row in range(9):
        vars = []
        for col in range(9):
            var = x[row,col]
            vars.append(var)
            vals = set(range(int(round(var.getLbLocal())), int(round(var.getUbLocal())) + 1))
            domains[var.ptr()] = vals
        # this is kind of ugly, isn't it?
        cons = scip.createCons(conshdlr, "row_%d" % row)
        #print("in test: received a constraint with id ", id(cons)) ### DELETE
        cons.data = SimpleNamespace() # so that data behaves like an instance of a class (ie, cons.data.whatever is allowed)
        cons.data.vars = vars
        cons.data.domains = domains
        scip.addPyCons(cons)

    # col constraints
    for col in range(9):
        vars = []
        for row in range(9):
            var = x[row,col]
            vars.append(var)
        cons = scip.createCons(conshdlr, "col_%d"%col)
        cons.data = SimpleNamespace()
        cons.data.vars = vars
        cons.data.domains = domains
        scip.addPyCons(cons)

    # square constraints
    for idx1 in range(3):
        for idx2 in range(3):
            vars = []
            for row in range(3):
                for col in range(3):
                    var = x[3*idx1 + row, 3*idx2 + col]
                    vars.append(var)
            cons = scip.createCons(conshdlr, "square_%d-%d"%(idx1, idx2))
            cons.data = SimpleNamespace()
            cons.data.vars = vars
            cons.data.domains = domains
            scip.addPyCons(cons)


    #scip.setObjective()

    return scip, x
Esempio n. 4
0
def repairlp(model, directory, mode):
    """
    Repair a the LP solution of a MIP model by rounding, constraint relaxation, local branching
    :param model:
    :return:
    """
    MIP_model = model
    # model_copy = Model('model orig copy', sourceModel=MIP_model, origcopy=True)
    print("original model:", MIP_model)
    print("N of variables: {}".format(MIP_model.getNVars()))
    print("N of constraints: {}".format(MIP_model.getNConss()))
    # print("copyed model:", model_copy)
    MIP_model.setPresolve(pyscipopt.SCIP_PARAMSETTING.OFF)
    MIP_model.setHeuristics(pyscipopt.SCIP_PARAMSETTING.OFF)
    MIP_model.setSeparating(pyscipopt.SCIP_PARAMSETTING.OFF)
    MIP_model.setIntParam("lp/solvefreq", 0)
    MIP_model.setParam("limits/nodes", 1)

    # MIP_model.setParam("limits/solutions", 1)
    MIP_model.optimize()
    #
    status = MIP_model.getStatus()
    lp_status = MIP_model.getLPSolstat()
    stage = MIP_model.getStage()
    n_sols = MIP_model.getNSols()

    print("* Model status: %s" % status)
    print("* Solve stage: %s" % stage)
    print("* LP status: %s" % lp_status)
    print('* number of sol : ', n_sols)

    if status == 'optimal':
        print("Optimal value:", MIP_model.getObjVal())
        # for v in pyscipopt_model.getVars():
        #     if v.name != "n":
        #         print("%s: %d" % (v, pyscipopt_model.getVal(v)))
    else:
        print("Not optimal")
        print("Obj    value:", MIP_model.getObjVal())
        print("LP Obj value:", MIP_model.getLPObjVal())
        lpcands, lpcandssol, lpcadsfrac, nlpcands, npriolpcands, nfracimplvars = MIP_model.getLPBranchCands()
        print("No. of branching cands", nlpcands)
        # print("Branching cands  : ", lpcands)
        # print("Branching values :", lpcandssol)
        # print("fractional part of cands:", lpcadsfrac )

        # for v in pyscipopt_model.getVars():
        #     if v.name != "n":
        #         print("%s: %d" % (v, pyscipopt_model.getVal(v)))

        sol_lp = MIP_model.createLPSol() # get current LP solution
        # print("LP solution:", sol_lp)

        # round the LP solution to nearest integer
        for c in range(0, nlpcands):
            lpcand = lpcands[c]
            lpcand_val = lpcandssol[c]
            rounded_cand_val = MIP_model.floor(lpcand_val + 0.5)
            # rounded_cand_val = MIP_model.floor(lpcand_val + 0.5)
            assert MIP_model.isFeasIntegral(rounded_cand_val), "rounded value is not integral"
            MIP_model.setSolVal(sol_lp, lpcand, rounded_cand_val)

        # created a sub-MIP and copy rounded solution of MIP to subMIP

        subMIP_model = Model('repair subMIP model')
        # subMIP_model.copyParamSettings(MIP_model)
        sol_subMIP = subMIP_model.createSol()

        MIP_vars = MIP_model.getVars()
        n_vars = MIP_model.getNVars()

        subMIP_vars = subMIP_model.getVars()
        n_sub_vars = subMIP_model.getNVars()
        # print("no. of sub_MIP variables: ", n_sub_vars)

        # add variables of original MIP to subMIP and set obj coefficient to 0(by default)
        for i in range(0, n_vars):
            lb = MIP_vars[i].getLbGlobal()
            ub = MIP_vars[i].getUbGlobal()
            var_type = MIP_vars[i].vtype()
            value = MIP_model.getSolVal(sol_lp, MIP_vars[i])
            subMIP_var = subMIP_model.addVar("sub_"+ MIP_vars[i].name, var_type, lb, ub, 0.0)
            subMIP_model.setSolVal(sol_subMIP, subMIP_var, value)



        subMIP_vars = subMIP_model.getVars()
        n_sub_vars = subMIP_model.getNVars()
        print("no. of MIP variables: ", n_vars)
        print("no. of sub_MIP variables: ", n_sub_vars)
        # print("list of sub_MIP variables: ", subMIP_vars)
        # print("list of sub_MIP rounded solution: ", sol_subMIP)

        # for i in range(n_sub_vars):
        #     val = MIP_model.getSolVal(sol_lp, MIP_vars[i])
        #     subMIP_model.setSolVal(sol_subMIP, subMIP_vars[i], val)

        # modify the sub-MIP by relaxing the violated constrants of rounded solution

        # conss = subMIP_model.getConss()
        # n_conss = subMIP_model.getNConss()
        # print("Constraints: ", conss)
        # print("number of constraints:", n_conss)

        rows = MIP_model.getLPRowsData()
        n_rows = MIP_model.getNLPRows()
        print("number of rows:", n_rows)
        slacks = []
        n_violatedcons = 0
        vars_slack = []

        for i in range(0,n_rows):

            constant = rows[i].getConstant()
            lhs = rows[i].getLhs()
            rhs = rows[i].getRhs()
            vals = rows[i].getVals()
            n_nonzeros = rows[i].getNNonz()
            cols = rows[i].getCols()
            rowsol_activity = MIP_model.getRowSolActivity(rows[i], sol_lp)

            cons_vars = numpy.empty(n_nonzeros, dtype = numpy.object)

            # compute the coefficient of slack variables
            if (MIP_model.isFeasLT(rowsol_activity,lhs)):
                slack_coeff = lhs - rowsol_activity
                n_violatedcons += 1
            elif MIP_model.isFeasGT(rowsol_activity, rhs):
                slack_coeff = rhs - rowsol_activity
                n_violatedcons +=1
            else:
                slack_coeff = 0.0
            slacks.append(slack_coeff)

            for j in range(0, n_nonzeros):
                var = cols[j].getVar()
                pos = var.getProbindex()
                cons_vars[j] = subMIP_vars[pos]

            # create the constraint of original MIP for subMIP
            constraint = subMIP_model.createConsBasicLinear(rows[i].getName(), n_nonzeros, cons_vars, vals, lhs, rhs)
            for j in range(0, n_nonzeros): #release cons_vars variables after creating a constraint
                subMIP_model.releaseVar(cons_vars[j])

            # add a slack variable for violated constraints (slack coefficient != 0)
            if not subMIP_model.isFeasEQ(slack_coeff, 0.0):
                var_slack = subMIP_model.addVar(name="artificialslack_"+str(i),vtype='BINARY', lb=0.0, ub=1.0, obj=1.0)
                subMIP_model.setSolVal(sol_subMIP,var_slack, 1.0)
                vars_slack.append(var_slack)

                subMIP_model.addConsCoeff(constraint, var_slack, slack_coeff)
                subMIP_model.releaseVar(var_slack) #release slack variable after calling addConsCoeff

            # add the relaxed constriant to subMIP
            subMIP_model.addPyCons(constraint)


        print("sub-MIP relaxed!")

        n_conss = subMIP_model.getNConss()
        n_vars = subMIP_model.getNVars()
        print("number of  variables after relaxation: ", n_vars)
        print("number of violated constraints: ", n_violatedcons )
        print("repaired-sub-MIP number of constraints:", n_conss)
        # print("Variable set of sub-MIP: ", subMIP_model.getVars())
        # conss = subMIP_model.getConss()
        # print("repaired-sub-MIP Constraints: ", conss)

        feasible = subMIP_model.checkSol(solution=sol_subMIP)
        if feasible:
            # print("the trivial solution of subMIP is feasible ")
            subMIP_model.addSol(sol_subMIP, False)
            print("the feasible solution of subMIP is added to subMIP")
        else:
            print("Error: the trivial solution of subMIP is not feasible!")
        # bestSol = subMIP_model.getBestSol()
        # print("BestSol: ", bestSol)
        # print("BestObj: ", subMIP_model.getSolObjVal(sol=bestSol))

        # collect the index of slack variables in subMIP
        indexlist_varsslack = []
        for i in range(len(vars_slack)):
            pos = vars_slack[i].getProbindex()
            indexlist_varsslack.append(pos)

        n_binvars = subMIP_model.getNBinVars()
        vars = subMIP_model.getVars()

        n_supportbinvars = 0
        for i in range(n_binvars):
            val = subMIP_model.getSolVal(sol_subMIP, vars[i])
            assert subMIP_model.isFeasIntegral(val), "Error: Value of a binary varialbe is not integral!"
            if subMIP_model.isFeasEQ(val, 1.0):
                n_supportbinvars += 1

        neigh_sizes = []
        objs = []
        t = []
        subMIP_model.resetParams()

        nsample = 41
        for i in range(nsample):

            # create a copy of the MIP to be 'locally branched'

            # subMIP_copy = Model('model orig copy', sourceModel=subMIP_model, origcopy=True)
            subMIP_copy, subMIP_copy_vars, success = subMIP_model.createCopy(problemName='subMIPmodelCopy', origcopy=True)
            sol_subMIP_copy = subMIP_copy.createSol()

            # create a primal solution for the copy MIP by copying the solution of original MIP
            n_vars = subMIP_model.getNVars()
            subMIP_vars = subMIP_model.getVars()
            for j in range(n_vars):
                val = subMIP_model.getSolVal(sol_subMIP, subMIP_vars[j])
                subMIP_copy.setSolVal(sol_subMIP_copy, subMIP_copy_vars[j], val)
            feasible = subMIP_copy.checkSol(solution=sol_subMIP_copy)
            # print("Vars: ",subMIP_copy.getVars())
            if feasible:
                # print("the trivial solution of subMIP is feasible ")
                subMIP_copy.addSol(sol_subMIP_copy,False)
                print("the feasible solution of subMIP_copy is added to subMIP_copy")
            else:
                print("Error: the trivial solution of subMIP_copy is not feasible!")

            initial_obj = subMIP_copy.getSolObjVal(sol_subMIP_copy)
            print("Initial obj before LB: {}".format(initial_obj))

            # add LB constraint to subMIP model

            # if nsample==21:
            #     if i<10:
            #         alpha = 0.1 * (i+1)
            #     elif i<20:
            #         alpha = (i + 1 - 10)
            #     else:
            #         alpha = i
            #     neigh_size = alpha * n_violatedcons
            #
            # elif nsample==41:
            #     if i < 11:
            #         alpha = 0.01 * i
            #     elif i < 31:
            #         alpha = 0.02 * (i - 5)
            #     else:
            #         alpha = 0.05 * (i - 20)
            #
            #     neigh_size = alpha * n_binvars

            # sample strategy: 0.01 over [0, 0.1], 0.02 over [0.1, 0.5], 0.05 over [0.5, 1.0]
            if nsample==41:
                if i < 11:
                    alpha = 0.01 * i
                elif i < 31:
                    alpha = 0.02 * (i - 5)
                else:
                    alpha = 0.05 * (i - 20)

                if mode == 'repair-slackvars':
                    neigh_size = alpha * n_violatedcons
                    subMIP_copy = addLBConstraintAsymJustslackvars(subMIP_copy, sol_subMIP_copy, neigh_size, indexlist_varsslack)
                elif mode == 'repair-supportbinvars':
                    neigh_size = alpha * n_supportbinvars #
                    subMIP_copy = addLBConstraintAsymmetric(subMIP_copy, sol_subMIP_copy, neigh_size)
                elif mode == 'repair-binvars':
                    neigh_size = alpha * n_binvars
                    subMIP_copy = addLBConstraint(subMIP_copy, sol_subMIP_copy, neigh_size)

            subMIP_copy.setParam('limits/time', 30)
            subMIP_copy.optimize()

            status = subMIP_copy.getStatus()
            best_obj = subMIP_copy.getSolObjVal(subMIP_copy.getBestSol())
            solving_time = subMIP_copy.getSolvingTime() # total time used for solving (including presolving) the current problem

            print('Status: {}'.format(status),
                  "Best obj: {}".format(best_obj),
                  'Solving time: {}'.format(solving_time)
                      )

            neigh_sizes.append(alpha)
            objs.append(best_obj)
            t.append(solving_time)

        for i in range(len(t)):
            print('Neighsize: {:.4f}'.format(neigh_sizes[i]),
                  "Best obj: {:.4f}".format(objs[i]),
                  'Solving time: {:.4f}'.format(t[i])
                  )

        neigh_sizes = numpy.array(neigh_sizes).reshape(-1).astype('float64')
        # neigh_sizes = numpy.log10(neigh_sizes)
        t = numpy.array(t).reshape(-1)
        objs = numpy.array(objs).reshape(-1)

        numpy.savez(directory + MIP_model.getProbName(), neigh_sizes=neigh_sizes, objs=objs, t=t)