Exemple #1
0
    def gurobi_qp(self, qp):
        n = qp.H.shape[1]
        model = Model()
        x = {
            i: model.addVar(
                vtype=GRB.CONTINUOUS,
                name='x_%d' % i,
                lb=qp.lb,
                ub=qp.ub)
            for i in range(n)
        }
        model.update()  
        obj = QuadExpr()
        rows, cols = qp.H.nonzero()
        for i, j in zip(rows, cols):
            obj += 0.5 * x[i] * qp.H[i, j] * x[j]
        for i in range(n):
            obj += qp.f[i] * x[i]
        model.setObjective(obj, GRB.MINIMIZE)
        if qp.A is not None:
            A_nonzero_rows = get_nonzero_rows(qp.A)
            for i, row in A_nonzero_rows.items():
                model.addConstr(quicksum(qp.A[i, j] * x[j] for j in row) <= qp.b[i])

        if qp.Aeq is not None:
            A_nonzero_rows = get_nonzero_rows(qp.Aeq)
            for i, row in A_nonzero_rows.items():
                model.addConstr(quicksum(qp.Aeq[i, j] * x[j] for j in row) == qp.beq[i])
        model.optimize()
        self.solution = empty(n)
        for i in range(n):
            self.solution[i] = model.getVarByName('x_%d' % i).x
Exemple #2
0
def read_assignment(slots: typing.Collection[Slot],
                    flights: typing.Collection[Flight], model: grb.Model):
    assignments = {}
    for f in flights:
        for s in slots:
            if isfeasible(slot=s, flight=f):
                var = model.getVarByName(assignvarname(slot=s, flight=f))
                if abs(var.getAttr("X") - 1.0) < 0.001:
                    assignments[f] = s
    return assignments
Exemple #3
0
def get_new_flight_schedule(flights: typing.Iterable[Flight], n_slots: int, ip_model: gurobipy.Model) -> \
        typing.Set[Flight]:
    new_flights = set()
    for f in flights:
        found = False
        for t in range(0, n_slots):
            sched_var = ip_model.getVarByName('F' + str(f.flight_id) + 'T' +
                                              str(t))
            if sched_var is not None and abs(sched_var.getAttr("X") -
                                             1.0) < 0.0001:
                if not found:
                    found = True
                    new_flights.add(f.copy_reschedule(t))
                else:
                    print("Error: flight is double scheduled")

        if not found:
            remove_var = ip_model.getVarByName('R' + str(f.flight_id))
            if remove_var is None or remove_var.getAttr("X") < 0.000001:
                print("Error: flight is neither scheduled nor removed")
    return new_flights
Exemple #4
0
def reset_delay_costs(model: gurobipy.Model, delay_costs: typing.Mapping[typing.Tuple[int, int], float],
                      move_costs: typing.Mapping[typing.Tuple[int, int], float],
                      flights: typing.Iterable[flightsched.Flight],
                      n_slots: int):
    for f in flights:
        for t in range(0, n_slots):
            next_reschedule_var = model.getVarByName('F' + str(f.flight_id) + 'T' + str(t))
            if next_reschedule_var is not None:
                next_reschedule_var.setAttr(gurobipy.GRB.attr.Obj,
                                            -1 * (move_costs[f.flight_id, t]
                                                  + delay_costs[f.flight_id, t]))
    model.update()
    return
def find_feasible_start(n_colors, h, statespace, conflicts, verbose=False):
    
    model = Model("TimeFeasibility")
    p = len(h)
    y = {}
    # y[i,k] = if color i gets slot l
    for i in range(n_colors):
        for l in range(p):
            y[i,l] = model.addVar(vtype=GRB.BINARY, name="y_%s_%s" % (i,l))

    model.update()

    # Building constraints...    
    
    # c1: all get one
    for i in range(n_colors):
        model.addConstr( quicksum([ y[i, l] for l in range(p) ]) == 1, "c1")

    # c2: each slot needs to be used tops once
    for l in range(p):
        model.addConstr( quicksum([ y[i, l] for i in range(n_colors) ]) <= 1, "c2")    

    ### c3: statespace constraints
    for i in range(n_colors):
        #print l, h[l], i, [s for s in statespace]
        model.addConstr( quicksum([ y[i, l] for l in range(p) if h[l] not in statespace[i] ]) == 0, "c3")    
    
    # objective: minimize conflicts
    #obj = quicksum([ y[i,l] * y[j,l] for l in range(p) for i in range(n_colors) for j in range(i+1, n_colors) ]) 
    obj = quicksum([ sum(y[i,l] for i in range(n_colors)) for l in range(p)  ]) 
    #obj = 0
    model.setObjective(obj, GRB.MINIMIZE)
    
    if not verbose:
        model.params.OutputFlag = 0
    
    model.optimize()

    # return best room schedule
    color_schedule = []
    if model.status == GRB.INFEASIBLE:
        return color_schedule
                    
    for i in range(n_colors):
        for l in range(p):
            v = model.getVarByName("y_%s_%s" % (i,l)) 
            if v.x == 1:
                color_schedule.append(h[l])
                break
            
    return color_schedule
Exemple #6
0
class GurobiSolver(Solver):
    """ Implements the solver interface using gurobipy. """

    def __init__(self):
        Solver.__init__(self)
        self.problem = GurobiModel()
        

    def __getstate__(self):
        tmp_file = tempfile.mktemp(suffix=".lp")
        self.problem.update()
        self.problem.write(tmp_file)
        cplex_form = open(tmp_file).read()
        repr_dict = {'var_ids': self.var_ids, 'constr_ids': self.constr_ids, 'cplex_form': cplex_form}
        return repr_dict

    def __setstate__(self, repr_dict):
        tmp_file = tempfile.mktemp(suffix=".lp")
        open(tmp_file, 'w').write(repr_dict['cplex_form'])
        self.problem = read(tmp_file)
        self.var_ids = repr_dict['var_ids']
        self.constr_ids = repr_dict['constr_ids']

            
    def add_variable(self, var_id, lb=None, ub=None, vartype=VarType.CONTINUOUS, persistent=True, update_problem=True):
        """ Add a variable to the current problem.
        
        Arguments:
            var_id : str -- variable identifier
            lb : float -- lower bound
            ub : float -- upper bound
            vartype : VarType -- variable type (default: CONTINUOUS)
            persistent : bool -- if the variable should be reused for multiple calls (default: true)
            update_problem : bool -- update problem immediately (default: True)
        """
        lb = lb if lb is not None else -GRB.INFINITY
        ub = ub if ub is not None else GRB.INFINITY
        
        map_types = {VarType.BINARY: GRB.BINARY,
                     VarType.INTEGER: GRB.INTEGER,
                     VarType.CONTINUOUS: GRB.CONTINUOUS}

        if var_id in self.var_ids:
            var = self.problem.getVarByName(var_id)
            var.setAttr('lb', lb)
            var.setAttr('ub', ub)
            var.setAttr('vtype', map_types[vartype])
        else:
            self.problem.addVar(name=var_id, lb=lb, ub=ub, vtype=map_types[vartype])
            self.var_ids.append(var_id)
            
        if not persistent:
            self.temp_vars.add(var_id)
        
        if update_problem:
            self.problem.update()

    def add_constraint(self, constr_id, lhs, sense='=', rhs=0, persistent=True, update_problem=True):
        """ Add a variable to the current problem.
        
        Arguments:
            constr_id : str -- constraint identifier
            lhs : list [of (str, float)] -- variables and respective coefficients
            sense : {'<', '=', '>'} -- default '='
            rhs : float -- right-hand side of equation (default: 0)
            persistent : bool -- if the variable should be reused for multiple calls (default: True)
            update_problem : bool -- update problem immediately (default: True)
        """

        grb_sense = {'=': GRB.EQUAL,
                     '<': GRB.LESS_EQUAL,
                     '>': GRB.GREATER_EQUAL}

        if constr_id in self.constr_ids:
            constr = self.problem.getConstrByName(constr_id)
            self.problem.remove(constr)

        expr = quicksum([coeff * self.problem.getVarByName(r_id) for r_id, coeff in lhs if coeff])
        self.problem.addConstr(expr, grb_sense[sense], rhs, constr_id)
        self.constr_ids.append(constr_id)
            
        if not persistent:
            self.temp_constrs.add(constr_id)

        if update_problem:
            self.problem.update()
                                
    def remove_variable(self, var_id):
        """ Remove a variable from the current problem.
        
        Arguments:
            var_id : str -- variable identifier
        """
        if var_id in self.var_ids:
            self.problem.remove(self.problem.getVarByName(var_id))
            self.var_ids.remove(var_id)
    
    def remove_constraint(self, constr_id):
        """ Remove a constraint from the current problem.
        
        Arguments:
            constr_id : str -- constraint identifier
        """
        if constr_id in self.constr_ids:
            self.problem.remove(self.problem.getConstrByName(constr_id))
            self.constr_ids.remove(constr_id)
    
    def update(self):
        """ Update internal structure. Used for efficient lazy updating. """
        self.problem.update()

        
    def solve_lp(self, objective, model=None, constraints=None, get_shadow_prices=False, get_reduced_costs=False):
        """ Solve an LP optimization problem.

        Arguments:
            objective : dict (of str to float) -- reaction ids in the objective function and respective
                        coefficients, the sense is maximization by default
            model : ConstraintBasedModel -- model (optional, leave blank to reuse previous model structure)
            constraints : dict (of str to (float, float)) -- environmental or additional constraints (optional)
            get_shadow_prices : bool -- return shadow price information if available (optional, default: False)
            get_reduced_costs : bool -- return reduced costs information if available (optional, default: False)
        Returns:
            Solution
        """

        return self._generic_solve(None, objective, GRB.MAXIMIZE, model, constraints, get_shadow_prices,
                                   get_reduced_costs)

    def solve_qp(self, quad_obj, lin_obj, model=None, constraints=None, get_shadow_prices=False,
                 get_reduced_costs=False):
        """ Solve an LP optimization problem.

        Arguments:
            quad_obj : dict (of (str, str) to float) -- map reaction pairs to respective coefficients
            lin_obj : dict (of str to float) -- map single reaction ids to respective linear coefficients
            model : ConstraintBasedModel -- model (optional, leave blank to reuse previous model structure)
            constraints : dict (of str to (float, float)) -- overriding constraints (optional)
            get_shadow_prices : bool -- return shadow price information if available (default: False)
            get_reduced_costs : bool -- return reduced costs information if available (default: False)

        Returns:
            Solution
        """


        return self._generic_solve(quad_obj, lin_obj, GRB.MINIMIZE, model, constraints, get_shadow_prices,
                                   get_reduced_costs)

    def _generic_solve(self, quad_obj, lin_obj, sense, model=None, constraints=None, get_shadow_prices=False,
                       get_reduced_costs=False):

        if model:
            self.build_problem(model)

        problem = self.problem

        if constraints:
            old_constraints = {}
            for r_id, (lb, ub) in constraints.items():
                lpvar = problem.getVarByName(r_id)
                old_constraints[r_id] = (lpvar.lb, lpvar.ub)
                lpvar.lb = lb if lb is not None else -GRB.INFINITY
                lpvar.ub = ub if ub is not None else GRB.INFINITY
            problem.update()

        #create objective function
        quad_obj_expr = [q * problem.getVarByName(r_id1) * problem.getVarByName(r_id2)
                         for (r_id1, r_id2), q in quad_obj.items() if q] if quad_obj else []

        lin_obj_expr = [f * problem.getVarByName(r_id)
                        for r_id, f in lin_obj.items() if f] if lin_obj else []

        obj_expr = quicksum(quad_obj_expr + lin_obj_expr)

        problem.setObjective(obj_expr, sense)
        problem.update()

