Esempio n. 1
0
def solve(F, groups):
    allEmployees = 0
    for g in groups:
        allEmployees += g.employees
    G = len(groups)

    print("Solving for {} floors with {} employees in {} groups".format(
        F, allEmployees, G))

    # A binary search would be much better here, of course, but meh
    for wcs in range(1, allEmployees + 1):
        m = Model()
        x = [m.add_var(var_type=INTEGER) for i in range(F * G)]
        for i, g in enumerate(groups):
            m += xsum(x[i * F + f] for f in g.floors) >= g.employees

        for f in range(F):
            m += xsum(x[f + g * F] for g in range(G)) <= wcs

        m.objective = xsum(0 for i in range(F * G))
        m.max_gap = 0.05
        status = m.optimize(max_seconds=3000)
        if status == OptimizationStatus.OPTIMAL:
            print('optimal solution cost {} found'.format(m.objective_value))
        elif status == OptimizationStatus.FEASIBLE:
            print('sol.cost {} found, best possible: {}'.format(
                m.objective_value, m.objective_bound))
        elif status == OptimizationStatus.NO_SOLUTION_FOUND:
            print('no feasible solution found, lower bound is: {}'.format(
                m.objective_bound))

        if status == OptimizationStatus.OPTIMAL or status == OptimizationStatus.FEASIBLE:
            print(
                '==================================================================='
            )
            print('solution:')
            for v in m.vars:
                if abs(v.x) > 1e-6:  # only printing non-zeros
                    print('{} : {}'.format(v.name, v.x))
            return wcs
Esempio n. 2
0
    def runSolver(k):
        # return dict, k
        m = Model(solver_name=CBC)

        #######################
        ###### Variables ######
        #######################
        """
        x[s1][r] = binary var if student s1 is in room r
        n x k matrix
        Room     0 1 2 3   Student
                [1 0 0 0]    0
                [1 1 0 1]    1
                [1 1 0 1]    2
        """
        x = [[
            m.add_var(name='x_{}_{}'.format(i, l), var_type='B')
            for l in range(k)
        ] for i in range(n)]
        """
        y[r][s1][s2] = binary var if s1 and s2 are in room r
        Each entry of s is a sqaure n x n matrix
                [(0, 0) ... (i, 0)]
                [ ...   ...   ... ]
                [(j, 0) ... (i, j)]
        all entries below the diagonal are repeated. 
        """
        y = []
        for l in range(k):
            y.append([[
                m.add_var(name='y_{}_{}_{}'.format(i, j, l), var_type='B')
                for i in range(n)
            ] for j in range(n)])

        #######################
        ##### Constraints #####
        #######################

        # https://cs.stackexchange.com/questions/12102/express-boolean-logic-operations-in-zero-one-integer-linear-programming-ilp?fbclid=IwAR0DTuP7zy4KUkgU_Vxip-21mMd0Gysw_EE1-BwGJtMz3BdPmDbTaAoPGI8
        for l in range(k):
            for i in range(n):
                for j in range(i + 1, n):
                    m += x[i][l] + x[j][l] - 1 <= y[l][i][j]
                    m += x[i][l] >= y[l][i][j]
                    m += x[j][l] >= y[l][i][j]

        # ensures each student can only be in 1 room
        for i in range(n):
            m += xsum(x[i][l] for l in range(k)) == 1

        # ensures that rooms have at least 1 person
        for l in range(k):
            m += xsum(x[i][l] for i in range(n)) >= 1

        # ensures each room meets stress requirement
        for l in range(k):
            m += xsum(y[l][i][j] * getStress(i, j) for i in range(n)
                      for j in range(i + 1, n)) <= S_MAX / k

        # optimize for happiness
        m.objective = maximize(
            xsum(y[l][i][j] * getHappiness(i, j) for l in range(k)
                 for i in range(n) for j in range(i + 1, n)))

        solution = {}
        m.max_gap = 0.05
        status = m.optimize(max_seconds=300)
        if status == OptimizationStatus.OPTIMAL:
            print('optimal solution cost {} found'.format(m.objective_value))
        elif status == OptimizationStatus.FEASIBLE:
            print('sol.cost {} found, best possible: {}'.format(
                m.objective_value, m.objective_bound))
        elif status == OptimizationStatus.NO_SOLUTION_FOUND:
            print('no feasible solution found, lower bound is: {}'.format(
                m.objective_bound))
        elif status == OptimizationStatus.INFEASIBLE:
            print('infeasible: ')
        if status == OptimizationStatus.OPTIMAL or status == OptimizationStatus.FEASIBLE:
            print('solution:')
            for v in m.vars:
                if abs(v.x) > 1e-6:  # only printing non-zeros
                    print('{} : {}'.format(v.name, v.x))
                    student_room = v.name.split('_')
                    if student_room[0] == 'x':
                        solution[int(student_room[1])] = int(student_room[2])

            solution = dict(sorted(solution.items(), key=lambda item: item[1]))
            return solution
Esempio n. 3
0
    def solve(self,
              list_of_locations,
              list_of_homes,
              starting_car_location,
              adjacency_matrix,
              input_file,
              params=[]):
        """
		Solve the problem using an MST/DFS approach.
		Input:
			list_of_locations: A list of locations such that node i of the graph corresponds to name at index i of the list
			list_of_homes: A list of homes
			starting_car_location: The name of the starting location for the car
			adjacency_matrix: The adjacency matrix from the input file
		Output:
			A cost of how expensive the current solution is
			A list of locations representing the car path
			A dictionary mapping drop-off location to a list of homes of TAs that got off at that particular location
			NOTE: all outputs should be in terms of indices not the names of the locations themselves
		"""
        conn = sqlite3.connect('models.sqlite')
        c = conn.cursor()
        seen = c.execute(
            'SELECT best_objective_bound FROM models WHERE input_file = (?)',
            (input_file, )).fetchone()

        self.log_new_entry(input_file)

        home_indices = convert_locations_to_indices(list_of_homes,
                                                    list_of_locations)
        location_indices = convert_locations_to_indices(
            list_of_locations, list_of_locations)

        edge_scale = 1.0
        if "--approx" in params:
            edge_scale = 1 / 10000

        G, message = adjacency_matrix_to_graph(adjacency_matrix, edge_scale)
        E = list(G.to_directed().edges(data='weight'))

        starting_car_index = list_of_locations.index(starting_car_location)

        start_paths = [
            convert_locations_to_indices([starting_car_location],
                                         list_of_locations)
        ]
        num_random_paths = 5
        if "-r" in params:
            num_random_paths = int(params[params.index("-r") + 1])

        for i in range(num_random_paths):
            start_paths.append(self.generate_random(G, starting_car_index))

        if seen:
            output_file = 'submissions/submission_final/{}.out'.format(
                input_file.split('.')[0])
            print(output_file)
            if not "--no-prev" in params and os.path.isfile(output_file):
                start_paths.append(
                    convert_locations_to_indices(
                        utils.read_file(output_file)[0], list_of_locations))

        best_start_path_cost = float('inf')
        best_start_path_index = -1
        for i, path in enumerate(start_paths):
            walk_cost, dropoffs = self.find_best_dropoffs(
                G, home_indices, path)
            cost, msg = cost_of_solution(G, path, dropoffs)

            if cost < best_start_path_cost:
                best_start_path_cost = cost
                best_start_path_index = i

        start_path = start_paths[best_start_path_index]
        print("Starting path:")
        if best_start_path_index == num_random_paths:
            print("SAVED PATH:", start_path)
        elif best_start_path_index >= 0:
            print("RANDOM PATH:", start_path)
        else:
            print("No start path found")
        print("Starting cost:", best_start_path_cost)

        # number of nodes and list of vertices, not including source or sink
        n, V, H = len(list_of_locations), location_indices, home_indices
        bigNum = (2 * n)

        model = Model()

        # does the car drive from i to j?
        x = [model.add_var(var_type=BINARY) for e in E]

        # does the kth TA walk from i to j? over all num_homes TAs
        t = [[model.add_var(var_type=BINARY) for e in E] for k in H]

        # car flow from vertex u to vertex v
        f = [model.add_var(var_type=INTEGER) for e in E] \
        + [model.add_var(var_type=INTEGER) for v in V] \
        + [model.add_var(var_type=INTEGER)]

        # kth TA flow from vertex u to vertex v
        f_t = [[model.add_var(var_type=BINARY)
                for e in E] + [model.add_var(var_type=BINARY) for v in V]
               for k in H]

        for i in range(len(f)):
            model += f[i] >= 0

        # For each vertex v where v != source and v != sink, Sum{x_(u, v)} = Sum{x_(v, w)}
        for v in V:
            model += xsum([x[i] for i in range(len(E))
                           if E[i][1] == v]) == xsum(
                               [x[i] for i in range(len(E)) if E[i][0] == v])

        # For each vertex v where v != sink, Sum{f_(u, v)} = Sum{f_(v, w)}
        for j in range(len(V)):
            model += xsum([f[i] for i in range(len(E)) if E[i][1] == V[j]]) + (f[-1] if V[j] == starting_car_index else 0) \
              == xsum([f[i] for i in range(len(E)) if E[i][0] == V[j]]) + f[len(E) + j]

        # For each edge (u, v) where u != source and v != sink, f_(u, v) <= (big number) * x_(u, v)
        for i in range(len(E)):
            model += f[i] <= bigNum * x[i]

        # For edge (source, start vertex), f_(source, start vertex) <= (big number)
        model += f[-1] <= bigNum

        # For each edge (u, sink), f_(u, sink) <= Sum{x_(w, u)}
        for j in range(len(V)):
            model += f[j + len(E)] \
              <= xsum([x[i] for i in range(len(E)) if E[i][1] == V[j]])

        # For just the source vertex, f_(source,start vertex)} = Sum{x_(a, b)}
        model += f[-1] == xsum(x)

        # For every TA for every edge, can't flow unless edge is walked along
        for i in range(len(t)):
            for j in range(len(E)):
                model += f_t[i][j] <= t[i][j]

        # For every TA for every non-home vertex, flow in equals flow out
        for i in range(len(H)):
            for j in range(len(V)):
                if V[j] != H[i]:
                    model += xsum(f_t[i][k] for k in range(len(E)) if E[k][1] == V[j]) + f_t[i][len(E) + j] \
                     == xsum(f_t[i][k] for k in range(len(E)) if E[k][0] == V[j])

        # For every TA, flow out of the source vertex is exactly 1
        for k in f_t:
            model += xsum(k[len(E) + i] for i in range(len(V))) == 1

        # For every TA for every edge out of source, can't flow unless car visits vertex
        for k in f_t:
            for i in range(len(V)):
                model += k[len(E) + i] <= xsum(
                    x[j] for j in range(len(E)) if E[j][1] == V[i])

        # For every TA, flow into the home vertex is exactly 1
        for i in range(len(H)):
            model += xsum(f_t[i][j] for j in range(len(E))
                          if E[j][1] == H[i]) + f_t[i][len(E) + H[i]] == 1

        # objective function: minimize the distance
        model.objective = minimize(2.0/3.0 * xsum([x[i] * E[i][2] for i in range(len(E))]) \
         + xsum([xsum([t[i][j] * E[j][2] for j in range(len(E))]) for i in range(len(t))]))

        # WINNING ONLINE
        model.max_gap = 0.00001
        model.emphasis = 2
        model.symmetry = 2

        if "--no-model-start" not in params:
            model.start = self.construct_starter(x, t, G, home_indices,
                                                 start_path)

        timeout = 300
        if "-t" in params:
            timeout = int(params[params.index("-t") + 1])

        if timeout != -1:
            status = model.optimize(max_seconds=timeout)
        else:
            status = model.optimize()

        objective_value = model.objective_value / edge_scale
        objective_bound = model.objective_bound / edge_scale

        if status == OptimizationStatus.OPTIMAL:
            print('optimal solution cost {} found'.format(objective_value))
            self.log_update_entry(Fore.GREEN +
                                  "Optimal cost={}.".format(objective_value) +
                                  Style.RESET_ALL)
        else:
            print("!!!! TIMEOUT !!!!")
            self.log_update_entry(Fore.RED + "Timeout!" + Style.RESET_ALL)

            if status == OptimizationStatus.FEASIBLE:
                print('sol.cost {} found, best possible: {}'.format(
                    objective_value, objective_bound))
                self.log_update_entry("Feasible cost={}, bound={}.".format(
                    objective_value, objective_bound))
            elif status == OptimizationStatus.NO_SOLUTION_FOUND:
                print('no feasible solution found, lower bound is: {}'.format(
                    objective_bound))
                self.log_update_entry(
                    "Failed, bound={}.".format(objective_bound))

        # if no solution found, return inf cost
        if model.num_solutions == 0:
            conn.close()
            return float('inf'), [], {}

        # printing the solution if found
        out.write('Route with total cost %g found. \n' % (objective_value))

        if "-v" in params:
            out.write('\nEdges (In, Out, Weight):\n')
            for i in E:
                out.write(str(i) + '\t')

            out.write('\n\nCar - Chosen Edges:\n')
            for i in x:
                out.write(str(i.x) + '\t')

            out.write('\n\nCar - Flow Capacities:\n')
            for i in f:
                out.write(str(i.x) + '\t')

            out.write('\n\nTAs - Home Indices:\n')
            for i in H:
                out.write(str(i) + '\n')

            out.write('\nTAs - Chosen Edges:\n')
            for i in t:
                for j in range(len(i)):
                    out.write(str(i[j].x) + '\t')
                out.write('\n')

            out.write('\nTAs - Flow Capacities:\n')
            for i in f_t:
                for j in range(len(i)):
                    out.write(str(i[j].x) + '\t')
                out.write('\n')

            out.write('\nActive Edges:\n')

            for i in range(len(x)):
                if (x[i].x >= 1.0):
                    out.write('Edge from %i to %i with weight %f \n' %
                              (E[i][0], E[i][1], E[i][2]))
            out.write('\n')

        list_of_edges = [E[i] for i in range(len(x)) if x[i].x >= 1.0]
        car_path_indices = self.construct_path(starting_car_index,
                                               list_of_edges, input_file)

        walk_cost, dropoffs_dict = self.find_best_dropoffs(
            G, home_indices, car_path_indices)
        updated = False
        if not seen:
            print("SAVING", input_file)
            c.execute('INSERT INTO models (input_file, best_objective_bound, optimal) VALUES (?, ?, ?)', \
             (input_file, objective_value, status == OptimizationStatus.OPTIMAL))
            conn.commit()
        elif objective_value <= seen[0]:
            updated = True
            print("UPDATING", input_file)
            c.execute('UPDATE models SET best_objective_bound = ?, optimal = ? WHERE input_file = ?', \
             (objective_value, status == OptimizationStatus.OPTIMAL, input_file))
            conn.commit()
        if not "-s" in params:
            print("Walk cost =", walk_cost, "\n")

        if updated:
            self.log_update_entry(Fore.GREEN + "Updated" + Style.RESET_ALL)
        else:
            self.log_update_entry(Fore.RED + "Not Updated" + Style.RESET_ALL)

        conn.close()
        return objective_value, car_path_indices, dropoffs_dict