#        from datetime import datetime
#        self.problem.write("problem_{}.lp".format(str(datetime.now())))
        
        #run the optimization
        problem.optimize()

        status = status_mapping[problem.status] if problem.status in status_mapping else Status.UNKNOWN
        message = str(problem.status)

        if status == Status.OPTIMAL:
            fobj = problem.ObjVal
            values = OrderedDict([(r_id, problem.getVarByName(r_id).X) for r_id in self.var_ids])

            #if metabolite is disconnected no constraint will exist
            shadow_prices = OrderedDict([(m_id, problem.getConstrByName(m_id).Pi)
                                         for m_id in self.constr_ids
                                         if problem.getConstrByName(m_id)]) if get_shadow_prices else None

            reduced_costs = OrderedDict([(r_id, problem.getVarByName(r_id).RC)
                                         for r_id in self.var_ids]) if get_reduced_costs else None

            solution = Solution(status, message, fobj, values, shadow_prices, reduced_costs)
        else:
            solution = Solution(status, message)

        #reset old constraints because temporary constraints should not be persistent
        if constraints:
            for r_id, (lb, ub) in old_constraints.items():
                lpvar = problem.getVarByName(r_id)
                lpvar.lb, lpvar.ub = lb, ub
            problem.update()

        return solution
def find_ranking(comparisons, verbose=False):
    """
    Find the least changes to a set of comparisons so that they are consistent
    (transitive), it returns a topological ranking.

    comparisons     A dictionary with tuple keys in the form of (i, j), values
                    are scalars indicating the probability of i >= j. It is
                    assumed that comparisons are symmetric. The only way this
                    function it can handle '=' is by setting the probability to
                    0.5. In this case the MILP can treat it as > or < (but not
                    as =).
    verbose         Whether to print gurobi's progress.

    Returns:
        A tuple of size two:
        0) Ranking derived from topological sort (list of ranks in order of nodes);
        1) Sum of absolute changes to the comparisons.
    """
    # remove unnecessary variables
    comparisons = {(i, j) if i < j else (j, i): value if i < j else 1 - value
                   for (i, j), value in comparisons.items()}
    nodes = np.unique([i for ij in comparisons.keys() for i in ij])

    # define variables
    model = Model('comparison')
    model.setParam('OutputFlag', verbose)
    values = np.fromiter(comparisons.values(), dtype=float)
    assert values.max() <= 1 and values.min() >= 0
    # variables to encode the error of comparisons
    E_ij = model.addVars(comparisons.keys(),
                         name='e_ij',
                         vtype=GRB.CONTINUOUS,
                         ub=1.0 - values,
                         lb=-values)
    # variable to encode hard choice of >= and <=
    Z_ij = model.addVars(comparisons.keys(), name='z_ij', vtype=GRB.BINARY)
    # variables to help with transitivity in non-fully connected graphs
    R_i = model.addVars(nodes,
                        name='r_i',
                        vtype=GRB.CONTINUOUS,
                        lb=0,
                        ub=len(nodes))
    # variables to emulate abs
    T_ij_pos = {}
    T_ij_neg = {}
    index = (values != 1) & (values != 0)
    T_ij_pos = model.addVars(
        (ij for ij, value in comparisons.items() if value not in [0.0, 1.0]),
        vtype=GRB.CONTINUOUS,
        name='T_ij_pos',
        lb=0,
        ub=1 - values[index])
    T_ij_neg = model.addVars(
        (ij for ij, value in comparisons.items() if value not in [0.0, 1.0]),
        vtype=GRB.CONTINUOUS,
        name='T_ij_neg',
        lb=0,
        ub=values[index])
    model.update()

    # emulate abs for non-binary comparisons: E_ij = T_ij_pos - T_ij_neg
    model.addConstrs((E_ij[ij] == T_ij_pos[ij] - T_ij_neg[ij]
                      for ij in T_ij_pos), 'E_ij = T_ij_pos - T_ij_neg')

    # hard decision of >= and <=: z_ij == 1 <-> i > j
    model.addConstrs((E_ij[ij] + comparisons[ij] - 0.5 >= -1 + z_ij
                      for ij, z_ij in Z_ij.items()), 'z_ij_upper_bound')
    model.addConstrs((E_ij[ij] + comparisons[ij] - 0.5 <= z_ij
                      for ij, z_ij in Z_ij.items()), 'z_ij_lower_bound')

    # transitivity
    for (i, j), a in Z_ij.items():
        for k in nodes:
            j_, k_ = j, k
            if j > k:
                j_, k_ = k, j
            b = Z_ij.get((j_, k_), None)
            if b is None:
                continue
            elif j_ != j:
                b = 1 - b

            i_, k_ = i, k
            if i > k:
                i_, k_ = k, i
            c = Z_ij.get((i_, k_), None)
            if c is None:
                continue
            elif i_ != i:
                c = 1 - c
            # a <= b and b <= c -> a <= c
            model.addLConstr(a + b, GRB.LESS_EQUAL, 1 + c,
                             f'transitivity_ge_{i},{j},{k}')
            # a >= b and b >= c -> a >= c
            model.addLConstr(a + b, GRB.GREATER_EQUAL, c,
                             f'transitivity_le_{i},{j},{k}')

    # transitivity helper (for not-fully connected graphs)
    # also provides a latent rank
    big_m = len(nodes)
    model.addConstrs(((1 - z_ij) * big_m + R_i[i] >= R_i[j] + 1
                      for (i, j), z_ij in Z_ij.items()),
                     'rank_transitivity_larger')
    model.addConstrs(
        (z_ij * big_m + R_i[j] >= R_i[i] + 1 for (i, j), z_ij in Z_ij.items()),
        'rank_transitivity_smaller')

    # objective function
    objective = LinExpr()
    for ij, value in comparisons.items():
        if value == 1.0:
            objective += -E_ij[ij]
        elif value == 0.0:
            objective += E_ij[ij]
        else:
            objective += T_ij_pos[ij] + T_ij_neg[ij]
    model.setObjective(objective, GRB.MINIMIZE)

    # solve
    model.optimize()

    # verify abs emulation: one T_ij has to be 0
    for ij, value in T_ij_pos.items():
        assert value.X == 0 or T_ij_neg[ij] == 0, \
            f'T_{ij} pos {value.X} neg {T_ij_neg[ij]}'

    # find minimal Rs
    model_ = Model('comparison')
    model_.setParam('OutputFlag', verbose)
    R_i = model_.addVars(nodes,
                         name='r_i',
                         vtype=GRB.CONTINUOUS,
                         lb=0,
                         ub=len(nodes))
    for (i, j), z_ij in Z_ij.items():
        if z_ij.x == 1:
            model_.addConstr(R_i[i] >= R_i[j] + 1)
        else:
            model_.addConstr(R_i[j] >= R_i[i] + 1)
    model_.setObjective(R_i.sum(), GRB.MINIMIZE)
    model_.optimize()

    return [model_.getVarByName(f'r_i[{i}]').X for i in range(len(nodes))], \
        model.objVal
class SQModel(object):
    '''
    classdocs
    '''
    # Private model object
    __model = []
         
    # Private model variables
    __z0 = {}
    __z = {}
    __q = {}
         
    # Private model parameters
    __BackupCapacity = {}
    __bBackupLink = {}
    __links = []
    __nodes = []
    __capacity = []
    __epsilon = 1
    __impSample = {}
    __N = 1
        
    def __init__(self,imp_samp,nodes,links,capacity,epsilon,N,backup_link,link_capacity):
        '''
        Constructor
        '''
        self.__links = links
        self.__nodes = nodes
        self.__capacity = capacity
        self.__epsilon = epsilon
        self.__N = N
        self.__loadModel(imp_samp,backup_link,link_capacity)
                                 
    def __loadModel(self,imp_samp, backup_link,link_capacity):
                 
        # Create optimization model
        self.__model = Model('Backup')
     
        for i,j in self.__links:
            for k in range(self.__N):
                self.__z[k,i,j] = self.__model.addVar(lb=0,name='z[%s][%s][%s]' % (k,i,j))
        self.__model.update()
        
        for i,j in self.__links: 
            self.__z0[i,j] = self.__model.addVar(lb=-GRB.INFINITY,name='z0[%s][%s]' %(i,j))
        self.__model.update()
         
        for i,j in self.__links: 
            self.__q[i,j] = self.__model.addVar(lb=-GRB.INFINITY,name='q[%s][%s]' %(i,j))
        self.__model.update()
        
        self.__model.modelSense = GRB.MINIMIZE
        
        self.__model.setObjective(quicksum(self.__q[i,j] for i,j in self.__links))
        
        self.__model.update()
         
            
        #------------------------------------------------------------------------#
        #                    Constraints definition                              #
        #                                                                        #
        #                                                                        #
        #------------------------------------------------------------------------#
          
        # Buffer probability I
        for i,j in self.__links:
            self.__model.addConstr(self.__z0[i,j] + 1/(self.__N*self.__epsilon)*quicksum(self.__z[k,i,j]*imp_samp[k] for (k) in range(self.__N)) <= self.__q[i,j],'[CONST]Buffer_Prob_I[%s][%s]'%(i,j))
        self.__model.update()
         
        # Link capacity constraints
        for i,j in self.__links:
            for k in range(self.__N):
                self.__model.addConstr((quicksum(backup_link[i,j,s,d]*self.__capacity[k,s,d] for s,d in self.__links) - link_capacity[i,j] - self.__z0[i,j]) <= self.__z[k,i,j],'[CONST]Buffer_Prob_II[%s][%s][%s]' % (k,i,j))
        self.__model.update()
        
        # Link capacity constraints
        for i,j in self.__links:
            for k in range(self.__N):
                self.__model.addConstr(self.__z[k,i,j] >= 0,'[CONST]Buffer_Prob_III[%s][%s][%s]' % (k,i,j))
        self.__model.update()
         
    def optimize(self,MipGap, TimeLimit, LogLevel = None):
         
        self.__model.write('quantile.lp')
         
        if MipGap != None:
            self.__model.params.MIPGap = MipGap
        if TimeLimit != None:
            self.__model.params.timeLimit = TimeLimit
        # Compute optimal solution
        self.__model.optimize()
         
        # Print solution
        if self.__model.status == GRB.Status.OPTIMAL:
            #SuperQuantileSolution = self.__model.getAttr('x', self.__z0)
            SuperQuantileSolution = {}
            OptimalZnot = {}
            for i,j in self.__links:
                name='q[%s][%s]'%(i,j)
                v = self.__model.getVarByName(name)
                SuperQuantileSolution[i,j]=v.x
                name='z0[%s][%s]'%(i,j)
                v = self.__model.getVarByName(name)
                OptimalZnot[i,j]=v.x
            
            if LogLevel == 1:
                for v in self.__model.getVars():
                    print('%s %g' % (v.varName, v.x))
                                                
        else:
            print('Optimal value not found!\n')
            SuperQuantileSolution = {}
            OptimalZnot={}
                         
        return SuperQuantileSolution, OptimalZnot    
    
    def reset(self):
        '''
        Reset model solution
        '''
        self.__model.reset()