def tsp_mip_solver(input_data):
    # parse the input
    lines = input_data.split('\n')

    nodeCount = int(lines[0])

    points = []
    for i in range(1, nodeCount + 1):
        line = lines[i]
        parts = line.split()
        points.append(Point(float(parts[0]), float(parts[1])))
    print('Points parsed!')

    # calculate distance matrix
    d_m = [[length(q, p) for q in points] for p in points]
    print('Distance matrix ready!')

    # declare MIP model
    m = Model(solver_name='GRB')
    print('-Model instatiated!', datetime.datetime.now())

    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 0

    # whenever the distance of the lower and upper bounds is less or
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05

    # specifies number of used threads
    # 0 uses solver default configuration,
    # -1 uses the number of available processing cores
    # ≥1 uses the specified number of threads.
    # An increased number of threads may improve the solution time but also increases
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = 0

    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts = -1

    m.preprocess = 1
    m.pump_passes = 20
    m.sol_pool_size = 1

    nodes = set(range(nodeCount))

    # instantiate "entering and leaving" variables
    x = [[m.add_var(name="x{}_{}".format(p, q), var_type='B') for q in nodes]
         for p in nodes]
    # instantiate subtour elimination variables
    y = [m.add_var(name="y{}".format(i)) for i in nodes]
    print('-Variables instantiated', datetime.datetime.now())

    # declare objective function
    m.objective = minimize(
        xsum(d_m[i][j] * x[i][j] for i in nodes for j in nodes))

    print('-Objective declared!', datetime.datetime.now())

    # declare constraints
    # leave each city only once
    for i in tqdm(nodes):
        m.add_constr(xsum(x[i][j] for j in nodes - {i}) == 1)

    # enter each city only once
    for i in tqdm(nodes):
        m.add_constr(xsum(x[j][i] for j in nodes - {i}) == 1)

    # subtour elimination constraints
    for (i, j) in tqdm(product(nodes - {0}, nodes - {0})):
        if i != j:
            m.add_constr(y[i] - (nodeCount + 1) * x[i][j] >= y[j] - nodeCount)

    print('-Constraints declared!', datetime.datetime.now())

    #Maximum time in seconds that the search can go on if a feasible solution
    #is available and it is not being improved
    mssi = 1000  #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 1000000  #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000  #default = inf

    # executes the optimization
    print('-Optimizer start.', datetime.datetime.now())
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds=ms, max_seconds_same_incumbent=mssi)

    print('Opt. Status:', status)
    print('MIP Sol. Obj.:', m.objective_value)
    print('Dual Bound:', m.objective_bound)
    print('Dual gap:', m.gap)

    sol = [0]
    c_node = 0
    for j in range(nodeCount - 1):
        for i in range(nodeCount):
            if round(m.var_by_name("x{}_{}".format(c_node, i)).x) != 0:
                sol.append(i)
                c_node = i
                break

    obj = m.objective_value

    # prepare the solution in the specified output format
    if status == OptimizationStatus.OPTIMAL:
        output_data = '%.2f' % obj + ' ' + str(1) + '\n'
        output_data += ' '.join(map(str, sol))
    elif status == OptimizationStatus.FEASIBLE:
        output_data = '%.2f' % obj + ' ' + str(0) + '\n'
        output_data += ' '.join(map(str, sol))

    return output_data