Exemple #9
0
class GurobiSolver(Solver):
    """ Implements the gurobi solver interface. """
    def __init__(self, model=None, env=None):
        Solver.__init__(self)
        if env:
            self.problem = GurobiModel(env=env)
        else:
            self.problem = GurobiModel()
        self.set_logging(False)
        self.set_parameters(default_parameters)
        if model:
            self.build_problem(model)

    def add_variable(self,
                     var_id,
                     lb=-inf,
                     ub=inf,
                     vartype=VarType.CONTINUOUS,
                     update=True):
        """ Add a variable to the current problem.

        Arguments:
            var_id (str): variable identifier
            lb (float): lower bound
            ub (float): upper bound
            vartype (VarType): variable type (default: CONTINUOUS)
            update (bool): update problem immediately (default: True)
        """

        lb = infinity_fix(lb)
        ub = infinity_fix(ub)

        if var_id in self.var_ids:
            var = self.problem.getVarByName(var_id)
            var.setAttr('lb', lb)
            var.setAttr('ub', ub)
            var.setAttr('vtype', vartype_mapping[vartype])
        else:
            self.problem.addVar(name=var_id,
                                lb=lb,
                                ub=ub,
                                vtype=vartype_mapping[vartype])
            self.var_ids.append(var_id)

        if update:
            self.problem.update()

    def add_constraint(self, constr_id, lhs, sense='=', rhs=0, update=True):
        """ Add a constraint to the current problem.

        Arguments:
            constr_id (str): constraint identifier
            lhs (dict): variables and respective coefficients
            sense (str): constraint sense (any of: '<', '=', '>'; default '=')
            rhs (float): right-hand side of equation (default: 0)
            update (bool): update problem immediately (default: True)
        """

        grb_sense = {
            '=': GRB.EQUAL,
            '<': GRB.LESS_EQUAL,
            '>': GRB.GREATER_EQUAL
        }

        if constr_id in self.constr_ids:
            constr = self.problem.getConstrByName(constr_id)
            self.problem.remove(constr)

        expr = quicksum(coeff * self.problem.getVarByName(r_id)
                        for r_id, coeff in lhs.items() if coeff)

        self.problem.addConstr(expr, grb_sense[sense], rhs, constr_id)
        self.constr_ids.append(constr_id)

        if update:
            self.problem.update()

    def remove_variable(self, var_id):
        """ Remove a variable from the current problem.

        Arguments:
            var_id (str): variable identifier
        """
        self.remove_variables([var_id])

    def remove_variables(self, var_ids):
        """ Remove variables from the current problem.

        Arguments:
            var_ids (list): variable identifiers
        """

        for var_id in var_ids:
            if var_id in self.var_ids:
                self.problem.remove(self.problem.getVarByName(var_id))
                self.var_ids.remove(var_id)

    def remove_constraint(self, constr_id):
        """ Remove a constraint from the current problem.

        Arguments:
            constr_id (str): constraint identifier
        """
        self.remove_constraints([constr_id])

    def remove_constraints(self, constr_ids):
        """ Remove constraints from the current problem.

        Arguments:
            constr_ids (list): constraint identifiers
        """

        for constr_id in constr_ids:
            if constr_id in self.constr_ids:
                self.problem.remove(self.problem.getConstrByName(constr_id))
                self.constr_ids.remove(constr_id)

    def set_bounds(self, bounds_dict):
        for var_id, bounds in bounds_dict.items():
            lpvar = self.problem.getVarByName(var_id)
            lpvar.lb = bounds[0] if bounds[0] is not None else GRB.INFINITY
            lpvar.ub = bounds[1] if bounds[1] is not None else GRB.INFINITY

    def update(self):
        """ Update internal structure. Used for efficient lazy updating. """
        self.problem.update()

    def set_objective(self, linear=None, quadratic=None, minimize=True):
        """ Set a predefined objective for this problem.

        Args:
            linear (dict): linear coefficients (optional)
            quadratic (dict): quadratic coefficients (optional)
            minimize (bool): solve a minimization problem (default: True)

        Notes:
            Setting the objective is optional. It can also be passed directly when calling **solve**.

        """

        lin_obj = []
        quad_obj = []

        if linear:

            if isinstance(linear, str):
                lin_obj = [1.0 * self.problem.getVarByName(linear)]
                if linear not in self.var_ids:
                    warn(
                        f"Objective variable not previously declared: {linear}"
                    )
            else:
                lin_obj = []
                for r_id, val in linear.items():
                    if r_id not in self.var_ids:
                        warn(
                            f"Objective variable not previously declared: {r_id}"
                        )
                    elif val != 0:
                        lin_obj.append(val * self.problem.getVarByName(r_id))

        if quadratic:
            quad_obj = []
            for (r_id1, r_id2), val in quadratic.items():
                if r_id1 not in self.var_ids:
                    warn(
                        f"Objective variable not previously declared: {r_id1}")
                elif r_id2 not in self.var_ids:
                    warn(
                        f"Objective variable not previously declared: {r_id2}")
                elif val != 0:
                    quad_obj.append(val * self.problem.getVarByName(r_id1) *
                                    self.problem.getVarByName(r_id2))

        obj_expr = quicksum(quad_obj + lin_obj)
        sense = GRB.MINIMIZE if minimize else GRB.MAXIMIZE

        self.problem.setObjective(obj_expr, sense)

    def solve(self,
              linear=None,
              quadratic=None,
              minimize=None,
              model=None,
              constraints=None,
              get_values=True,
              shadow_prices=False,
              reduced_costs=False,
              pool_size=0,
              pool_gap=None):
        """ Solve the optimization problem.

        Arguments:
            linear (str or dict): linear coefficients (or a single variable to optimize)
            quadratic (dict): quadratic objective (optional)
            minimize (bool): solve a minimization problem (default: True)
            model (CBModel): model (optional, leave blank to reuse previous model structure)
            constraints (dict): additional constraints (optional)
            get_values (bool or list): set to false for speedup if you only care about the objective (default: True)
            shadow_prices (bool): return shadow prices if available (default: False)
            reduced_costs (bool): return reduced costs if available (default: False)
            pool_size (int): calculate solution pool of given size (only for MILP problems)
            pool_gap (float): maximum relative gap for solutions in pool (optional)

        Returns:
            Solution: solution
        """

        if model:
            self.build_problem(model)

        problem = self.problem

        if constraints:
            old_constraints = {}
            for r_id, x in constraints.items():
                lb, ub = x if isinstance(x, tuple) else (x, x)
                if r_id in self.var_ids:
                    lpvar = problem.getVarByName(r_id)
                    old_constraints[r_id] = (lpvar.lb, lpvar.ub)
                    lpvar.lb = infinity_fix(lb)
                    lpvar.ub = infinity_fix(ub)
                else:
                    warn(
                        f"Constrained variable '{r_id}' not previously declared"
                    )
            problem.update()

        self.set_objective(linear, quadratic, minimize)

        # run the optimization
        if pool_size <= 1:

            problem.optimize()

            status = status_mapping.get(problem.status, Status.UNKNOWN)
            message = str(problem.status)

            if status == Status.OPTIMAL:
                fobj = problem.ObjVal
                values, s_prices, r_costs = None, None, None

                if get_values:
                    if isinstance(get_values, Iterable):
                        get_values = list(get_values)
                        values = {
                            r_id: problem.getVarByName(r_id).X
                            for r_id in get_values
                        }
                    else:
                        values = {
                            r_id: problem.getVarByName(r_id).X
                            for r_id in self.var_ids
                        }

                if shadow_prices:
                    s_prices = {
                        m_id: problem.getConstrByName(m_id).Pi
                        for m_id in self.constr_ids
                    }

                if reduced_costs:
                    r_costs = {
                        r_id: problem.getVarByName(r_id).RC
                        for r_id in self.var_ids
                    }

                solution = Solution(status, message, fobj, values, s_prices,
                                    r_costs)
            else:
                solution = Solution(status, message)

        else:

            problem.setParam(GRB.Param.PoolSearchMode, 2)
            self.set_parameter(Parameter.POOL_SIZE, pool_size)

            if pool_gap:
                self.set_parameter(Parameter.POOL_GAP, pool_gap)

            problem.optimize()

            status = status_mapping.get(problem.status, Status.UNKNOWN)

            if status == Status.OPTIMAL or status == Status.UNKNOWN:
                solution = self.get_solution_pool()
            else:
                solution = []

        # restore values of temporary constraints
        if constraints:
            for r_id, (lb, ub) in old_constraints.items():
                lpvar = problem.getVarByName(r_id)
                lpvar.lb, lpvar.ub = lb, ub
            problem.update()

        return solution

    def get_solution_pool(self, get_values=True):
        """ Return a solution pool for MILP problems.
        Must be called after using solve with pool_size argument > 0.

        Arguments:
            get_values (bool or list): set to false for speedup if you only care about the objective (default: True)

        Returns:
            list: list of Solution objects

        """
        solutions = []

        for i in range(self.problem.SolCount):
            self.problem.setParam(GRB.param.SolutionNumber, i)
            obj = self.problem.PoolObjVal
            if get_values:
                if isinstance(get_values, Iterable):
                    get_values = list(get_values)
                    values = {
                        r_id: self.problem.getVarByName(r_id).Xn
                        for r_id in get_values
                    }
                else:
                    values = {
                        r_id: self.problem.getVarByName(r_id).Xn
                        for r_id in self.var_ids
                    }
            else:
                values = None
            sol = Solution(fobj=obj, values=values)
            solutions.append(sol)

        return solutions

    def set_parameter(self, parameter, value):
        """ Set a parameter value for this optimization problem

        Arguments:
            parameter (Parameter): parameter type
            value (float): parameter value
        """

        if parameter in parameter_mapping:
            grb_param = parameter_mapping[parameter]
            self.problem.setParam(grb_param, value)
        else:
            raise Exception('Parameter unknown (or not yet supported).')

    def set_logging(self, enabled=False):
        """ Enable or disable log output:

        Arguments:
            enabled (bool): turn logging on (default: False)
        """

        self.problem.setParam('OutputFlag', 1 if enabled else 0)

    def write_to_file(self, filename):
        """ Write problem to file:

        Arguments:
            filename (str): file path
        """

        self.problem.write(filename)
Exemple #10
0
class SofdaiLp(object):
    def __init__(self, G, source, destination, Vms, VNFS):
        """
         This method is used for initialization
        Args:
            G: networkx graph
            source: Source node in the network
            destination: destination node in the network
            Vms: List of nodes which where we can place Vms
            VNFS: number of Vnfs 

        """
        self.model = Model()
        #        self.enabled_vm = dict()
        #        self.edge = dict()
        self.Vars = dict()
        self.F = list()

        #
        #Parameters
        self.G = G
        self.source = source
        self.destination = destination
        self.Vms = Vms
        self.VNFS = VNFS

    def check_source(self):
        """
        It check that source is the part of the network or invalid source. if 
        invalid then raise exception
        Return:
            True or false
        """
        source_bool = False
        for a in G.nodes():
            if a in self.source:
                source_bool = True
        if source_bool == False:
            raise Exception('Source is not in Network')

        return source_bool

    def check_destination(self):
        """
        It check that destination is the part of the  network or invalid 
        destination. if invalid then raise exception
        Return:
            True or false
        """
        destination_bool = {}
        destinations_bool = False
        for b in self.destination:
            destination_bool[b] = False
        for a in G.nodes():
            if a in self.destination:
                destination_bool[a] = True

        for c in destination_bool:
            if destination_bool[c] == False:
                raise Exception('Destination is not in Network')
            else:
                destinations_bool = True

        return destinations_bool

    def check_Vms(self):
        """
        It check that Vms is the part of the  network. if invalid then raise 
        exception.
        Return:
            True or false
        """
        vm_bool = {}
        vms_bool = False
        for b in self.Vms:
            vm_bool[b] = False
        for a in G.nodes():
            if a in self.Vms:
                vm_bool[a] = True

        for c in vm_bool:
            if vm_bool[c] == False:
                raise Exception('Node for Vm assignment is not in Network')
            if c in self.source:
                raise Exception('Source is assgined as VM')
            if c in self.destination:
                raise Exception('Destination is assnied as VM')
            else:
                vms_bool = True

        return vms_bool

    def Creatvarriables(self):  # for creating decision varriables q,x,y,p
        """
        This method creates Gurobi Decision Varriables
        r_: denote if node u is assigned as the enabled VM for VNF f in the 
            walk to destination d.
        lemda : denote if edge e u,v is located in the walk connecting the 
                enabled VM of VNF f and the enabled VM of the next VNF fN.
        lemda2 : denote if edge e v,u is located in the walk connecting the 
                enabled VM of VNF f and the enabled VM of the next VNF fN.
        T_:     if edge e u,v is located in the forest.
        o_:     represents if node u is assigned as the enabled VM of service f
                for the whole service forest.
        """
        self.F = self.source + self.VNFS

        self.r_template = "r_{:s}_{:s}_{:s}"
        self.lembda_template = "lambda_{:s}_{:s}_{:s}_{:s}"
        self.T_template = "T_{:s}_{:s}_{:s}"
        self.o_template = "o_{:s}_{:s}"
        self.lambda2_template = "lambda2_{:s}_{:s}_{:s}_{:s}"
        self.r1_template = "r_{:s}_{:s}_{:s}"

        for d in self.destination:
            for f in G.nodes():
                for u in G.nodes():
                    name = self.r_template.format(d, f, u)
                    self.Vars[name] = self.model.addVar(lb=0,
                                                        vtype=GRB.BINARY,
                                                        name=name)

                for u, v in G.edges():
                    name_lembda = self.lembda_template.format(d, f, u, v)
                    self.Vars[name_lembda] = self.model.addVar(
                        lb=0, vtype=GRB.BINARY, name=name_lembda)
                for u, v in G.edges():
                    name_T = self.T_template.format(f, u, v)
                    self.Vars[name_T] = self.model.addVar(lb=0,
                                                          vtype=GRB.BINARY,
                                                          name=name_T)
                for u in G.nodes():
                    name_o = self.o_template.format(f, u)
                    self.Vars[name_o] = self.model.addVar(lb=0,
                                                          vtype=GRB.BINARY,
                                                          name=name_o)
                for u, v in G.edges():
                    name_lambda2 = self.lambda2_template.format(d, f, v, u)
                    self.Vars[name_lambda2] = self.model.addVar(
                        lb=0, vtype=GRB.BINARY, name=name_lambda2)

        for d in self.destination:
            for fn in self.VNFS:
                for u in G.nodes():
                    name_r1 = self.r1_template.format(d, f, u)
                    self.Vars[name_r1] = self.model.addVar(lb=0,
                                                           vtype=GRB.BINARY,
                                                           name=name_r1)

        self.model.update()

    def Source_Selection(self):
        """
        Constraint 1 ensures that each destination chooses one source s in S
        as its service source.
        Returns:
            None
        """
        self.add_fs = 0
        for d in self.destination:
            for f in self.source:
                for s in self.source:
                    name_r = self.r_template.format(d, f, s)
                    add1 = self.model.getVarByName(name_r)

                    self.add_fs = self.add_fs + add1
        self.model.addConstr(self.add_fs, GRB.EQUAL, 1)

    def enabled_VM(self):
        """
        Constraint 2 finds a node u from M as the enaled VM of each VNF f for
        each destination.
        Returns:
            None
        """
        self.add = 0
        for d in self.destination:
            for f in self.source:
                for u in self.Vms:
                    name_r = self.r_template.format(d, f, u)
                    add1 = self.model.getVarByName(name_r)

                    self.add = self.add + add1
        self.model.addConstr(self.add, GRB.EQUAL, 1)

    def destination_assignment1(self):
        """
        There are two constraints which deals with assignment of destinations
        for Function Fd. Constraint 3 is the 1st one in that
        Constraint 3 assign only one destination for Function Fd
        Returns:
            None
        """
        self.assign = 0
        for d in self.destination:
            for f in self.destination:
                for u in self.destination:
                    name_r = self.r_template.format(d, f, u)
                    self.assign = self.model.getVarByName(name_r)

                self.model.addConstr(self.assign, GRB.EQUAL, 1)

    def destination_assignment2(self):
        """
        The 2nd constraint for destination assignment
        Contraint 4 assign only one destination for Function Fd
        Returns:
            None
        """
        self.add1 = 0
        for d in self.destination:
            for f in self.source:
                for u in self.Vms:
                    name_r = self.r_template.format(d, f, u)
                    add2 = self.model.getVarByName(name_r)

                    self.add1 = add2
                self.model.addConstr(self.add1, GRB.EQUAL, 0)

    def assignment_of_enabled_VM(self):
        """
        Contraint 5 assign u as the enabled VM of VNF f for the whole service
        forest if u has been selected by atleast one destination d for VNF f
        Returns:
            None
        """
        for d in self.destination:
            for f in self.VNFS:
                for u in G.nodes():
                    name_r = self.r_template.format(d, f, u)
                    name_o = self.o_template.format(f, u)
                    LHS = self.model.getVarByName(name_r)
                    RHS = self.model.getVarByName(name_o)
        self.model.addConstr(LHS, GRB.LESS_EQUAL, RHS)

    def atmost_one_VNF(self):
        """
        Contraint 6 ensures that each node u is in charge of at most one VNF.
        Returns:
            None
        """
        self.sum_o = 0
        for f in self.VNFS:
            for u in G.nodes():
                name_o = self.o_template.format(f, u)
                RHS = self.model.getVarByName(name_o)
                self.sum_o += RHS
        self.model.addConstr(self.sum_o, GRB.LESS_EQUAL, 1)

    def routing_of_service_chain(self):
        """
        Contraint 7 It first finds the routing of the service chain for each 
        destination d. It ensures that at least one edge eu;v incident from u
        is selected for the service chain because no edge e v;u incident 
        to u is chosen
        Returns:
            None
        """
        add_lemda1 = 0
        add_lemda2 = 0
        self.final_lemda = 0
        for d in self.destination:
            for f in self.F:
                for u, v in G.edges():
                    name_lembda = self.lembda_template.format(d, f, u, v)
                    LS = self.model.getVarByName(name_lembda)
                    add_lemda1 += LS
        for d in self.destination:
            for f in self.F:
                for u, v in G.edges():
                    name_lembda2 = self.lambda2_template.format(d, f, v, u)
                    RS = self.model.getVarByName(name_lembda2)
                    add_lemda2 += RS

        self.final_lemda = add_lemda1 - add_lemda2

        for d in self.destination:
            for f in self.F:
                for fn in self.VNFS:
                    for u in G.nodes():
                        name_r = self.r_template.format(d, f, u)
                        name_r1 = self.r1_template.format(d, fn, u)
                        LS1 = self.model.getVarByName(name_r)
                        RS1 = self.model.getVarByName(name_r1)
                        final_r = LS1 - RS1

                        self.model.addConstr(self.final_lemda,
                                             GRB.GREATER_EQUAL, final_r)

    def edge_inthe_service_forest(self):
        """
        Contraint 8 states that any edge e u;v is in the service forest if it
        is in the service chain for at least one destination d..
        Returns:
            None
        """
        for d in self.destination:
            for f in self.F:
                for u, v in G.edges():
                    name_lembda = self.lembda_template.format(d, f, u, v)
                    name_T = self.T_template.format(f, u, v)
                    LS = self.model.getVarByName(name_lembda)
                    RS = self.model.getVarByName(name_T)
                    self.model.addConstr(LS, GRB.LESS_EQUAL, RS)

    def optimzation(self):
        cost_nodes = 0
        cost_edges = 0
        for f in self.VNFS:
            for u in G.nodes:
                name_o = self.o_template.format(f, u)
                LS = G.nodes[u]['Cost'] * self.model.getVarByName(name_o)
                cost_nodes += LS
        for f in self.VNFS:
            for u, v in G.edges():
                name_T = self.T_template.format(f, u, v)
                RS = G.edges[u, v]['Cost'] * self.model.getVarByName(name_T)
                cost_edges += RS
        final_cost = cost_nodes + cost_edges
        self.model.setObjective(final_cost, GRB.MINIMIZE)
        self.model.optimize()
        status = self.model.status

        return status

    def build(self):
        self.check_source()
        self.check_destination()
        self.check_Vms()
        self.Creatvarriables()
        self.Source_Selection()
        self.enabled_VM()
        self.destination_assignment1()
        self.destination_assignment2()
        self.assignment_of_enabled_VM()
        self.atmost_one_VNF()
        self.routing_of_service_chain()
        self.edge_inthe_service_forest()