Esempio n. 5
0
def fl_mip_solver(input_data):
    # Modify this code to run your optimization algorithm

    # parse the input
    lines = input_data.split('\n')

    parts = lines[0].split()
    facility_count = int(parts[0])
    customer_count = int(parts[1])
    
    facilities = []
    for i in range(1, facility_count+1):
        parts = lines[i].split()
        facilities.append(Facility(i-1, float(parts[0]), int(parts[1]), Point(float(parts[2]), float(parts[3])) ))

    customers = []
    for i in range(facility_count+1, facility_count+1+customer_count):
        parts = lines[i].split()
        customers.append(Customer(i-1-facility_count, int(parts[0]), Point(float(parts[1]), float(parts[2]))))
    
    print('F count:',facility_count)
    print('C count:',customer_count)
    
    # instantiates setup cost vector
    set_cost = [f.setup_cost for f in facilities]
    
    # instantiates proportional capacity matrix such that Cap[i,j] represents the demand of 
    # customer 'j' as a fraction of the total capacity of facility 'i'
    Cap = [[c.demand/f.capacity  for c in customers] for f in facilities]
    
    # instantiates distance matrix in such a way that "sum_{j \in M} D[i,j]*at[i,j]" is the total
    # distance cost for facility 'i'
    D = [[length(c.location, f.location)  for c in customers] for f in facilities]
    
    # declares MIP model
    #m = Model(solver_name=CBC)
    m = Model(solver_name='GRB')
    print('-Model instatiated!',datetime.datetime.now())
    
    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 2
    
    # whenever the distance of the lower and upper bounds is less or 
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05
    
    # specifies number of used threads
    # 0 uses solver default configuration, 
    # -1 uses the number of available processing cores 
    # ≥1 uses the specified number of threads. 
    # An increased number of threads may improve the solution time but also increases 
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = -1
    
    
    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts=-1

    m.preprocess=1
    m.pump_passes=10
    m.sol_pool_size=1

    # instantiates open facilities variables
    op = [m.add_var(name="op{}".format(i),var_type='B') for i in range(facility_count)]
    
    # instantiates matrix of atribution variables 
    # (line 'i' is an atribution vector for facility 'i')
    # e.g.: At[3][4] returns whether customer 4 is assigned to facility 3
    At = [[m.add_var(name="At{},{}".format(i,j) ,var_type='B') for j in range(customer_count)] 
          for i in range(facility_count)]
    
    print('-Variables declared!',datetime.datetime.now())
    
    # instantiates objective function
    # m.objective = minimize("setup costs" + "distance costs")
    # Form example:
    # m.objective = minimize(
    # xsum(dist[i, j] * x[i, j] for (i, j) in product(F, C)) + xsum(y[i] for i in F) )
    
    m.objective = minimize(
        xsum(set_cost[i]*op[i] for i in range(facility_count)) +
        xsum(sum(D[i][j]*At[i][j] for j in range(customer_count)) for i in range(facility_count)) 
            )
    
    print('-Objective declared!',datetime.datetime.now())
    
    # instatiates capacity constraints
    #     -can be expressed as "sum_{j \in M} Cap[i,j]*At[i,j] <= op[i]" for all facilities 'i'
    #     -if a facility is closed (op[i]=0), its effective capacity is 0
    for i in range(facility_count):
        m.add_lazy_constr( xsum(Cap[i][j]*At[i][j] for j in range(customer_count)) <= op[i] )
    
    # instantiates assignment constraints (maximum of 1 facility per customer)
    #     -can be expressed as: "sum of elements over of each column of 'At' == 1"     
    for i in range(customer_count):
        m += xsum(At[j][i] for j in range(facility_count)) == 1 
    
    print('-Contraints processed!',datetime.datetime.now())
    
    #Maximum time in seconds that the search can go on if a feasible solution 
    #is available and it is not being improved
    mssi = 1000 #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 40000 #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000 #default = inf
    
    print('-Optimizer start.',datetime.datetime.now())
    # executes the optimization
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi)
    final_obj = m.objective_value

    print('Opt. Status:',status)
    print('MIP Sol. Obj.:',final_obj)
    print('Dual Bound:',m.objective_bound)
    print('Dual gap:',m.gap)
    
    
    used=[i for i in range(facility_count) if m.vars[i].x==1]

    solution=[None] * customer_count
    for i in range(facility_count):
        for j in range(customer_count):
            if round(m.vars[i*customer_count + j + facility_count].x) == 1:
                solution[j]=i
    # parse output varibles and convert to conventional solution output format
    
    # calculate the cost of the solution
    obj = sum([facilities[f].setup_cost for f in used])
    
    for customer in customers:
        obj += length(customer.location, facilities[solution[customer.index]].location)

    if status == OptimizationStatus.OPTIMAL:
    	# prepare the solution in the specified output format
    	output_data = '%.2f' % obj + ' ' + str(1) + '\n'
    	output_data += ' '.join(map(str, solution))
    elif status == OptimizationStatus.FEASIBLE:
    	output_data = '%.2f' % obj + ' ' + str(0) + '\n'
    	output_data += ' '.join(map(str, solution))
    
    return output_data
Esempio n. 6
0
def mip_solver(input_data):
    # Modify this code to run your optimization algorithm

    # parse the input
    lines = input_data.split('\n')

    firstLine = lines[0].split()
    item_count = int(firstLine[0])
    capacity = int(firstLine[1])

    items = []

    for i in range(1, item_count+1):
        line = lines[i]
        parts = line.split()
        items.append(Item(i-1, int(parts[0]), int(parts[1])))

    # a trivial algorithm for filling the knapsack
    # it takes items in-order until the knapsack is full
    value = 0
    weight = 0
    taken = [0]*len(items)
    
    # declare value vector
    val_vec = [item.value for item in items]
    
    # declare weight vector scaled by capacity
    wgt_vec = [item.weight/capacity for item in items]
    
    # start MIP solver
    m = Model(solver_name='GRB')
    print('-Model instatiated!',datetime.datetime.now())
    
    # states search emphasis
    #     - '0' (default) balanced approach
    #     - '1' (feasibility) aggressively searches for feasible solutions
    #     - '2' (optimality) explores search space to tighten dual gap
    m.emphasis = 2
    
    # whenever the distance of the lower and upper bounds is less or 
    # equal max_gap*100%, the search can be finished
    m.max_gap = 0.05
    
    # specifies number of used threads
    # 0 uses solver default configuration, 
    # -1 uses the number of available processing cores 
    # ≥1 uses the specified number of threads. 
    # An increased number of threads may improve the solution time but also increases 
    # the memory consumption. Each thread needs to store a different model instance!
    m.threads = -1
    
    
    # controls the generation of cutting planes
    # cutting planes usually improve the LP relaxation bound but also make the solution time of the LP relaxation larger
    # -1 means automatic
    #  0 disables completely
    #  1 (default) generates cutting planes in a moderate way
    #  2 generates cutting planes aggressively
    #  3 generates even more cutting planes
    m.cuts=-1

    m.preprocess=1
    m.pump_passes=10
    m.sol_pool_size=1
    
    # instantiates taken items variable vector
    taken = [m.add_var(name="it{}".format(i),var_type='B') for i in range(item_count)]
    print('-Variables declared!',datetime.datetime.now())
    
    # instantiates objective function
    m.objective = maximize( xsum( val_vec[i]*taken[i] for i in range(item_count) ) )
    print('-Objective declared!',datetime.datetime.now())
    
    m.add_constr( xsum( wgt_vec[i]*taken[i] for i in range(item_count)) <=1 )
    print('-Contraints processed!',datetime.datetime.now())
    
    #Maximum time in seconds that the search can go on if a feasible solution 
    #is available and it is not being improved
    mssi = 1000 #default = inf
    # specifies maximum number of nodes to be explored in the search tree (default = inf)
    mn = 40000 #default = 1073741824
    # optimize model m within a processing time limit of 'ms' seconds
    ms = 3000 #default = inf
    
    print('-Optimizer start.',datetime.datetime.now())
    # executes the optimization
    #status = m.optimize(max_seconds = ms,max_seconds_same_incumbent = mssi,max_nodes = mn)
    status = m.optimize(max_seconds = ms , max_seconds_same_incumbent = mssi)
    final_obj = m.objective_value
    
    print('Opt. Status:',status)
    print('MIP Sol. Obj.:',final_obj)
    print('Dual Bound:',m.objective_bound)
    print('Dual gap:',m.gap)
    
    sol = [round(it.x) for it in taken]
    value = int(final_obj)
    
    taken = sol
    # prepare the solution in the specified output format
    if status == OptimizationStatus.OPTIMAL:
        output_data = str(value) + ' ' + str(1) + '\n'
        output_data += ' '.join(map(str, taken))
    elif status == OptimizationStatus.FEASIBLE:
        output_data = str(value) + ' ' + str(0) + '\n'
        output_data += ' '.join(map(str, taken))


    if item_count == 10000:
        output_data=greedy(input_data)

    return output_data