def _objective_function_for_delta_weight(D, delta_weight, d1, d2):
    global _time_limit_per_model, _round, _pr_dataset, _tardiness_objective_dataset
    m = Model("model_for_supplier_assignment")
    m.setParam('OutputFlag', False)
    m.params.timelimit = _time_limit_per_model
    # m.params.IntFeasTol = 1e-7
    x = {}
    q = {}
    for (r, s, p) in D.supplier_project_shipping:
        x[r, s, p] = m.addVar(vtype=GRB.BINARY, name="x_%s_%s_%s" % (r, s, p))
        q[r, s, p] = m.addVar(vtype=GRB.CONTINUOUS, name="q_%s_%s_%s" % (r, s, p))

    AT = {}
    for j in range(D.project_n):
        for k in [r for r, p in D.resource_project_demand if p == D.project_list[j]]:
            AT[j, k] = m.addVar(vtype=GRB.CONTINUOUS, name="AT_%s_%s" % (j, k))
    m.update()

    ## define constraints
    # equation 2
    for (r, s) in D.resource_supplier_capacity:
        m.addConstr(quicksum(q[r, s, D.project_list[j]] for j in range(D.project_n)), GRB.LESS_EQUAL,
                    D.resource_supplier_capacity[r, s],
                    name="constraint_3_resource_%s_supplier_%s" % (r, s))

    # constraint 21(4) 23(6)
    for (r, p) in D.resource_project_demand:
        # equation 5
        m.addConstr(quicksum(x[r, i, p] for i in D.resource_supplier_list[r]), GRB.EQUAL, 1,
                    name="constraint_6_resource_%s_project_%s" % (r, p))
        # equation 3
        m.addConstr(quicksum(q[r, i, p] for i in D.resource_supplier_list[r]), GRB.GREATER_EQUAL,
                    D.resource_project_demand[r, p], name="constraint_4_resource_%s_project_%s" % (r, p))

    # constraint 22(5)
    for (i, j, k) in q:
        # i resource, j supplier, k project
        # equation 4
        m.addConstr(q[i, j, k], GRB.LESS_EQUAL, D.M * x[i, j, k],
                    name="constraint_5_resource_%s_supplier_%s_project_%s" % (i, j, k))
    # constraint 7
    shipping_cost_expr = LinExpr()
    for (i, j, k) in q:
        shipping_cost_expr.addTerms(D.c[i, j, k], q[i, j, k])
    # equation 6
    m.addConstr(shipping_cost_expr, GRB.LESS_EQUAL, D.B, name="constraint_7")

    # constraint 8
    # equation 26
    for j in range(D.project_n):
        p = D.project_list[j]
        project_resources = [r for (r, p_) in D.resource_project_demand.keys() if p_ == p]
        for r in project_resources:
            suppliers = D.resource_supplier_list[r]
            m.addConstr(
                quicksum(
                    x[r, s, p] * (D.resource_supplier_release_time[r, s] + D.supplier_project_shipping[r, s, p]) for
                    s in
                    suppliers), GRB.LESS_EQUAL, AT[j, r],
                name="constraint_8_project_%d_resource_%s_deliver" % (j, r))
    m.update()

    expr = LinExpr()
    for j in range(D.project_n):
        p = D.project_list[j]
        for r in [r for (r, p_) in D.resource_project_demand.keys() if p_ == p]:
            expr.add(delta_weight[j, r] * AT[j, r])
    m.setObjective(expr, GRB.MINIMIZE)
    m.update()
    ##########################################
    # m.params.presolve = 1
    m.update()
    # Solve
    # m.params.presolve=0
    m.optimize()
    _exit_if_infeasible(m)
    m.write(join(_result_output_path, "round_%d_supplier_assign.lp" % _round))
    m.write(join(_result_output_path, "round_%d_supplier_assign.sol" % _round))
    with open(join(log_output_path, 'shipping_cost.txt'), 'a') as fout:
        fout.write('shipping cost: %f\n' % shipping_cost_expr.getValue())
    _logger.info('shipping cost: %f' % shipping_cost_expr.getValue())

    print('status', m.status)
    # m.write(join(_output_path, 'delta_weight.sol'))
    # m.write(join(_output_path, 'delta_weight.lp'))
    X_ = {}
    for (i, j, k) in D.supplier_project_shipping:
        v = m.getVarByName("x_%s_%s_%s" % (i, j, k))
        if v.X == 1:
            X_[i, j, k] = 1

    AT_ = {}
    for j, r in AT:
        val = AT[j, r].X
        if val > 0:
            AT_[j, r] = val

    tardiness_obj_val, skj, sj = _objective_function_for_tardiness(X_, AT_, D)
    new_delta_weight = {}
    # delta_weight_keys = list(delta_weight.keys())
    # delta_weight_keys.sort(key=lambda x: x[1])
    # delta_weight_keys.sort(key=lambda x: x[0])
    for j, r in delta_weight.keys():
        new_delta_weight[j, r] = delta_weight[j, r] * (1 + d1 * (d2 + sj.get(j, 0)) * skj.get((j, r), 0))
        # print('j', type(j), j)
        # print('r', type(r), r)
        # print('previous weight', type(delta_weight[j, r]), delta_weight[j, r])
        # print('d1', type(d1), d1)
        # print('d2', type(d2), d2)
        # print('sj', type(sj.get(j, 0)), sj.get(j, 0))
        # print('skj', type(skj.get((j, r))), skj.get((j, r)))
        # print('new weight', type(new_delta_weight[j, r]), new_delta_weight[j, r])
        _logger.info(
            'r[%d,%s] = %f *(1+%f*(%f+%f)*%f) = %f' % (
                j, r, delta_weight[j, r], d1, d2, sj.get(j, 0), skj.get((j, r), 0), new_delta_weight[j, r]))

        # new_delta_weight[j, r] = 1
    _normalize(new_delta_weight)

    for j, r in new_delta_weight.keys():
        # _logger.info('j:' + str(j))
        # _logger.info('r:' + str(r))
        # _logger.info(str([_round, j, r, new_delta_weight[j, r]]))
        _weight_dataset.loc[_weight_dataset.shape[0]] = [_round, j, r, new_delta_weight[j, r]]

    for j in range(D.project_n):
        _pr_dataset.loc[_pr_dataset.shape[0]] = [_round, j, sj.get(j, 0)]

    _tardiness_objective_dataset.loc[_tardiness_objective_dataset.shape[0]] = [_round, tardiness_obj_val]

    return new_delta_weight
Exemple #12
0
def _objective_function_for_delta_weight(D, delta_weight):
    m = Model("model_for_supplier_assignment")
    x = {}
    q = {}
    for (r, s, p) in D.supplier_project_shipping:
        # i resource, j supplier, k project
        x[r, s, p] = m.addVar(vtype=GRB.BINARY, name="x_%s_%s_%s" % (r, s, p))
        q[r, s, p] = m.addVar(vtype=GRB.CONTINUOUS, name="q_%s_%s_%s" % (r, s, p))

    AT = {}

    for j in range(D.project_n):
        for k in sorted([r for r, p in D.resource_project_demand if p == D.project_list[j]]):
            AT[j, k] = m.addVar(vtype=GRB.CONTINUOUS, name="AT_%s_%s" % (j, k))

    m.update()

    ## define constraints
    # constraint 20(3)
    for (r, s) in D.resource_supplier_capacity:
        m.addConstr(quicksum(q[r, s, D.project_list[j]] for j in range(D.project_n)), GRB.LESS_EQUAL,
                    D.resource_supplier_capacity[r, s],
                    name="constraint_3_resource_%s_supplier_%s" % (r, s))

    # constraint 21(4) 23(6)
    for (r, p) in D.resource_project_demand:
        m.addConstr(quicksum(x[r, i, p] for i in D.resource_supplier_list[r]), GRB.EQUAL, 1,
                    name="constraint_6_resource_%s_project_%s" % (r, p))
        m.addConstr(quicksum(q[r, i, p] for i in D.resource_supplier_list[r]), GRB.GREATER_EQUAL,
                    D.resource_project_demand[r, p], name="constraint_4_resource_%s_project_%s" % (r, p))

    # constraint 22(5)
    for (i, j, k) in q:
        # i resource, j supplier, k project
        m.addConstr(q[i, j, k], GRB.LESS_EQUAL, D.M * x[i, j, k],
                    name="constraint_5_resource_%s_supplier_%s_project_%s" % (i, j, k))

    # constraint 7
    expr = LinExpr()
    for (i, j, k) in q:
        expr = expr + D.c[i, j, k] * q[i, j, k]
    m.addConstr(expr, GRB.LESS_EQUAL, D.B, name="constraint_7")

    # constraint 8
    for j in range(D.project_n):
        p = D.project_list[j]
        project_resources = sorted([r for (r, p_) in D.resource_project_demand.keys() if p_ == p])
        for r in project_resources:
            suppliers = D.resource_supplier_list[r]

            # print(list(D.supplier_project_shipping.keys())[:10])
            # print(D.supplier_project_shipping['NK0g77', 'S1671', 'P1'])
            # print(list(x.keys())[:10])
            # print(x['NK0g77', 'S1671', 'P1'])
            m.addConstr(
                quicksum(
                    x[r, s, p] * (D.resource_supplier_release_time[r, s] + D.supplier_project_shipping[r, s, p]) for
                    s in
                    suppliers), GRB.LESS_EQUAL, AT[j, r],
                name="constraint_8_project_%d_resource_%s_deliver" % (j, r))

    m.update()

    expr = LinExpr()
    for j in range(D.project_n):
        for r in sorted([r for (r, p_) in D.resource_project_demand.keys() if p_ == p]):
            expr.add(delta_weight[_delta_project_idx[j, r]] * AT[j, r])
    m.setObjective(expr, GRB.MINIMIZE)
    m.update()
    ##########################################
    m.params.presolve = 1
    m.update()
    # Solve
    # m.params.presolve=0
    m.optimize()
    print(m.Status)
    X_ = {}
    for (i, j, k) in D.supplier_project_shipping:
        v = m.getVarByName("x_%s_%s_%s" % (i, j, k))
        print(v)
        if v.X == 1:
            X_[i, j, k] = 1

    AT_ = {}
    for j, r in AT:
        val = AT[j, r].X
        if val > 0:
            AT_[j, r] = val

    return -_objective_function_for_tardiness(X_, AT_, D),
Exemple #13
0
def gurobi_solve_qp(P, q, G=None, h=None, A=None, b=None, initvals=None):
    """
    Solve a Quadratic Program defined as:

        minimize
            (1/2) * x.T * P * x + q.T * x

        subject to
            G * x <= h
            A * x == b

    using Gurobi <http://www.gurobi.com/>.

    Parameters
    ----------
    P : array, shape=(n, n)
        Primal quadratic cost matrix.
    q : array, shape=(n,)
        Primal quadratic cost vector.
    G : array, shape=(m, n)
        Linear inequality constraint matrix.
    h : array, shape=(m,)
        Linear inequality constraint vector.
    A : array, shape=(meq, n), optional
        Linear equality constraint matrix.
    b : array, shape=(meq,), optional
        Linear equality constraint vector.
    initvals : array, shape=(n,), optional
        Warm-start guess vector (not used).

    Returns
    -------
    x : array, shape=(n,)
        Solution to the QP, if found, otherwise ``None``.
    """
    if initvals is not None:
        print("Gurobi: note that warm-start values are ignored by wrapper")
    n = P.shape[1]
    model = Model()
    x = {
        i: model.addVar(
            vtype=GRB.CONTINUOUS,
            name='x_%d' % i,
            lb=-GRB.INFINITY,
            ub=+GRB.INFINITY)
        for i in xrange(n)
    }
    model.update()   # integrate new variables

    # minimize
    #     1/2 x.T * P * x + q * x
    obj = QuadExpr()
    rows, cols = P.nonzero()
    for i, j in zip(rows, cols):
        obj += 0.5 * x[i] * P[i, j] * x[j]
    for i in xrange(n):
        obj += q[i] * x[i]
    model.setObjective(obj, GRB.MINIMIZE)

    # subject to
    #     G * x <= h
    if G is not None:
        G_nonzero_rows = get_nonzero_rows(G)
        for i, row in G_nonzero_rows.iteritems():
            model.addConstr(quicksum(G[i, j] * x[j] for j in row) <= h[i])

    # subject to
    #     A * x == b
    if A is not None:
        A_nonzero_rows = get_nonzero_rows(A)
        for i, row in A_nonzero_rows.iteritems():
            model.addConstr(quicksum(A[i, j] * x[j] for j in row) == b[i])

    model.optimize()

    a = empty(n)
    for i in xrange(n):
        a[i] = model.getVarByName('x_%d' % i).x
    return a
Exemple #14
0
class GurobiWrapper(SolverWrapper):

    def __init__(self):
        self.model = Model()

    def solve(self):
        self.model.optimize()

    def add_variable(self, name, lb, ub, vtype):
        args = {'name': name}
        if ub is not None:
            args['ub'] = ub
        if lb is not None:
            args['lb'] = lb
        if vtype is not None:
            args['vtype'] = self._var_types_mapping(vtype)

        self.model.addVar(**args)

    def add_variables(self, name, lb, ub, vtype):
        args = {'name': name}
        if ub is not None:
            args['ub'] = ub
        if lb is not None:
            args['lb'] = lb
        if vtype is not None:
            args['vtype'] = [self._var_types_mapping(t) for t in vtype]

        self.model.addVars(len(name), **args)

    def add_constraint(self, var, coeff, sense, rs):
        args = {
            'lhs': LinExpr(coeff, self._gurobi_variables(var)),
            'sense': self._sense_mapping(sense),
            'rhs': rs
        }
        self.model.addConstr(**args)

    def add_constraints(self, var, coeff, sense, rs):
        for constr in zip(var, coeff, sense, rs):
            self.add_constraint(*constr)

    def set_objective_sense(self, sense):
        mapping = {
            Objective.maximize: GRB.MAXIMIZE,
            Objective.minimize: GRB.MINIMIZE
        }
        self.model.ModelSense = mapping[sense]

    def set_objective(self, var, coeff):
        lin_expr = LinExpr(coeff, self._gurobi_variables(var))
        self.model.setObjective(lin_expr)

    def write_to_file(self, name):
        self.model.write(name)

    def get_solution_status(self):
        """
        :type solution_type: SolutionType
        :type additional_data: Dict[{'status', 'status_str'}]
        :return solution_type, additional_data:
        """
        status = self.model.Status
        solution_type = SolutionType.feasible if status == 1 else SolutionType.infeasible

        additional_data = {
            'status': status,
            'status_str': self._solution_strings()[status]
        }

        return solution_type, additional_data

    def get_objective_value(self):
        return self.model.ObjVal

    def get_solution(self):
        return {var.VarName: var.X for var in self.model.getVars()}

    def _gurobi_variable(self, name):
        return self.model.getVarByName(name)

    def _gurobi_variables(self, names):
        return [self._gurobi_variable(n) for n in names]

    @staticmethod
    def _sense_mapping(sense):
        mapping = {
            Sense.lt: GRB.LESS_EQUAL,
            Sense.gt: GRB.GREATER_EQUAL,
            Sense.eq: GRB.EQUAL
        }
        return mapping[sense]

    @staticmethod
    def _var_types_mapping(vtype):
        mapping = {
            VariableTypes.int: GRB.INTEGER,
            VariableTypes.continuous: GRB.CONTINUOUS
        }
        return mapping[vtype]

    @staticmethod
    def _solution_strings():
        return {
            1: 'LOADED',
            2: 'OPTIMAL',
            3: 'INFEASIBLE',
            4: 'INF_OR_UNBD',
            5: 'UNBOUNDED',
            6: 'CUTOFF',
            7: 'ITERATION_LIMIT',
            8: 'NODE_LIMIT',
            9: 'TIME_LIMIT',
            10: 'SOLUTION_LIMIT',
            11: 'INTERRUPTED',
            12: 'NUMERIC',
            13: 'SUBOPTIMAL',
            14: 'INPROGRESS',
            15: 'USER_OBJ_LIMIT'
        }
def schedule_rooms_in_period_raumsperren(exams_to_schedule, period, data, verbose = False):
    #print period
    '''
        schedule_rooms needs to be called for every single period
        schedule_rooms tries to schedule a given set of exams which are written in the same period on the rooms avialable for the given period
    '''
    
    # TODO: Initialise using meaningful values
    # ...

    #verbose = True
    
    n = len(exams_to_schedule)
    r = data['r']
    c = data['c']
    T = data['T']
    s = data['s']
    z = {}

    exam_rooms_index = data['exam_rooms_index']
    
    model = Model("RoomPlanner")

    # z[i,k] = if exam i is written in room k
    for i in exams_to_schedule:
        for k in exam_rooms_index[i]:
            if period == -1 or T[k][period] == 1:
                z[i,k] = model.addVar(vtype=GRB.BINARY, name="z_%s_%s" % (i,k))

    model.update()

    # Building constraints...    
    
    # c1: seats for all students
    for i in exams_to_schedule:
        model.addConstr( quicksum([ z[i, k] * c[k] for k in range(r) if period == -1 or T[k][period] == 1 ]) >= s[i], "c1")
    
    # c2: only one exam per room
    for k in range(r):
            if period == -1 or T[k][period] == 1:
                model.addConstr( quicksum([ z[i, k] for i in exams_to_schedule  ]) <= 1, "c2")    

    # objective: minimize number of used rooms
    obj1 = quicksum([ z[i,k] for i in exams_to_schedule for k in exam_rooms_index[i] if T[k][period] == 1 ]) 

    model.setObjective( obj1, GRB.MINIMIZE)
    
    if not verbose:
        model.params.OutputFlag = 0
    
    model.optimize()

    
    # return best room schedule
    try:       
        z=defaultdict(int)
        for i in exams_to_schedule:
            for k in exam_rooms_index[i]:
                if period == -1 or T[k][period] == 1:
                    v = model.getVarByName("z_%s_%s" % (i,k)) 
                    z[i,k]  = v.x
                    
        return z
    except GurobiError:
        return None
Exemple #16
0
class GurobiSolver(Solver):
    """ Implements the solver interface using gurobipy. """

    def __init__(self, model=None):
        Solver.__init__(self)
        self.problem = GurobiModel()
        self.set_logging()
        self.set_parameters(default_parameters)
        if model:
            self.build_problem(model)

    def add_variable(self, var_id, lb=None, ub=None, vartype=VarType.CONTINUOUS, persistent=True, update_problem=True):
        """ Add a variable to the current problem.

        Arguments:
            var_id (str): variable identifier
            lb (float): lower bound
            ub (float): upper bound
            vartype (VarType): variable type (default: CONTINUOUS)
            persistent (bool): if the variable should be reused for multiple calls (default: true)
            update_problem (bool): update problem immediately (default: True)
        """

        lb = lb if lb is not None else -GRB.INFINITY
        ub = ub if ub is not None else GRB.INFINITY

        if var_id in self.var_ids:
            var = self.problem.getVarByName(var_id)
            var.setAttr('lb', lb)
            var.setAttr('ub', ub)
            var.setAttr('vtype', vartype_mapping[vartype])
        else:
            self.problem.addVar(name=var_id, lb=lb, ub=ub, vtype=vartype_mapping[vartype])
            self.var_ids.append(var_id)
            
        if not persistent:
            self.temp_vars.add(var_id)
        
        if update_problem:
            self.problem.update()

    def add_constraint(self, constr_id, lhs, sense='=', rhs=0, persistent=True, update_problem=True):
        """ Add a constraint to the current problem.

        Arguments:
            constr_id (str): constraint identifier
            lhs (dict): variables and respective coefficients
            sense (str): constraint sense (any of: '<', '=', '>'; default '=')
            rhs (float): right-hand side of equation (default: 0)
            persistent (bool): if the variable should be reused for multiple calls (default: True)
            update_problem (bool): update problem immediately (default: True)
        """

        grb_sense = {'=': GRB.EQUAL,
                     '<': GRB.LESS_EQUAL,
                     '>': GRB.GREATER_EQUAL}

        if constr_id in self.constr_ids:
            constr = self.problem.getConstrByName(constr_id)
            self.problem.remove(constr)

        expr = quicksum(coeff * self.problem.getVarByName(r_id) for r_id, coeff in lhs.items() if coeff)

        self.problem.addConstr(expr, grb_sense[sense], rhs, constr_id)
        self.constr_ids.append(constr_id)
            
        if not persistent:
            self.temp_constrs.add(constr_id)

        if update_problem:
            self.problem.update()
                                
    def remove_variable(self, var_id):
        """ Remove a variable from the current problem.
        
        Arguments:
            var_id (str): variable identifier
        """
        if var_id in self.var_ids:
            self.problem.remove(self.problem.getVarByName(var_id))
            self.var_ids.remove(var_id)
    
    def remove_constraint(self, constr_id):
        """ Remove a constraint from the current problem.
        
        Arguments:
            constr_id (str): constraint identifier
        """
        if constr_id in self.constr_ids:
            self.problem.remove(self.problem.getConstrByName(constr_id))
            self.constr_ids.remove(constr_id)
    
    def update(self):
        """ Update internal structure. Used for efficient lazy updating. """
        self.problem.update()

    def set_objective(self, linear=None, quadratic=None, minimize=True):
        """ Set a predefined objective for this problem.

        Args:
            linear (dict): linear coefficients (optional)
            quadratic (dict): quadratic coefficients (optional)
            minimize (bool): solve a minimization problem (default: True)

        Notes:
            Setting the objective is optional. It can also be passed directly when calling **solve**.

        """

        lin_obj = []
        quad_obj = []

        if linear:
            lin_obj = [f * self.problem.getVarByName(r_id) for r_id, f in linear.items() if f]

        if quadratic:
            quad_obj = [q * self.problem.getVarByName(r_id1) * self.problem.getVarByName(r_id2)
                        for (r_id1, r_id2), q in quadratic.items() if q]

        obj_expr = quicksum(quad_obj + lin_obj)
        sense = GRB.MINIMIZE if minimize else GRB.MAXIMIZE

        self.problem.setObjective(obj_expr, sense)

    def solve(self, linear=None, quadratic=None, minimize=None, model=None, constraints=None, get_values=True,
              get_shadow_prices=False, get_reduced_costs=False):
        """ Solve the optimization problem.

        Arguments:
            linear (dict): linear objective (optional)
            quadratic (dict): quadratic objective (optional)
            minimize (bool): solve a minimization problem (default: True)
            model (CBModel): model (optional, leave blank to reuse previous model structure)
            constraints (dict): additional constraints (optional)
            get_values (bool): set to false for speedup if you only care about the objective value (default: True)
            get_shadow_prices (bool): return shadow prices if available (default: False)
            get_reduced_costs (bool): return reduced costs if available (default: False)

        Returns:
            Solution: solution
        """

        if model:
            self.build_problem(model)

        problem = self.problem

        if constraints:
            old_constraints = {}
            for r_id, x in constraints.items():
                lb, ub = x if isinstance(x, tuple) else (x, x)
                if r_id in self.var_ids:
                    lpvar = problem.getVarByName(r_id)
                    old_constraints[r_id] = (lpvar.lb, lpvar.ub)
                    lpvar.lb = lb if lb is not None else -GRB.INFINITY
                    lpvar.ub = ub if ub is not None else GRB.INFINITY
                else:
                    warnings.warn("Constrained variable '{}' not previously declared".format(r_id), RuntimeWarning)
            problem.update()

        self.set_objective(linear, quadratic, minimize)

        #run the optimization
        problem.optimize()

        status = status_mapping[problem.status] if problem.status in status_mapping else Status.UNKNOWN
        message = str(problem.status)

        if status == Status.OPTIMAL:
            fobj = problem.ObjVal
            values, shadow_prices, reduced_costs = None, None, None

            if get_values:
                values = OrderedDict([(r_id, problem.getVarByName(r_id).X) for r_id in self.var_ids])

            if get_shadow_prices:
                shadow_prices = OrderedDict([(m_id, problem.getConstrByName(m_id).Pi) for m_id in self.constr_ids])

            if get_reduced_costs:
                reduced_costs = OrderedDict([(r_id, problem.getVarByName(r_id).RC) for r_id in self.var_ids])

            solution = Solution(status, message, fobj, values, shadow_prices, reduced_costs)
        else:
            solution = Solution(status, message)

        #reset old constraints because temporary constraints should not be persistent
        if constraints:
            for r_id, (lb, ub) in old_constraints.items():
                lpvar = problem.getVarByName(r_id)
                lpvar.lb, lpvar.ub = lb, ub
            problem.update()

        return solution

    #TODO: 2_program_MMsolver.prof
    def set_lower_bounds(self, bounds_dict):
        for var_id, lb in bounds_dict.iteritems():
            lpvar = self.problem.getVarByName(var_id)
            lpvar.lb = lb if lb is not None else GRB.INFINITY

    #TODO: 2_program_MMsolver.prof
    def set_upper_bounds(self, bounds_dict):
        for var_id, ub in bounds_dict.iteritems():
            lpvar = self.problem.getVarByName(var_id)
            lpvar.ub = ub if ub is not None else GRB.INFINITY

    #TODO: 2_program_MMsolver.prof
    def set_bounds(self, bounds_dict):
        for var_id, bounds in bounds_dict.iteritems():
            lpvar = self.problem.getVarByName(var_id)
            lpvar.lb = bounds[0] if bounds[0] is not None else GRB.INFINITY
            lpvar.ub = bounds[1] if bounds[1] is not None else GRB.INFINITY

    def set_parameter(self, parameter, value):
        """ Set a parameter value for this optimization problem

        Arguments:
            parameter (Parameter): parameter type
            value (float): parameter value
        """

        if parameter in parameter_mapping:
            grb_param = parameter_mapping[parameter]
            self.problem.setParam(grb_param, value)
        else:
            raise Exception('Parameter unknown (or not yet supported).')

    def set_logging(self, enabled=False):
        """ Enable or disable log output:

        Arguments:
            enabled (bool): turn logging on (default: False)
        """

        self.problem.setParam('OutputFlag', 1 if enabled else 0)

    def write_to_file(self, filename):
        """ Write problem to file:

        Arguments:
            filename (str): file path
        """

        self.problem.write(filename)
Exemple #17
0
def update_R_c(rep, rep_norm, solver='cvxopt', tol=1e-6):
    """
    Function to update R and c while leaving the network parameters fixed in a block coordinate optimization.
    Using quadratic programming of cvxopt.
    """

    assert solver in ('cvxopt', 'gurobi')

    n, d = rep.shape

    # Define QP
    P = (2 * np.dot(rep, rep.T)).astype(np.double)
    q = (-(rep_norm**2)).astype(np.double)
    G = (np.concatenate((np.eye(n), -np.eye(n)), axis=0)).astype(np.double)
    h = (np.concatenate(
        ((1 / (Cfg.nu.get_value() * n)) * np.ones(n), np.zeros(n)),
        axis=0)).astype(np.double)
    A = (np.ones(n)).astype(np.double)
    b = 1

    if solver == 'cvxopt':

        from cvxopt import matrix
        from cvxopt.solvers import qp

        # Solve QP
        sol = qp(matrix(P), matrix(q), matrix(G), matrix(h),
                 matrix(A).trans(), matrix(b, tc='d'))['x']
        a = np.array(sol).reshape(n)
        print("Sum of the elements of alpha: {:.3f}".format(np.sum(a)))

    if solver == 'gurobi':

        # Gurobi Python wrapper from https://github.com/stephane-caron/qpsolvers/blob/master/qpsolvers/gurobi_.py

        from gurobipy import Model, QuadExpr, GRB, quicksum

        # setup model
        model = Model()
        x = {
            i: model.addVar(vtype=GRB.CONTINUOUS,
                            name='x_%d' % i,
                            lb=-GRB.INFINITY,
                            ub=+GRB.INFINITY)
            for i in xrange(n)
        }
        model.update()

        # minimize 1/2 x.T * P * x + q * x
        obj = QuadExpr()
        rows, cols = P.nonzero()
        for i, j in zip(rows, cols):
            obj += 0.5 * x[i] * P[i, j] * x[j]
        for i in xrange(n):
            obj += q[i] * x[i]
        model.setObjective(obj, GRB.MINIMIZE)

        # subject to G * x <= h
        G_nonzero_rows = get_nonzero_rows(G)
        for i, row in G_nonzero_rows.iteritems():
            model.addConstr(quicksum(G[i, j] * x[j] for j in row) <= h[i])

        # subject to A * x == b
        A_nonzero_rows = get_nonzero_rows(A)
        for i, row in A_nonzero_rows.iteritems():
            model.addConstr(quicksum(A[i, j] * x[j] for j in row) == b[i])

        # Solve QP
        model.optimize()

        a = np.empty(n)
        for i in xrange(n):
            a[i] = model.getVarByName('x_%d' % i).x

    # Set new center c and radius R
    c = np.dot(a, rep).reshape(d).astype(np.float32)

    # Recover R (using the specified numeric tolerance on the range)
    n_svs = 0  # number of support vectors
    while n_svs == 0:
        lower = tol * (1 / (Cfg.nu.get_value() * n))
        upper = (1 - tol) * (1 / (Cfg.nu.get_value() * n))
        idx_svs = (a > lower) & (a < upper)
        n_svs = np.sum(idx_svs)
        tol /= 10  # decrease tolerance if there are still no support vectors found

    print("Number of Support Vectors: {}".format(n_svs))
    R = np.mean(np.sum((rep[idx_svs] - c)**2, axis=1)).astype(np.float32)

    return R, c
Exemple #18
0
def gurobi_solve_qp(P,
                    q,
                    G=None,
                    h=None,
                    A=None,
                    b=None,
                    initvals=None,
                    verbose=False):
    """
    Solve a Quadratic Program defined as:

    .. math::

        \\begin{split}\\begin{array}{ll}
        \\mbox{minimize} &
            \\frac{1}{2} x^T P x + q^T x \\\\
        \\mbox{subject to}
            & G x \\leq h                \\\\
            & A x = h
        \\end{array}\\end{split}

    using `Gurobi <http://www.gurobi.com/>`_.

    Parameters
    ----------
    P : array, shape=(n, n)
        Primal quadratic cost matrix.
    q : array, shape=(n,)
        Primal quadratic cost vector.
    G : array, shape=(m, n)
        Linear inequality constraint matrix.
    h : array, shape=(m,)
        Linear inequality constraint vector.
    A : array, shape=(meq, n), optional
        Linear equality constraint matrix.
    b : array, shape=(meq,), optional
        Linear equality constraint vector.
    initvals : array, shape=(n,), optional
        Warm-start guess vector (not used).
    verbose : bool, optional
        Set to `True` to print out extra information.

    Returns
    -------
    x : array, shape=(n,)
        Solution to the QP, if found, otherwise ``None``.
    """
    setParam('OutputFlag', 1 if verbose else 0)
    if initvals is not None:
        print("Gurobi: note that warm-start values are ignored by wrapper")
    n = P.shape[1]
    model = Model()
    x = {
        i: model.addVar(vtype=GRB.CONTINUOUS,
                        name='x_%d' % i,
                        lb=-GRB.INFINITY,
                        ub=+GRB.INFINITY)
        for i in range(n)
    }
    model.update()  # integrate new variables

    # minimize
    #     1/2 x.T * P * x + q * x
    obj = QuadExpr()
    rows, cols = P.nonzero()
    for i, j in zip(rows, cols):
        obj += 0.5 * x[i] * P[i, j] * x[j]
    for i in range(n):
        obj += q[i] * x[i]
    model.setObjective(obj, GRB.MINIMIZE)

    # subject to
    #     G * x <= h
    if G is not None:
        G_nonzero_rows = get_nonzero_rows(G)
        for i, row in G_nonzero_rows.items():
            model.addConstr(quicksum(G[i, j] * x[j] for j in row) <= h[i])

    # subject to
    #     A * x == b
    if A is not None:
        A_nonzero_rows = get_nonzero_rows(A)
        for i, row in A_nonzero_rows.items():
            model.addConstr(quicksum(A[i, j] * x[j] for j in row) == b[i])

    model.optimize()

    a = empty(n)
    for i in range(n):
        a[i] = model.getVarByName('x_%d' % i).x
    return a
def find_ranking(comparisons, equal_width=0.2, max_rank=-1, verbose=False):
    """
    Find the least changes to a set of comparisons so that they are consistent
    (transitive), it returns a topological ranking.

    comparisons     A dictionary with tuple keys in the form of (i, j), values
                    are scalars indicating the probability of i > j. It is
                    assumed that comparisons are symmetric. Use 0 for i < j,
                    0.5 for i == j, and 1 for i > j (and any value in between).
    equal_width     0..0.5-equal_width/2 is considered '<=' and 0.5..0.5+equal_width/2
                    is considered '>='. In between it is considered to be '=='.
    max_rank        Maximal rank, a low value forces the model to choose more
                    equal cases.
    verbose         Whether to print gurobi's progress.

    Returns:
        A tuple of size two:
        0) Ranking derived from topological sort (list of ranks in order of
           nodes);
        1) Sum of absolute changes to the comparisons.
    """
    # remove unnecessary variables
    comparisons = {(i, j) if i < j else (j, i): value if i < j else 1 - value
                   for (i, j), value in comparisons.items()}
    nodes = np.unique([i for ij in comparisons.keys() for i in ij])

    # define variables
    model = Model('comparison')
    model.setParam('OutputFlag', verbose)
    values = np.fromiter(comparisons.values(), dtype=float)
    assert values.max() <= 1 and values.min() >= 0
    # variables to encode the error of comparisons
    E_ij = model.addVars(comparisons.keys(),
                         name='e_ij',
                         vtype=GRB.CONTINUOUS,
                         ub=1.0 - values,
                         lb=-values)
    # variables to encode hard choice of >=, <=, ==
    Ge_ij = model.addVars(comparisons.keys(), name='ge_ij', vtype=GRB.BINARY)
    Le_ij = model.addVars(comparisons.keys(), name='le_ij', vtype=GRB.BINARY)
    Eq_ij = model.addVars(comparisons.keys(), name='eq_ij', vtype=GRB.BINARY)
    # variables to help with transitivity in non-fully connected graphs
    if max_rank < 1:
        max_rank = len(nodes)
    R_i = model.addVars(nodes,
                        name='r_i',
                        vtype=GRB.CONTINUOUS,
                        lb=0,
                        ub=max_rank)
    # variables to emulate abs
    T_ij_pos = {}
    T_ij_neg = {}
    index = (values != 1) & (values != 0)
    T_ij_pos = model.addVars(
        (ij for ij, value in comparisons.items() if value not in [0.0, 1.0]),
        vtype=GRB.CONTINUOUS,
        name='T_ij_pos',
        lb=0,
        ub=1 - values[index])
    T_ij_neg = model.addVars(
        (ij for ij, value in comparisons.items() if value not in [0.0, 1.0]),
        vtype=GRB.CONTINUOUS,
        name='T_ij_neg',
        lb=0,
        ub=values[index])
    model.update()

    # emulate abs for non-binary comparisons: E_ij = T_ij_pos - T_ij_neg
    model.addConstrs((E_ij[ij] == T_ij_pos[ij] - T_ij_neg[ij]
                      for ij in T_ij_pos), 'E_ij = T_ij_pos - T_ij_neg')

    # hard decision of >=, <=, and ==
    lower_bound = 0.5 - equal_width / 2.0
    upper_bound = 0.5 + equal_width / 2.0
    # <=
    model.addConstrs((E_ij[ij] + comparisons[ij] - upper_bound <= ge_ij
                      for ij, ge_ij in Ge_ij.items()), 'ge_ij_lower_bound')
    model.addConstrs((E_ij[ij] + comparisons[ij] - upper_bound >= -1 + ge_ij
                      for ij, ge_ij in Ge_ij.items()), 'ge_ij_upper_bound')
    # >=
    model.addConstrs((E_ij[ij] + comparisons[ij] - lower_bound >= -le_ij
                      for ij, le_ij in Le_ij.items()), 'le_ij_lower_bound')
    model.addConstrs((E_ij[ij] + comparisons[ij] - lower_bound <= 1 - le_ij
                      for ij, le_ij in Le_ij.items()), 'le_ij_upper_bound')
    # ==
    model.addConstrs((
        le + eq + ge == 1
        for le, eq, ge in zip(Le_ij.values(), Eq_ij.values(), Ge_ij.values())),
                     'eq_ij')

    # transitivity
    for (i, j), eq_a in Eq_ij.items():
        le_a = Le_ij[i, j]
        ge_a = Ge_ij[i, j]
        for k in nodes:
            j_, k_ = j, k
            if j > k:
                j_, k_ = k, j
            eq_b = Eq_ij.get((j_, k_), None)
            if eq_b is None:
                continue
            else:
                le_b = Le_ij[j_, k_]
                ge_b = Ge_ij[j_, k_]
            if j_ != j:
                le_b, ge_b = ge_b, le_b

            i_, k_ = i, k
            if i > k:
                i_, k_ = k, i
            eq_c = Eq_ij.get((i_, k_), None)
            if eq_c is None:
                continue
            else:
                le_c = Le_ij[i_, k_]
                ge_c = Ge_ij[i_, k_]
            if i_ != i:
                le_c, ge_c = ge_c, le_c

            # a <= b and b <= c -> a <= c
            model.addLConstr(ge_a + ge_b, GRB.LESS_EQUAL, 1 + ge_c,
                             f'transitivity_ge_{i},{j},{k}')
            # a >= b and b >= c -> a >= c
            model.addLConstr(le_a + le_b, GRB.LESS_EQUAL, 1 + le_c,
                             f'transitivity_le_{i},{j},{k}')
            # a <= b and b == c -> a <= c
            model.addLConstr(le_a + eq_b, GRB.LESS_EQUAL, 1 + le_c,
                             f'transitivity_leeq_{i},{j},{k}')
            # a == b and b <= c -> a <= c
            model.addLConstr(eq_a + le_b, GRB.LESS_EQUAL, 1 + le_c,
                             f'transitivity_eqle_{i},{j},{k}')
            # a >= b and b == c --> a >= c
            model.addLConstr(ge_a + eq_b, GRB.LESS_EQUAL, 1 + ge_c,
                             f'transitivity_geeq_{i},{j},{k}')
            # a == b and b >= c --> a >= c
            model.addLConstr(eq_a + ge_b, GRB.LESS_EQUAL, 1 + ge_c,
                             f'transitivity_eqge_{i},{j},{k}')
            # a == b and b == c --> a == c
            model.addLConstr(eq_a + eq_b, GRB.LESS_EQUAL, 1 + eq_c,
                             f'transitivity_eq_{i},{j},{k}')

    # transitivity helper (for not-fully connected graphs)
    # also provides a latent rank
    big_m = max_rank
    model.addConstrs(((1 - ge_ij) * big_m + R_i[i] >= R_i[j] + 1
                      for (i, j), ge_ij in Ge_ij.items()),
                     'rank_transitivity_larger')
    model.addConstrs(((1 - le_ij) * big_m + R_i[j] >= R_i[i] + 1
                      for (i, j), le_ij in Le_ij.items()),
                     'rank_transitivity_smaller')
    model.addConstrs(((1 - eq_ij) * big_m + R_i[j] >= R_i[i]
                      for (i, j), eq_ij in Eq_ij.items()),
                     'rank_transitivity_equal1')
    model.addConstrs(((1 - eq_ij) * big_m + R_i[i] >= R_i[j]
                      for (i, j), eq_ij in Eq_ij.items()),
                     'rank_transitivity_equal2')

    # objective function
    objective = LinExpr()
    for ij, value in comparisons.items():
        if value == 1.0:
            objective += -E_ij[ij]
        elif value == 0.0:
            objective += E_ij[ij]
        else:
            objective += T_ij_pos[ij] + T_ij_neg[ij]
    model.setObjective(objective, GRB.MINIMIZE)

    # solve
    model.optimize()

    # verify abs emulation: one T_ij has to be 0
    for ij, value in T_ij_pos.items():
        assert value.X == 0 or T_ij_neg[ij] == 0, \
            f'T_{ij} pos {value.X} neg {T_ij_neg[ij]}'

    # find minimal Rs
    model_ = Model('comparison')
    model_.setParam('OutputFlag', verbose)
    R_i = model_.addVars(nodes,
                         name='r_i',
                         vtype=GRB.CONTINUOUS,
                         lb=0,
                         ub=len(nodes))
    for ((i, j), ge_ij), le_ij in zip(Ge_ij.items(), Le_ij.values()):
        if ge_ij.x == 1:
            model_.addConstr(R_i[i] >= R_i[j] + 1)
        elif le_ij.x == 1:
            model_.addConstr(R_i[j] >= R_i[i] + 1)
        else:
            model_.addConstr(R_i[j] == R_i[i])
    model_.setObjective(R_i.sum(), GRB.MINIMIZE)
    model_.optimize()

    return [model_.getVarByName(f'r_i[{i}]').X for i in range(len(nodes))], \
        model.objVal
Exemple #20
0
def gurobi_solve_qp(P, q, G=None, h=None, A=None, b=None, initvals=None):
    """
    Solve a Quadratic Program defined as:

        minimize
            (1/2) * x.T * P * x + q.T * x

        subject to
            G * x <= h
            A * x == b

    using Gurobi <http://www.gurobi.com/>.

    Parameters
    ----------
    P : array, shape=(n, n)
        Primal quadratic cost matrix.
    q : array, shape=(n,)
        Primal quadratic cost vector.
    G : array, shape=(m, n)
        Linear inequality constraint matrix.
    h : array, shape=(m,)
        Linear inequality constraint vector.
    A : array, shape=(meq, n), optional
        Linear equality constraint matrix.
    b : array, shape=(meq,), optional
        Linear equality constraint vector.
    initvals : array, shape=(n,), optional
        Warm-start guess vector (not used).

    Returns
    -------
    x : array, shape=(n,)
        Solution to the QP, if found, otherwise ``None``.
    """
    if initvals is not None:
        print("Gurobi: note that warm-start values are ignored by wrapper")
    n = P.shape[1]
    model = Model()
    x = {
        i: model.addVar(
            vtype=GRB.CONTINUOUS,
            name='x_%d' % i,
            lb=-GRB.INFINITY,
            ub=+GRB.INFINITY)
        for i in range(n)
    }
    model.update()   # integrate new variables

    # minimize
    #     1/2 x.T * P * x + q * x
    obj = QuadExpr()
    rows, cols = P.nonzero()
    for i, j in zip(rows, cols):
        obj += 0.5 * x[i] * P[i, j] * x[j]
    for i in range(n):
        obj += q[i] * x[i]
    model.setObjective(obj, GRB.MINIMIZE)

    # subject to
    #     G * x <= h
    if G is not None:
        G_nonzero_rows = get_nonzero_rows(G)
        for i, row in G_nonzero_rows.items():
            model.addConstr(quicksum(G[i, j] * x[j] for j in row) <= h[i])

    # subject to
    #     A * x == b
    if A is not None:
        A_nonzero_rows = get_nonzero_rows(A)
        for i, row in A_nonzero_rows.items():
            model.addConstr(quicksum(A[i, j] * x[j] for j in row) == b[i])

    model.optimize()

    a = empty(n)
    for i in range(n):
        a[i] = model.getVarByName('x_%d' % i).x
    return a